diff --git a/include/swift-c/DependencyScan/DependencyScan.h b/include/swift-c/DependencyScan/DependencyScan.h new file mode 100644 index 00000000000..b04f8134580 --- /dev/null +++ b/include/swift-c/DependencyScan/DependencyScan.h @@ -0,0 +1,332 @@ +//===--- DependencyScan.h - C API for Swift Dependency Scanning ---*- C -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This C API is primarily intended to serve as the Swift Driver's +// dependency scanning facility (https://github.com/apple/swift-driver). +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_C_DEPENDENCY_SCAN_H +#define SWIFT_C_DEPENDENCY_SCAN_H + +#include "DependencyScanMacros.h" +#include +#include +#include + +/// The version constants for the SwiftDependencyScan C API. +/// SWIFTSCAN_VERSION_MINOR should increase when there are API additions. +/// SWIFTSCAN_VERSION_MAJOR is intended for "major" source/ABI breaking changes. +#define SWIFTSCAN_VERSION_MAJOR 0 +#define SWIFTSCAN_VERSION_MINOR 1 + +SWIFTSCAN_BEGIN_DECLS + +//=== Public Scanner Data Types -------------------------------------------===// + +/** + * A character string used to pass around dependency scan result metadata. + * Lifetime of the string is strictly tied to the object whose field it + * represents. When the owning object is released, string memory is freed. + */ +typedef struct { + const void *data; + size_t length; +} swiftscan_string_ref_t; + +typedef struct { + swiftscan_string_ref_t *strings; + size_t count; +} swiftscan_string_set_t; + +typedef enum { + SWIFTSCAN_DEPENDENCY_INFO_SWIFT_TEXTUAL = 0, + SWIFTSCAN_DEPENDENCY_INFO_SWIFT_BINARY = 1, + SWIFTSCAN_DEPENDENCY_INFO_SWIFT_PLACEHOLDER = 2, + SWIFTSCAN_DEPENDENCY_INFO_CLANG = 3 +} swiftscan_dependency_info_kind_t; + +/// Opaque container of the details specific to a given module dependency. +typedef struct swiftscan_module_details_s *swiftscan_module_details_t; + +/// Opaque container to a dependency info of a given module. +typedef struct swiftscan_dependency_info_s *swiftscan_dependency_info_t; + +/// Opaque container to an overall result of a dependency scan. +typedef struct swiftscan_dependency_graph_s *swiftscan_dependency_graph_t; + +/// Opaque container to contain the result of a dependency prescan. +typedef struct swiftscan_import_set_s *swiftscan_import_set_t; + +/// Full Dependency Graph (Result) +typedef struct { + swiftscan_dependency_info_t *modules; + size_t count; +} swiftscan_dependency_set_t; + +//=== Batch Scan Input Specification --------------------------------------===// + +/// Opaque container to a container of batch scan entry information. +typedef struct swiftscan_batch_scan_entry_s *swiftscan_batch_scan_entry_t; + +typedef struct { + swiftscan_batch_scan_entry_t *modules; + size_t count; +} swiftscan_batch_scan_input_t; + +typedef struct { + swiftscan_dependency_graph_t *results; + size_t count; +} swiftscan_batch_scan_result_t; + +//=== Scanner Invocation Specification ------------------------------------===// + +/// Opaque container of all relevant context required to launch a dependency +/// scan (command line arguments, working directory, etc.) +typedef struct swiftscan_scan_invocation_s *swiftscan_scan_invocation_t; + +//=== Dependency Result Functions -----------------------------------------===// + +SWIFTSCAN_PUBLIC swiftscan_string_ref_t +swiftscan_dependency_graph_get_main_module_name( + swiftscan_dependency_graph_t result); + +SWIFTSCAN_PUBLIC swiftscan_dependency_set_t * +swiftscan_dependency_graph_get_dependencies( + swiftscan_dependency_graph_t result); + +//=== Dependency Module Info Functions ------------------------------------===// + +SWIFTSCAN_PUBLIC swiftscan_string_ref_t +swiftscan_module_info_get_module_name(swiftscan_dependency_info_t info); + +SWIFTSCAN_PUBLIC swiftscan_string_ref_t +swiftscan_module_info_get_module_path(swiftscan_dependency_info_t info); + +SWIFTSCAN_PUBLIC swiftscan_string_set_t * +swiftscan_module_info_get_source_files(swiftscan_dependency_info_t info); + +SWIFTSCAN_PUBLIC swiftscan_string_set_t * +swiftscan_module_info_get_direct_dependencies(swiftscan_dependency_info_t info); + +SWIFTSCAN_PUBLIC swiftscan_module_details_t +swiftscan_module_info_get_details(swiftscan_dependency_info_t info); + +//=== Dependency Module Info Details Functions ----------------------------===// + +SWIFTSCAN_PUBLIC swiftscan_dependency_info_kind_t +swiftscan_module_detail_get_kind(swiftscan_module_details_t details); + +//=== Swift Textual Module Details query APIs -----------------------------===// +SWIFTSCAN_PUBLIC swiftscan_string_ref_t +swiftscan_swift_textual_detail_get_module_interface_path( + swiftscan_module_details_t details); + +SWIFTSCAN_PUBLIC swiftscan_string_set_t * +swiftscan_swift_textual_detail_get_compiled_module_candidates( + swiftscan_module_details_t details); + +SWIFTSCAN_PUBLIC swiftscan_string_ref_t +swiftscan_swift_textual_detail_get_bridging_header_path( + swiftscan_module_details_t details); + +SWIFTSCAN_PUBLIC swiftscan_string_set_t * +swiftscan_swift_textual_detail_get_bridging_source_files( + swiftscan_module_details_t details); + +SWIFTSCAN_PUBLIC swiftscan_string_set_t * +swiftscan_swift_textual_detail_get_bridging_module_dependencies( + swiftscan_module_details_t details); + +SWIFTSCAN_PUBLIC swiftscan_string_set_t * +swiftscan_swift_textual_detail_get_command_line( + swiftscan_module_details_t details); + +SWIFTSCAN_PUBLIC swiftscan_string_set_t * +swiftscan_swift_textual_detail_get_extra_pcm_args( + swiftscan_module_details_t details); + +SWIFTSCAN_PUBLIC swiftscan_string_ref_t +swiftscan_swift_textual_detail_get_context_hash( + swiftscan_module_details_t details); + +SWIFTSCAN_PUBLIC bool swiftscan_swift_textual_detail_get_is_framework( + swiftscan_module_details_t details); + +//=== Swift Binary Module Details query APIs ------------------------------===// + +SWIFTSCAN_PUBLIC swiftscan_string_ref_t +swiftscan_swift_binary_detail_get_compiled_module_path( + swiftscan_module_details_t details); + +SWIFTSCAN_PUBLIC swiftscan_string_ref_t +swiftscan_swift_binary_detail_get_module_doc_path( + swiftscan_module_details_t details); + +SWIFTSCAN_PUBLIC swiftscan_string_ref_t +swiftscan_swift_binary_detail_get_module_source_info_path( + swiftscan_module_details_t details); + +//=== Swift Placeholder Module Details query APIs -------------------------===// + +SWIFTSCAN_PUBLIC swiftscan_string_ref_t +swiftscan_swift_placeholder_detail_get_compiled_module_path( + swiftscan_module_details_t details); + +SWIFTSCAN_PUBLIC swiftscan_string_ref_t +swiftscan_swift_placeholder_detail_get_module_doc_path( + swiftscan_module_details_t details); + +SWIFTSCAN_PUBLIC swiftscan_string_ref_t +swiftscan_swift_placeholder_detail_get_module_source_info_path( + swiftscan_module_details_t details); + +//=== Clang Module Details query APIs -------------------------------------===// + +SWIFTSCAN_PUBLIC swiftscan_string_ref_t +swiftscan_clang_detail_get_module_map_path(swiftscan_module_details_t details); + +SWIFTSCAN_PUBLIC swiftscan_string_ref_t +swiftscan_clang_detail_get_context_hash(swiftscan_module_details_t details); + +SWIFTSCAN_PUBLIC swiftscan_string_set_t * +swiftscan_clang_detail_get_command_line(swiftscan_module_details_t details); + +//=== Batch Scan Input Functions ------------------------------------------===// + +/// Create an \c swiftscan_batch_scan_input_t instance. +/// The returned \c swiftscan_batch_scan_input_t is owned by the caller and must be disposed +/// of using \c swiftscan_batch_scan_input_dispose . +SWIFTSCAN_PUBLIC swiftscan_batch_scan_input_t * +swiftscan_batch_scan_input_create(); + +SWIFTSCAN_PUBLIC void +swiftscan_batch_scan_input_set_modules(swiftscan_batch_scan_input_t *input, + int count, + swiftscan_batch_scan_entry_t *modules); + +//=== Batch Scan Entry Functions ------------------------------------------===// + +/// Create an \c swiftscan_batch_scan_entry_t instance. +/// The returned \c swiftscan_batch_scan_entry_t is owned by the caller and must be disposed +/// of using \c swiftscan_batch_scan_entry_dispose . +SWIFTSCAN_PUBLIC swiftscan_batch_scan_entry_t +swiftscan_batch_scan_entry_create(); + +SWIFTSCAN_PUBLIC void +swiftscan_batch_scan_entry_set_module_name(swiftscan_batch_scan_entry_t entry, + const char *name); + +SWIFTSCAN_PUBLIC void +swiftscan_batch_scan_entry_set_arguments(swiftscan_batch_scan_entry_t entry, + const char *arguments); + +SWIFTSCAN_PUBLIC void +swiftscan_batch_scan_entry_set_is_swift(swiftscan_batch_scan_entry_t entry, + bool is_swift); + +SWIFTSCAN_PUBLIC swiftscan_string_ref_t +swiftscan_batch_scan_entry_get_module_name(swiftscan_batch_scan_entry_t entry); + +SWIFTSCAN_PUBLIC swiftscan_string_ref_t +swiftscan_batch_scan_entry_get_arguments(swiftscan_batch_scan_entry_t entry); + +SWIFTSCAN_PUBLIC bool +swiftscan_batch_scan_entry_get_is_swift(swiftscan_batch_scan_entry_t entry); + +//=== Prescan Result Functions --------------------------------------------===// + +SWIFTSCAN_PUBLIC swiftscan_string_set_t * +swiftscan_import_set_get_imports(swiftscan_import_set_t result); + +//=== Scanner Invocation Functions ----------------------------------------===// + +/// Create an \c swiftscan_scan_invocation_t instance. +/// The returned \c swiftscan_scan_invocation_t is owned by the caller and must be disposed +/// of using \c swiftscan_scan_invocation_dispose . +SWIFTSCAN_PUBLIC swiftscan_scan_invocation_t swiftscan_scan_invocation_create(); + +SWIFTSCAN_PUBLIC void swiftscan_scan_invocation_set_working_directory( + swiftscan_scan_invocation_t invocation, const char *working_directory); + +SWIFTSCAN_PUBLIC void +swiftscan_scan_invocation_set_argv(swiftscan_scan_invocation_t invocation, + int argc, const char **argv); + +SWIFTSCAN_PUBLIC swiftscan_string_ref_t +swiftscan_scan_invocation_get_working_directory( + swiftscan_scan_invocation_t invocation); + +SWIFTSCAN_PUBLIC int +swiftscan_scan_invocation_get_argc(swiftscan_scan_invocation_t invocation); + +SWIFTSCAN_PUBLIC swiftscan_string_set_t * +swiftscan_scan_invocation_get_argv(swiftscan_scan_invocation_t invocation); + +//=== Cleanup Functions ---------------------------------------------------===// + +SWIFTSCAN_PUBLIC void +swiftscan_dependency_graph_dispose(swiftscan_dependency_graph_t result); + +SWIFTSCAN_PUBLIC void +swiftscan_import_set_dispose(swiftscan_import_set_t result); + +SWIFTSCAN_PUBLIC void +swiftscan_batch_scan_entry_dispose(swiftscan_batch_scan_entry_t entry); + +SWIFTSCAN_PUBLIC void +swiftscan_batch_scan_input_dispose(swiftscan_batch_scan_input_t *input); + +SWIFTSCAN_PUBLIC void +swiftscan_batch_scan_result_dispose(swiftscan_batch_scan_result_t *result); + +SWIFTSCAN_PUBLIC void +swiftscan_scan_invocation_dispose(swiftscan_scan_invocation_t invocation); + +//=== Scanner Functions ---------------------------------------------------===// + +/// Container of the configuration state and shared cache for dependency +/// scanning. +typedef void *swiftscan_scanner_t; + +/// Create an \c swiftscan_scanner_t instance. +/// The returned \c swiftscan_scanner_t is owned by the caller and must be disposed +/// of using \c swiftscan_scanner_dispose . +SWIFTSCAN_PUBLIC swiftscan_scanner_t swiftscan_scanner_create(void); +SWIFTSCAN_PUBLIC void swiftscan_scanner_dispose(swiftscan_scanner_t); + +/// Invoke a dependency scan using arguments specified in the \c +/// swiftscan_scan_invocation_t argument. The returned \c +/// swiftscan_dependency_graph_t is owned by the caller and must be disposed of +/// using \c swiftscan_dependency_graph_dispose . +SWIFTSCAN_PUBLIC swiftscan_dependency_graph_t swiftscan_dependency_graph_create( + swiftscan_scanner_t scanner, swiftscan_scan_invocation_t invocation); + +/// Invoke the scan for an input batch of modules specified in the +/// \c swiftscan_batch_scan_input_t argument. The returned +/// \c swiftscan_batch_scan_result_t is owned by the caller and must be disposed +/// of using \c swiftscan_batch_scan_result_dispose . +SWIFTSCAN_PUBLIC swiftscan_batch_scan_result_t * +swiftscan_batch_scan_result_create(swiftscan_scanner_t scanner, + swiftscan_batch_scan_input_t *batch_input, + swiftscan_scan_invocation_t invocation); + +/// Invoke the import prescan using arguments specified in the \c +/// swiftscan_scan_invocation_t argument. The returned \c swiftscan_import_set_t +/// is owned by the caller and must be disposed of using \c +/// swiftscan_import_set_dispose . +SWIFTSCAN_PUBLIC swiftscan_import_set_t swiftscan_import_set_create( + swiftscan_scanner_t scanner, swiftscan_scan_invocation_t invocation); + +SWIFTSCAN_END_DECLS + +#endif // SWIFT_C_DEPENDENCY_SCAN_H diff --git a/include/swift-c/DependencyScan/DependencyScanMacros.h b/include/swift-c/DependencyScan/DependencyScanMacros.h new file mode 100644 index 00000000000..472466cfb31 --- /dev/null +++ b/include/swift-c/DependencyScan/DependencyScanMacros.h @@ -0,0 +1,31 @@ +//===-- DependencyScanMacros.h - Swift Dependency Scanning Macros -*- C -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifdef __cplusplus +# define SWIFTSCAN_BEGIN_DECLS extern "C" { +# define SWIFTSCAN_END_DECLS } +#else +# define SWIFTSCAN_BEGIN_DECLS +# define SWIFTSCAN_END_DECLS +#endif + +#ifndef SWIFTSCAN_PUBLIC +# ifdef _WIN32 +# ifdef libSwiftScan_EXPORTS +# define SWIFTSCAN_PUBLIC __declspec(dllexport) +# else +# define SWIFTSCAN_PUBLIC __declspec(dllimport) +# endif +# else +# define SWIFTSCAN_PUBLIC +# endif +#endif diff --git a/include/swift-c/DependencyScan/module.modulemap b/include/swift-c/DependencyScan/module.modulemap new file mode 100644 index 00000000000..8a3b4e4fbcd --- /dev/null +++ b/include/swift-c/DependencyScan/module.modulemap @@ -0,0 +1,4 @@ +module _InternalSwiftScan { + header "DependencyScan.h" + link "_InternalSwiftScan" +} diff --git a/include/swift/DependencyScan/DependencyScanImpl.h b/include/swift/DependencyScan/DependencyScanImpl.h new file mode 100644 index 00000000000..6cb58facac1 --- /dev/null +++ b/include/swift/DependencyScan/DependencyScanImpl.h @@ -0,0 +1,159 @@ +//===-------------- DependencyScanImpl.h - Swift Compiler -----------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Implementation details of the dependency scanning C API +// +//===----------------------------------------------------------------------===// +#ifndef SWIFT_C_DEPENDENCY_SCAN_IMPL_H +#define SWIFT_C_DEPENDENCY_SCAN_IMPL_H + +#include "swift-c/DependencyScan/DependencyScan.h" + +namespace swift { +namespace dependencies { +class DependencyScanningTool; +} +} // namespace swift + +struct swiftscan_dependency_graph_s { + /// The name of the main module for this dependency graph (root node) + swiftscan_string_ref_t main_module_name; + + /// The complete list of modules discovered + swiftscan_dependency_set_t *dependencies; +}; + +struct swiftscan_dependency_info_s { + /// The module's name + /// The format is: + /// `:` + /// where `module-kind` is one of: + /// "swiftTextual" + /// "swiftBinary" + /// "swiftPlaceholder" + /// "clang"" + swiftscan_string_ref_t module_name; + + /// The path for the module. + swiftscan_string_ref_t module_path; + + /// The source files used to build this module. + swiftscan_string_set_t *source_files; + + /** + * The list of modules which this module direct depends on. + * The format is: + * `:` + */ + swiftscan_string_set_t *direct_dependencies; + + /// Specific details of a particular kind of module. + swiftscan_module_details_t details; +}; + +/// Swift modules to be built from a module interface, may have a bridging +/// header. +typedef struct { + /// The module interface from which this module was built, if any. + swiftscan_string_ref_t module_interface_path; + + /// The paths of potentially ready-to-use compiled modules for the interface. + swiftscan_string_set_t *compiled_module_candidates; + + /// The bridging header, if any. + swiftscan_string_ref_t bridging_header_path; + + /// The source files referenced by the bridging header. + swiftscan_string_set_t *bridging_source_files; + + /// (Clang) modules on which the bridging header depends. + swiftscan_string_set_t *bridging_module_dependencies; + + /// Options to the compile command required to build this module interface + swiftscan_string_set_t *command_line; + + /// To build a PCM to be used by this Swift module, we need to append these + /// arguments to the generic PCM build arguments reported from the dependency + /// graph. + swiftscan_string_set_t *extra_pcm_args; + + /// The hash value that will be used for the generated module + swiftscan_string_ref_t context_hash; + + /// A flag to indicate whether or not this module is a framework. + bool is_framework; +} swiftscan_swift_textual_details_t; + +/// Swift modules with only a binary module file. +typedef struct { + /// The path to the pre-compiled binary module + swiftscan_string_ref_t compiled_module_path; + + /// The path to the .swiftModuleDoc file. + swiftscan_string_ref_t module_doc_path; + + /// The path to the .swiftSourceInfo file. + swiftscan_string_ref_t module_source_info_path; +} swiftscan_swift_binary_details_t; + +/// Swift placeholder modules carry additional details that specify their +/// module doc path and source info paths. +typedef struct { + /// The path to the pre-compiled binary module + swiftscan_string_ref_t compiled_module_path; + + /// The path to the .swiftModuleDoc file. + swiftscan_string_ref_t module_doc_path; + + /// The path to the .swiftSourceInfo file. + swiftscan_string_ref_t module_source_info_path; +} swiftscan_swift_placeholder_details_t; + +/// Clang modules are built from a module map file. +typedef struct { + /// The path to the module map used to build this module. + swiftscan_string_ref_t module_map_path; + + /// clang-generated context hash + swiftscan_string_ref_t context_hash; + + /// Options to the compile command required to build this clang modulemap + swiftscan_string_set_t *command_line; +} swiftscan_clang_details_t; + +struct swiftscan_module_details_s { + swiftscan_dependency_info_kind_t kind; + union { + swiftscan_swift_textual_details_t swift_textual_details; + swiftscan_swift_binary_details_t swift_binary_details; + swiftscan_swift_placeholder_details_t swift_placeholder_details; + swiftscan_clang_details_t clang_details; + }; +}; + +struct swiftscan_batch_scan_entry_s { + swiftscan_string_ref_t module_name; + swiftscan_string_ref_t arguments; + bool is_swift; +}; + +struct swiftscan_import_set_s { + /// The complete list of imports discovered + swiftscan_string_set_t *imports; +}; + +struct swiftscan_scan_invocation_s { + swiftscan_string_ref_t working_directory; + swiftscan_string_set_t *argv; +}; + +#endif // SWIFT_C_DEPENDENCY_SCAN_IMPL_H diff --git a/include/swift/DependencyScan/DependencyScanningTool.h b/include/swift/DependencyScan/DependencyScanningTool.h new file mode 100644 index 00000000000..d18d5bf0916 --- /dev/null +++ b/include/swift/DependencyScan/DependencyScanningTool.h @@ -0,0 +1,83 @@ +//===-------------- DependencyScanningTool.h - Swift Compiler -------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_DEPENDENCY_SCANNING_TOOL_H +#define SWIFT_DEPENDENCY_SCANNING_TOOL_H + +#include "swift-c/DependencyScan/DependencyScan.h" +#include "swift/Frontend/Frontend.h" +#include "swift/AST/ModuleDependencies.h" +#include "swift/DependencyScan/ScanDependencies.h" +#include "swift/Frontend/PrintingDiagnosticConsumer.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/StringSaver.h" + +namespace swift { +namespace dependencies { + +/// The high-level implementation of the dependency scanner that runs on +/// an individual worker thread. +class DependencyScanningTool { +public: + /// Construct a dependency scanning tool. + DependencyScanningTool(); + + /// Collect the full module depenedency graph for the input, ignoring any + /// placeholder modules. + /// + /// \returns a \c StringError with the diagnostic output if errors + /// occurred, \c swiftscan_dependency_result_t otherwise. + llvm::ErrorOr + getDependencies(ArrayRef Command, + const llvm::StringSet<> &PlaceholderModules); + + /// Collect the set of imports for the input module + /// + /// \returns a \c StringError with the diagnostic output if errors + /// occurred, \c swiftscan_prescan_result_t otherwise. + llvm::ErrorOr + getImports(ArrayRef Command); + + /// Collect the full module depenedency graph for the input collection of + /// module names (batch inputs) and output them to the + /// BatchScanInput-specified output locations. + /// + /// \returns a \c std::error_code if errors occured during scan. + std::vector> + getDependencies(ArrayRef Command, + const std::vector &BatchInput, + const llvm::StringSet<> &PlaceholderModules); + +private: + /// Using the specified invocation command, instantiate a CompilerInstance + /// that will be used for this scan. + llvm::ErrorOr> + initCompilerInstanceForScan(ArrayRef Command); + + /// Shared cache of module dependencies, re-used by individual full-scan queries + /// during the lifetime of this Tool. + std::unique_ptr SharedCache; + + /// Shared cache of compiler instances created during batch scanning, corresponding to + /// command-line options specified in the batch scan input entry. + std::unique_ptr VersionedPCMInstanceCacheCache; + + /// A shared consumer that, for now, just prints the encountered diagnostics. + PrintingDiagnosticConsumer PDC; + llvm::BumpPtrAllocator Alloc; + llvm::StringSaver Saver; +}; + +} // end namespace dependencies +} // end namespace swift + +#endif // SWIFT_DEPENDENCY_SCANNING_TOOL_H diff --git a/include/swift/DependencyScan/ScanDependencies.h b/include/swift/DependencyScan/ScanDependencies.h new file mode 100644 index 00000000000..e1d5edb7970 --- /dev/null +++ b/include/swift/DependencyScan/ScanDependencies.h @@ -0,0 +1,88 @@ +//===--- ScanDependencies.h -- Scans the dependencies of a module ------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_DEPENDENCY_SCANDEPENDENCIES_H +#define SWIFT_DEPENDENCY_SCANDEPENDENCIES_H + +#include "swift-c/DependencyScan/DependencyScan.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/Error.h" + +namespace llvm { +class StringSaver; +} + +namespace swift { + +class CompilerInvocation; +class CompilerInstance; +class ModuleDependenciesCache; + +namespace dependencies { + +using CompilerArgInstanceCacheMap = + llvm::StringMap, + std::unique_ptr>>; + +struct BatchScanInput { + llvm::StringRef moduleName; + llvm::StringRef arguments; + llvm::StringRef outputPath; + bool isSwift; +}; + +// MARK: FrontendTool dependency scanner entry points +/// Scans the dependencies of the main module of \c instance and writes out +/// the resulting JSON according to the instance's output parameters. +bool scanDependencies(CompilerInstance &instance); + +/// Identify all imports in the translation unit's module. +bool prescanDependencies(CompilerInstance &instance); + +/// Batch scan the dependencies for modules specified in \c batchInputFile. +bool batchScanDependencies(CompilerInstance &instance, + llvm::StringRef batchInputFile); + +/// Batch prescan the imports of modules specified in \c batchInputFile. +bool batchPrescanDependencies(CompilerInstance &instance, + llvm::StringRef batchInputFile); + +// MARK: Dependency scanning execution +/// Scans the dependencies of the main module of \c instance. +llvm::ErrorOr +performModuleScan(CompilerInstance &instance, + ModuleDependenciesCache &cache); + +/// Scans the main module of \c instance for all direct module imports +llvm::ErrorOr +performModulePrescan(CompilerInstance &instance); + +/// Batch scan the dependencies for modules specified in \c batchInputFile. +std::vector> +performBatchModuleScan(CompilerInstance &invocationInstance, + ModuleDependenciesCache &invocationCache, + CompilerArgInstanceCacheMap *versionedPCMInstanceCache, + llvm::StringSaver &saver, + const std::vector &BatchInput); + +/// Batch prescan the imports of modules specified in \c batchInputFile. +std::vector> +performBatchModulePrescan(CompilerInstance &invocationInstance, + ModuleDependenciesCache &cache, + llvm::StringSaver &saver, + const std::vector &BatchInput); + +} // end namespace dependencies +} // end namespace swift + +#endif diff --git a/include/swift/DependencyScan/StringUtils.h b/include/swift/DependencyScan/StringUtils.h new file mode 100644 index 00000000000..a6b79c4a25c --- /dev/null +++ b/include/swift/DependencyScan/StringUtils.h @@ -0,0 +1,41 @@ +//===----- StringUtils.h - Managed C String Utility Functions -----*- C -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift-c/DependencyScan/DependencyScan.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" +#include +#include + +//=== Private Utility Functions--------------------------------------------===// +namespace swift { +namespace dependencies { + +/// Create null string +swiftscan_string_ref_t create_null(); + +/// Create a \c swiftscan_string_ref_t object from a nul-terminated C string. New +/// \c swiftscan_string_ref_t will contain a copy of \p string. +swiftscan_string_ref_t create_clone(const char *string); + +/// Create an array of \c swiftscan_string_ref_t objects from a vector of C++ strings using the +/// create_clone routine. +swiftscan_string_set_t *create_set(const std::vector &strings); + +/// Create an array of swiftscan_string_ref_t objects from an array of C strings using the +/// create_clone routine. +swiftscan_string_set_t *create_set(int count, const char **strings); + +/// Retrieve the character data associated with the given string. +const char *get_C_string(swiftscan_string_ref_t string); +} +} diff --git a/include/swift/Frontend/Frontend.h b/include/swift/Frontend/Frontend.h index ec9f5b44d85..1165347d390 100644 --- a/include/swift/Frontend/Frontend.h +++ b/include/swift/Frontend/Frontend.h @@ -491,8 +491,6 @@ public: DependencyTracker *getDependencyTracker() { return DepTracker.get(); } const DependencyTracker *getDependencyTracker() const { return DepTracker.get(); } - ModuleDependenciesCache *getModuleDependencyCache() { return ModDepCache.get(); } - UnifiedStatsReporter *getStatsReporter() const { return Stats.get(); } /// Retrieve the main module containing the files being compiled. @@ -558,7 +556,6 @@ private: bool setUpVirtualFileSystemOverlays(); void setUpLLVMArguments(); void setUpDiagnosticOptions(); - void setUpModuleDependencyCacheIfNeeded(); bool setUpModuleLoaders(); bool setUpInputs(); bool setUpASTContextIfNeeded(); diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index c2872363880..54119e57a07 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -134,7 +134,6 @@ public: DumpPCM, ///< Dump information about a precompiled Clang module ScanDependencies, ///< Scan dependencies of Swift source files - ScanClangDependencies, ///< Scan dependencies of a Clang module PrintVersion, ///< Print version information. PrintFeature, ///< Print supported feature of this compiler }; diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index f3424f89983..918ad87cb40 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -1121,10 +1121,6 @@ def scan_dependencies : Flag<["-"], "scan-dependencies">, HelpText<"Scan dependencies of the given Swift sources">, ModeOpt, Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>; -def scan_clang_dependencies : Flag<["-"], "scan-clang-dependencies">, - HelpText<"Scan dependencies of the given Clang module">, ModeOpt, - Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>; - def emit_supported_features : Flag<["-"], "emit-supported-features">, HelpText<"Emit a JSON file including all supported compiler features">, ModeOpt, Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>; diff --git a/lib/AST/ModuleLoader.cpp b/lib/AST/ModuleLoader.cpp index 5d58678b33a..cea9b116851 100644 --- a/lib/AST/ModuleLoader.cpp +++ b/lib/AST/ModuleLoader.cpp @@ -180,12 +180,18 @@ ModuleDependencies::collectCrossImportOverlayNames(ASTContext &ctx, llvm::StringMap> result; // Mimic getModuleDefiningPath() for Swift and Clang module. if (auto *swiftDep = getAsSwiftTextualModule()) { - // Prefer interface path to binary module path if we have it. - modulePath = swiftDep->swiftInterfaceFile; - assert(modulePath.hasValue()); - StringRef parentDir = llvm::sys::path::parent_path(*modulePath); - if (llvm::sys::path::extension(parentDir) == ".swiftmodule") { - modulePath = parentDir.str(); + if (swiftDep->swiftInterfaceFile.hasValue()) { + // Prefer interface path to binary module path if we have it. + modulePath = swiftDep->swiftInterfaceFile; + assert(modulePath.hasValue()); + StringRef parentDir = llvm::sys::path::parent_path(*modulePath); + if (llvm::sys::path::extension(parentDir) == ".swiftmodule") { + modulePath = parentDir.str(); + } + } else { + // This must be a module compiled from source-code + assert(!swiftDep->sourceFiles.empty()); + return result; } } else if (auto *swiftBinaryDep = getAsSwiftBinaryModule()) { modulePath = swiftBinaryDep->compiledModulePath; diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 8bada4e3583..84d95099889 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -19,6 +19,7 @@ add_subdirectory(ASTSectionImporter) add_subdirectory(Basic) add_subdirectory(ClangImporter) add_subdirectory(Demangling) +add_subdirectory(DependencyScan) add_subdirectory(Driver) add_subdirectory(Frontend) add_subdirectory(FrontendTool) diff --git a/lib/DependencyScan/CMakeLists.txt b/lib/DependencyScan/CMakeLists.txt new file mode 100644 index 00000000000..e5190d22e7a --- /dev/null +++ b/lib/DependencyScan/CMakeLists.txt @@ -0,0 +1,13 @@ +#set_swift_llvm_is_available() + +add_swift_host_library(swiftDependencyScan STATIC + DependencyScanningTool.cpp + ScanDependencies.cpp + StringUtils.cpp) + +target_link_libraries(swiftDependencyScan INTERFACE + clangBasic) + +target_link_libraries(swiftDependencyScan PRIVATE + swiftClangImporter + swiftFrontend) diff --git a/lib/DependencyScan/DependencyScanningTool.cpp b/lib/DependencyScan/DependencyScanningTool.cpp new file mode 100644 index 00000000000..9084d8787bd --- /dev/null +++ b/lib/DependencyScan/DependencyScanningTool.cpp @@ -0,0 +1,124 @@ +//===------------ DependencyScanningTool.cpp - Swift Compiler -------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/DependencyScan/DependencyScanningTool.h" +#include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/DiagnosticsFrontend.h" +#include "swift/Basic/LLVMInitialize.h" +#include "swift/DependencyScan/DependencyScanImpl.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" + +#include + +namespace swift { +namespace dependencies { + +DependencyScanningTool::DependencyScanningTool() + : SharedCache(std::make_unique()), + VersionedPCMInstanceCacheCache( + std::make_unique()), + PDC(), Alloc(), Saver(Alloc) {} + +llvm::ErrorOr +DependencyScanningTool::getDependencies( + ArrayRef Command, + const llvm::StringSet<> &PlaceholderModules) { + // The primary instance used to scan the query Swift source-code + auto InstanceOrErr = initCompilerInstanceForScan(Command); + if (std::error_code EC = InstanceOrErr.getError()) + return EC; + auto Instance = std::move(*InstanceOrErr); + + // Execute the scanning action, retreiving the in-memory result + auto DependenciesOrErr = performModuleScan(*Instance.get(), *SharedCache); + if (DependenciesOrErr.getError()) + return std::make_error_code(std::errc::not_supported); + auto Dependencies = std::move(*DependenciesOrErr); + + return Dependencies; +} + +llvm::ErrorOr +DependencyScanningTool::getImports(ArrayRef Command) { + // The primary instance used to scan the query Swift source-code + auto InstanceOrErr = initCompilerInstanceForScan(Command); + if (std::error_code EC = InstanceOrErr.getError()) + return EC; + auto Instance = std::move(*InstanceOrErr); + + // Execute the scanning action, retreiving the in-memory result + auto DependenciesOrErr = performModulePrescan(*Instance.get()); + if (DependenciesOrErr.getError()) + return std::make_error_code(std::errc::not_supported); + auto Dependencies = std::move(*DependenciesOrErr); + + return Dependencies; +} + +std::vector> +DependencyScanningTool::getDependencies( + ArrayRef Command, + const std::vector &BatchInput, + const llvm::StringSet<> &PlaceholderModules) { + // The primary instance used to scan Swift modules + auto InstanceOrErr = initCompilerInstanceForScan(Command); + if (std::error_code EC = InstanceOrErr.getError()) + return std::vector>( + BatchInput.size(), std::make_error_code(std::errc::invalid_argument)); + auto Instance = std::move(*InstanceOrErr); + + auto batchScanResults = performBatchModuleScan( + *Instance.get(), *SharedCache, VersionedPCMInstanceCacheCache.get(), + Saver, BatchInput); + + return batchScanResults; +} + +llvm::ErrorOr> +DependencyScanningTool::initCompilerInstanceForScan( + ArrayRef Command) { + // State unique to an individual scan + auto Instance = std::make_unique(); + Instance->addDiagnosticConsumer(&PDC); + + // Basic error checking on the arguments + if (Command.empty()) { + Instance->getDiags().diagnose(SourceLoc(), diag::error_no_frontend_args); + return std::make_error_code(std::errc::invalid_argument); + } + + CompilerInvocation Invocation; + SmallString<128> WorkingDirectory; + llvm::sys::fs::current_path(WorkingDirectory); + + // Parse arguments. + std::string CommandString; + for (const auto *c : Command) { + CommandString.append(c); + CommandString.append(" "); + } + SmallVector Args; + llvm::cl::TokenizeGNUCommandLine(CommandString, Saver, Args); + if (Invocation.parseArgs(Args, Instance->getDiags())) { + return std::make_error_code(std::errc::invalid_argument); + } + + // Setup the instance + Instance->setup(Invocation); + (void)Instance->getMainModule(); + + return Instance; +} + +} // namespace dependencies +} // namespace swift diff --git a/lib/DependencyScan/ScanDependencies.cpp b/lib/DependencyScan/ScanDependencies.cpp new file mode 100644 index 00000000000..7044a71f778 --- /dev/null +++ b/lib/DependencyScan/ScanDependencies.cpp @@ -0,0 +1,1403 @@ +//===--- ScanDependencies.cpp -- Scans the dependencies of a module -------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/DependencyScan/ScanDependencies.h" +#include "swift/AST/ASTContext.h" +#include "swift/AST/Decl.h" +#include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/DiagnosticsFrontend.h" +#include "swift/AST/Module.h" +#include "swift/AST/ModuleDependencies.h" +#include "swift/AST/ModuleLoader.h" +#include "swift/AST/SourceFile.h" +#include "swift/Basic/Defer.h" +#include "swift/Basic/LLVM.h" +#include "swift/Basic/STLExtras.h" +#include "swift/ClangImporter/ClangImporter.h" +#include "swift/DependencyScan/DependencyScanImpl.h" +#include "swift/DependencyScan/StringUtils.h" +#include "swift/Frontend/Frontend.h" +#include "swift/Frontend/FrontendOptions.h" +#include "swift/Frontend/ModuleInterfaceLoader.h" +#include "swift/Strings.h" +#include "clang/Basic/Module.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/StringSaver.h" +#include "llvm/Support/YAMLParser.h" +#include "llvm/Support/YAMLTraits.h" +#include + +using namespace swift; +using namespace swift::dependencies; +using namespace llvm::yaml; + +namespace { + +static std::string getScalaNodeText(Node *N) { + SmallString<32> Buffer; + return cast(N)->getValue(Buffer).str(); +} + +/// Parse an entry like this, where the "platforms" key-value pair is optional: +/// { +/// "swiftModuleName": "Foo", +/// "arguments": "-target 10.15", +/// "output": "../Foo.json" +/// }, +static bool parseBatchInputEntries(ASTContext &Ctx, llvm::StringSaver &saver, + Node *Node, + std::vector &result) { + auto *SN = cast(Node); + if (!SN) + return true; + for (auto It = SN->begin(); It != SN->end(); ++It) { + auto *MN = cast(&*It); + BatchScanInput entry; + Optional> Platforms; + for (auto &Pair : *MN) { + auto Key = getScalaNodeText(Pair.getKey()); + auto *Value = Pair.getValue(); + if (Key == "clangModuleName") { + entry.moduleName = saver.save(getScalaNodeText(Value)); + entry.isSwift = false; + } else if (Key == "swiftModuleName") { + entry.moduleName = saver.save(getScalaNodeText(Value)); + entry.isSwift = true; + } else if (Key == "arguments") { + entry.arguments = saver.save(getScalaNodeText(Value)); + } else if (Key == "output") { + entry.outputPath = saver.save(getScalaNodeText(Value)); + } else { + // Future proof. + continue; + } + } + if (entry.moduleName.empty()) + return true; + if (entry.outputPath.empty()) + return true; + result.emplace_back(std::move(entry)); + } + return false; +} + +static Optional> +parseBatchScanInputFile(ASTContext &ctx, StringRef batchInputPath, + llvm::StringSaver &saver) { + assert(!batchInputPath.empty()); + namespace yaml = llvm::yaml; + std::vector result; + + // Load the input file. + llvm::ErrorOr> FileBufOrErr = + llvm::MemoryBuffer::getFile(batchInputPath); + if (!FileBufOrErr) { + ctx.Diags.diagnose(SourceLoc(), diag::batch_scan_input_file_missing, + batchInputPath); + return None; + } + StringRef Buffer = FileBufOrErr->get()->getBuffer(); + + // Use a new source manager instead of the one from ASTContext because we + // don't want the Json file to be persistent. + SourceManager SM; + yaml::Stream Stream(llvm::MemoryBufferRef(Buffer, batchInputPath), + SM.getLLVMSourceMgr()); + for (auto DI = Stream.begin(); DI != Stream.end(); ++DI) { + assert(DI != Stream.end() && "Failed to read a document"); + yaml::Node *N = DI->getRoot(); + assert(N && "Failed to find a root"); + if (parseBatchInputEntries(ctx, saver, N, result)) { + ctx.Diags.diagnose(SourceLoc(), diag::batch_scan_input_file_corrupted, + batchInputPath); + return None; + } + } + return result; +} + +/// Find all of the imported Clang modules starting with the given module name. +static void findAllImportedClangModules(ASTContext &ctx, StringRef moduleName, + ModuleDependenciesCache &cache, + std::vector &allModules, + llvm::StringSet<> &knownModules) { + if (!knownModules.insert(moduleName).second) + return; + allModules.push_back(moduleName.str()); + + auto dependencies = + cache.findDependencies(moduleName, ModuleDependenciesKind::Clang); + if (!dependencies) + return; + + for (const auto &dep : dependencies->getModuleDependencies()) { + findAllImportedClangModules(ctx, dep, cache, allModules, knownModules); + } +} + +/// Resolve the direct dependencies of the given module. +static std::vector +resolveDirectDependencies(CompilerInstance &instance, ModuleDependencyID module, + ModuleDependenciesCache &cache, + InterfaceSubContextDelegate &ASTDelegate) { + auto &ctx = instance.getASTContext(); + auto knownDependencies = *cache.findDependencies(module.first, module.second); + auto isSwift = knownDependencies.isSwiftTextualModule(); + + // Find the dependencies of every module this module directly depends on. + std::set result; + for (auto dependsOn : knownDependencies.getModuleDependencies()) { + // Figure out what kind of module we need. + bool onlyClangModule = !isSwift || module.first == dependsOn; + + // Retrieve the dependencies for this module. + if (auto found = ctx.getModuleDependencies(dependsOn, onlyClangModule, + cache, ASTDelegate)) { + result.insert({dependsOn, found->getKind()}); + } + } + + if (isSwift) { + // A record of all of the Clang modules referenced from this Swift module. + std::vector allClangModules; + llvm::StringSet<> knownModules; + + // If the Swift module has a bridging header, add those dependencies. + if (knownDependencies.getBridgingHeader()) { + auto clangImporter = + static_cast(ctx.getClangModuleLoader()); + if (!clangImporter->addBridgingHeaderDependencies(module.first, cache)) { + // Grab the updated module dependencies. + // FIXME: This is such a hack. + knownDependencies = + *cache.findDependencies(module.first, module.second); + + // Add the Clang modules referenced from the bridging header to the + // set of Clang modules we know about. + auto swiftDeps = knownDependencies.getAsSwiftTextualModule(); + for (const auto &clangDep : swiftDeps->bridgingModuleDependencies) { + findAllImportedClangModules(ctx, clangDep, cache, allClangModules, + knownModules); + } + } + } + + // Find all of the Clang modules this Swift module depends on. + for (const auto &dep : result) { + if (dep.second != ModuleDependenciesKind::Clang) + continue; + + findAllImportedClangModules(ctx, dep.first, cache, allClangModules, + knownModules); + } + + // Look for overlays for each of the Clang modules. The Swift module + // directly depends on these. + for (const auto &clangDep : allClangModules) { + if (auto found = ctx.getModuleDependencies( + clangDep, /*onlyClangModule=*/false, cache, ASTDelegate)) { + // ASTContext::getModuleDependencies returns dependencies for a module + // with a given name. This Clang module may have the same name as the + // Swift module we are resolving, so we need to make sure we don't add a + // dependency from a Swift module to itself. + if ((found->getKind() == ModuleDependenciesKind::SwiftTextual || + found->getKind() == ModuleDependenciesKind::SwiftBinary || + found->getKind() == ModuleDependenciesKind::SwiftPlaceholder) && + clangDep != module.first) { + result.insert({clangDep, found->getKind()}); + } + } + } + } + return std::vector(result.begin(), result.end()); +} + +static void discoverCrosssImportOverlayDependencies( + CompilerInstance &instance, StringRef mainModuleName, + ArrayRef allDependencies, + ModuleDependenciesCache &cache, InterfaceSubContextDelegate &ASTDelegate, + llvm::function_ref action) { + // Modules explicitly imported. Only these can be secondary module. + llvm::SetVector newOverlays; + for (auto dep : allDependencies) { + auto moduleName = dep.first; + auto dependencies = *cache.findDependencies(moduleName, dep.second); + // Collect a map from secondary module name to cross-import overlay names. + auto overlayMap = dependencies.collectCrossImportOverlayNames( + instance.getASTContext(), moduleName); + if (overlayMap.empty()) + continue; + std::for_each(allDependencies.begin(), allDependencies.end(), + [&](ModuleDependencyID Id) { + // check if any explicitly imported modules can serve as a + // secondary module, and add the overlay names to the + // dependencies list. + for (auto overlayName : overlayMap[Id.first]) { + if (std::find_if(allDependencies.begin(), + allDependencies.end(), + [&](ModuleDependencyID Id) { + return Id.first == overlayName.str(); + }) == allDependencies.end()) { + newOverlays.insert(overlayName); + } + } + }); + } + // No new cross-import overlays are found, return. + if (newOverlays.empty()) + return; + // Construct a dummy main to resolve the newly discovered cross import + // overlays. + StringRef dummyMainName = "DummyMainModuleForResolvingCrossImportOverlays"; + auto dummyMainDependencies = ModuleDependencies::forMainSwiftModule({}); + + // Update main module's dependencies to include these new overlays. + auto mainDep = *cache.findDependencies(mainModuleName, + ModuleDependenciesKind::SwiftTextual); + std::for_each(newOverlays.begin(), newOverlays.end(), + [&](Identifier modName) { + dummyMainDependencies.addModuleDependency(modName.str()); + mainDep.addModuleDependency(modName.str()); + }); + cache.updateDependencies( + {mainModuleName.str(), ModuleDependenciesKind::SwiftTextual}, mainDep); + + // Record the dummy main module's direct dependencies. The dummy main module + // only directly depend on these newly discovered overlay modules. + cache.recordDependencies(dummyMainName, dummyMainDependencies); + llvm::SetVector, + std::set> + allModules; + + // Seed the all module list from the dummpy main module. + allModules.insert({dummyMainName.str(), dummyMainDependencies.getKind()}); + + // Explore the dependencies of every module. + for (unsigned currentModuleIdx = 0; currentModuleIdx < allModules.size(); + ++currentModuleIdx) { + auto module = allModules[currentModuleIdx]; + auto discoveredModules = + resolveDirectDependencies(instance, module, cache, ASTDelegate); + allModules.insert(discoveredModules.begin(), discoveredModules.end()); + } + // Report any discovered modules to the clients, which include all overlays + // and their dependencies. + std::for_each(/* +1 to exclude dummy main*/ allModules.begin() + 1, + allModules.end(), action); +} + +/// Write a single JSON field. +template +void writeJSONSingleField(llvm::raw_ostream &out, StringRef fieldName, + const T &value, unsigned indentLevel, + bool trailingComma); + +/// Write a string value as JSON. +void writeJSONValue(llvm::raw_ostream &out, StringRef value, + unsigned indentLevel) { + out << "\""; + out << value; + out << "\""; +} + +void writeJSONValue(llvm::raw_ostream &out, swiftscan_string_ref_t value, + unsigned indentLevel) { + out << "\""; + out << get_C_string(value); + out << "\""; +} + +void writeJSONValue(llvm::raw_ostream &out, swiftscan_string_set_t *value_set, + unsigned indentLevel) { + out << "[\n"; + + for (size_t i = 0; i < value_set->count; ++i) { + out.indent((indentLevel + 1) * 2); + + writeJSONValue(out, value_set->strings[i], indentLevel + 1); + + if (i != value_set->count - 1) { + out << ","; + } + out << "\n"; + } + + out.indent(indentLevel * 2); + out << "]"; +} + +void writeEncodedModuleIdJSONValue(llvm::raw_ostream &out, + swiftscan_string_ref_t value, + unsigned indentLevel) { + out << "{\n"; + static const std::string textualPrefix("swiftTextual"); + static const std::string binaryPrefix("swiftBinary"); + static const std::string placeholderPrefix("swiftPlaceholder"); + static const std::string clangPrefix("clang"); + std::string valueStr = get_C_string(value); + std::string moduleKind; + std::string moduleName; + if (!valueStr.compare(0, textualPrefix.size(), textualPrefix)) { + moduleKind = "swift"; + moduleName = valueStr.substr(textualPrefix.size() + 1); + } else if (!valueStr.compare(0, binaryPrefix.size(), binaryPrefix)) { + // FIXME: rename to be consistent in the clients (swift-driver) + moduleKind = "swiftPrebuiltExternal"; + moduleName = valueStr.substr(binaryPrefix.size() + 1); + } else if (!valueStr.compare(0, placeholderPrefix.size(), + placeholderPrefix)) { + moduleKind = "swiftPlaceholder"; + moduleName = valueStr.substr(placeholderPrefix.size() + 1); + } else { + moduleKind = "clang"; + moduleName = valueStr.substr(clangPrefix.size() + 1); + } + writeJSONSingleField(out, moduleKind, moduleName, indentLevel + 1, + /*trailingComma=*/false); + out.indent(indentLevel * 2); + out << "}"; +} + +/// Write a boolean value as JSON. +void writeJSONValue(llvm::raw_ostream &out, bool value, unsigned indentLevel) { + out.write_escaped(value ? "true" : "false"); +} + +/// Write a JSON array. +template +void writeJSONValue(llvm::raw_ostream &out, ArrayRef values, + unsigned indentLevel) { + out << "[\n"; + + for (const auto &value : values) { + + out.indent((indentLevel + 1) * 2); + + writeJSONValue(out, value, indentLevel + 1); + + if (&value != &values.back()) { + out << ","; + } + out << "\n"; + } + + out.indent(indentLevel * 2); + out << "]"; +} + +/// Write a JSON array. +template +void writeJSONValue(llvm::raw_ostream &out, const std::vector &values, + unsigned indentLevel) { + writeJSONValue(out, llvm::makeArrayRef(values), indentLevel); +} + +/// Write a single JSON field. +template +void writeJSONSingleField(llvm::raw_ostream &out, StringRef fieldName, + const T &value, unsigned indentLevel, + bool trailingComma) { + out.indent(indentLevel * 2); + writeJSONValue(out, fieldName, indentLevel); + out << ": "; + writeJSONValue(out, value, indentLevel); + if (trailingComma) + out << ","; + out << "\n"; +} + +void writeDirectDependencies(llvm::raw_ostream &out, + const swiftscan_string_set_t *dependencies, + unsigned indentLevel, bool trailingComma) { + out.indent(indentLevel * 2); + out << "\"directDependencies\": "; + out << "[\n"; + + for (size_t i = 0; i < dependencies->count; ++i) { + out.indent((indentLevel + 1) * 2); + writeEncodedModuleIdJSONValue(out, dependencies->strings[i], + indentLevel + 1); + if (i != dependencies->count - 1) { + out << ","; + } + out << "\n"; + } + + out.indent(indentLevel * 2); + out << "]"; + + if (trailingComma) + out << ","; + out << "\n"; +} + +static const swiftscan_swift_textual_details_t * +getAsTextualDependencyModule(swiftscan_module_details_t details) { + if (details->kind == SWIFTSCAN_DEPENDENCY_INFO_SWIFT_TEXTUAL) + return &details->swift_textual_details; + return nullptr; +} + +static const swiftscan_swift_placeholder_details_t * +getAsPlaceholderDependencyModule(swiftscan_module_details_t details) { + if (details->kind == SWIFTSCAN_DEPENDENCY_INFO_SWIFT_PLACEHOLDER) + return &details->swift_placeholder_details; + return nullptr; +} + +static const swiftscan_swift_binary_details_t * +getAsBinaryDependencyModule(swiftscan_module_details_t details) { + if (details->kind == SWIFTSCAN_DEPENDENCY_INFO_SWIFT_BINARY) + return &details->swift_binary_details; + return nullptr; +} + +static const swiftscan_clang_details_t * +getAsClangDependencyModule(swiftscan_module_details_t details) { + if (details->kind == SWIFTSCAN_DEPENDENCY_INFO_CLANG) + return &details->clang_details; + return nullptr; +} + +static void writePrescanJSON(llvm::raw_ostream &out, + const swiftscan_import_set_t importSet) { + // Write out a JSON containing all main module imports. + out << "{\n"; + SWIFT_DEFER { out << "}\n"; }; + + writeJSONSingleField(out, "imports", importSet->imports, 0, false); +} + +static void writeJSON(llvm::raw_ostream &out, + const swiftscan_dependency_graph_t fullDependencies) { + // Write out a JSON description of all of the dependencies. + out << "{\n"; + SWIFT_DEFER { out << "}\n"; }; + // Name of the main module. + writeJSONSingleField(out, "mainModuleName", + fullDependencies->main_module_name, + /*indentLevel=*/1, /*trailingComma=*/true); + // Write out all of the modules. + out << " \"modules\": [\n"; + SWIFT_DEFER { out << " ]\n"; }; + const auto module_set = fullDependencies->dependencies; + for (size_t mi = 0; mi < module_set->count; ++mi) { + const auto &moduleInfo = *module_set->modules[mi]; + auto &directDependencies = moduleInfo.direct_dependencies; + // The module we are describing. + out.indent(2 * 2); + writeEncodedModuleIdJSONValue(out, moduleInfo.module_name, 2); + out << ",\n"; + out.indent(2 * 2); + out << "{\n"; + auto swiftPlaceholderDeps = + getAsPlaceholderDependencyModule(moduleInfo.details); + auto swiftTextualDeps = getAsTextualDependencyModule(moduleInfo.details); + auto swiftBinaryDeps = getAsBinaryDependencyModule(moduleInfo.details); + auto clangDeps = getAsClangDependencyModule(moduleInfo.details); + + // Module path. + const char *modulePathSuffix = clangDeps ? ".pcm" : ".swiftmodule"; + + std::string modulePath; + std::string moduleKindAndName = + std::string(get_C_string(moduleInfo.module_name)); + std::string moduleName = + moduleKindAndName.substr(moduleKindAndName.find(":") + 1); + if (swiftPlaceholderDeps) + modulePath = get_C_string(swiftPlaceholderDeps->compiled_module_path); + else if (swiftBinaryDeps) + modulePath = get_C_string(swiftBinaryDeps->compiled_module_path); + else + modulePath = moduleName + modulePathSuffix; + + writeJSONSingleField(out, "modulePath", modulePath, /*indentLevel=*/3, + /*trailingComma=*/true); + + // Source files. + if (swiftTextualDeps || clangDeps) { + writeJSONSingleField(out, "sourceFiles", moduleInfo.source_files, 3, + /*trailingComma=*/true); + } + + // Direct dependencies. + if (swiftTextualDeps || swiftBinaryDeps || clangDeps) + writeDirectDependencies(out, directDependencies, 3, + /*trailingComma=*/true); + // Swift and Clang-specific details. + out.indent(3 * 2); + out << "\"details\": {\n"; + out.indent(4 * 2); + if (swiftTextualDeps) { + out << "\"swift\": {\n"; + /// Swift interface file, if there is one. The main module, for + /// example, will not have an interface file. + std::string moduleInterfacePath = + swiftTextualDeps->module_interface_path.data + ? get_C_string(swiftTextualDeps->module_interface_path) + : ""; + if (!moduleInterfacePath.empty()) { + writeJSONSingleField(out, "moduleInterfacePath", moduleInterfacePath, 5, + /*trailingComma=*/true); + writeJSONSingleField(out, "contextHash", swiftTextualDeps->context_hash, + 5, + /*trailingComma=*/true); + out.indent(5 * 2); + out << "\"commandLine\": [\n"; + for (int i = 0, count = swiftTextualDeps->command_line->count; + i < count; ++i) { + const auto &arg = + get_C_string(swiftTextualDeps->command_line->strings[i]); + out.indent(6 * 2); + out << "\"" << arg << "\""; + if (i != count - 1) + out << ","; + out << "\n"; + } + out.indent(5 * 2); + out << "],\n"; + out.indent(5 * 2); + out << "\"compiledModuleCandidates\": [\n"; + for (int i = 0, + count = swiftTextualDeps->compiled_module_candidates->count; + i < count; ++i) { + const auto &candidate = get_C_string( + swiftTextualDeps->compiled_module_candidates->strings[i]); + out.indent(6 * 2); + out << "\"" << candidate << "\""; + if (i != count - 1) + out << ","; + out << "\n"; + } + out.indent(5 * 2); + out << "],\n"; + } + bool hasBridgingHeaderPath = + swiftTextualDeps->bridging_header_path.data && + get_C_string(swiftTextualDeps->bridging_header_path)[0] != '\0'; + bool commaAfterFramework = + swiftTextualDeps->extra_pcm_args->count != 0 || hasBridgingHeaderPath; + + writeJSONSingleField(out, "isFramework", swiftTextualDeps->is_framework, + 5, commaAfterFramework); + if (swiftTextualDeps->extra_pcm_args->count != 0) { + out.indent(5 * 2); + out << "\"extraPcmArgs\": [\n"; + for (int i = 0, count = swiftTextualDeps->extra_pcm_args->count; + i < count; ++i) { + const auto &arg = + get_C_string(swiftTextualDeps->extra_pcm_args->strings[i]); + out.indent(6 * 2); + out << "\"" << arg << "\""; + if (i != count - 1) + out << ","; + out << "\n"; + } + out.indent(5 * 2); + out << (hasBridgingHeaderPath ? "],\n" : "]\n"); + } + /// Bridging header and its source file dependencies, if any. + if (hasBridgingHeaderPath) { + out.indent(5 * 2); + out << "\"bridgingHeader\": {\n"; + writeJSONSingleField(out, "path", + swiftTextualDeps->bridging_header_path, 6, + /*trailingComma=*/true); + writeJSONSingleField(out, "sourceFiles", + swiftTextualDeps->bridging_source_files, 6, + /*trailingComma=*/true); + writeJSONSingleField(out, "moduleDependencies", + swiftTextualDeps->bridging_module_dependencies, 6, + /*trailingComma=*/false); + out.indent(5 * 2); + out << "}\n"; + } + } else if (swiftPlaceholderDeps) { + out << "\"swiftPlaceholder\": {\n"; + + // Module doc file + if (swiftPlaceholderDeps->module_doc_path.data && + get_C_string(swiftPlaceholderDeps->module_doc_path)[0] != '\0') + writeJSONSingleField(out, "moduleDocPath", + swiftPlaceholderDeps->module_doc_path, + /*indentLevel=*/5, + /*trailingComma=*/true); + + // Module Source Info file + if (swiftPlaceholderDeps->module_source_info_path.data && + get_C_string(swiftPlaceholderDeps->module_source_info_path)[0] != + '\0') + writeJSONSingleField(out, "moduleSourceInfoPath", + swiftPlaceholderDeps->module_source_info_path, + /*indentLevel=*/5, + /*trailingComma=*/false); + } else if (swiftBinaryDeps) { + out << "\"swiftPrebuiltExternal\": {\n"; + bool hasCompiledModulePath = + swiftBinaryDeps->compiled_module_path.data && + get_C_string(swiftBinaryDeps->compiled_module_path)[0] != '\0'; + assert(hasCompiledModulePath && + "Expected .swiftmodule for a Binary Swift Module Dependency."); + + writeJSONSingleField(out, "compiledModulePath", + swiftBinaryDeps->compiled_module_path, + /*indentLevel=*/5, + /*trailingComma=*/true); + // Module doc file + if (swiftBinaryDeps->module_doc_path.data && + get_C_string(swiftBinaryDeps->module_doc_path)[0] != '\0') + writeJSONSingleField(out, "moduleDocPath", + swiftBinaryDeps->module_doc_path, + /*indentLevel=*/5, + /*trailingComma=*/true); + // Module Source Info file + if (swiftBinaryDeps->module_source_info_path.data && + get_C_string(swiftBinaryDeps->module_source_info_path)[0] != '\0') + writeJSONSingleField(out, "moduleSourceInfoPath", + swiftBinaryDeps->module_source_info_path, + /*indentLevel=*/5, + /*trailingComma=*/false); + } else { + out << "\"clang\": {\n"; + + // Module map file. + writeJSONSingleField(out, "moduleMapPath", clangDeps->module_map_path, 5, + /*trailingComma=*/true); + + // Context hash. + writeJSONSingleField(out, "contextHash", clangDeps->context_hash, 5, + /*trailingComma=*/true); + + // Command line. + writeJSONSingleField(out, "commandLine", clangDeps->command_line, 5, + /*trailingComma=*/false); + } + + out.indent(4 * 2); + out << "}\n"; + out.indent(3 * 2); + out << "}\n"; + out.indent(2 * 2); + out << "}"; + + if (mi != module_set->count - 1) + out << ","; + out << "\n"; + } +} + +static std::string createEncodedModuleKindAndName(ModuleDependencyID id) { + switch (id.second) { + case ModuleDependenciesKind::SwiftTextual: + return "swiftTextual:" + id.first; + case ModuleDependenciesKind::SwiftBinary: + return "swiftBinary:" + id.first; + case ModuleDependenciesKind::SwiftPlaceholder: + return "swiftPlaceholder:" + id.first; + case ModuleDependenciesKind::Clang: + return "clang:" + id.first; + } +} + +static swiftscan_dependency_graph_t +generateFullDependencyGraph(CompilerInstance &instance, + ModuleDependenciesCache &cache, + InterfaceSubContextDelegate &ASTDelegate, + ArrayRef allModules) { + if (allModules.empty()) { + return nullptr; + } + + std::string mainModuleName = allModules.front().first; + swiftscan_dependency_set_t *dependencySet = new swiftscan_dependency_set_t; + dependencySet->count = allModules.size(); + dependencySet->modules = + new swiftscan_dependency_info_t[dependencySet->count]; + + for (size_t i = 0; i < allModules.size(); ++i) { + const auto &module = allModules[i]; + // Grab the completed module dependencies. + auto moduleDeps = *cache.findDependencies(module.first, module.second); + + // Collect all the required pieces to build a ModuleInfo + auto swiftPlaceholderDeps = moduleDeps.getAsPlaceholderDependencyModule(); + auto swiftTextualDeps = moduleDeps.getAsSwiftTextualModule(); + auto swiftBinaryDeps = moduleDeps.getAsSwiftBinaryModule(); + auto clangDeps = moduleDeps.getAsClangModule(); + + // ModulePath + const char *modulePathSuffix = + moduleDeps.isSwiftModule() ? ".swiftmodule" : ".pcm"; + std::string modulePath; + if (swiftPlaceholderDeps) + modulePath = swiftPlaceholderDeps->compiledModulePath; + else if (swiftBinaryDeps) + modulePath = swiftBinaryDeps->compiledModulePath; + else + modulePath = module.first + modulePathSuffix; + + // SourceFiles + std::vector sourceFiles; + if (swiftTextualDeps) { + sourceFiles = swiftTextualDeps->sourceFiles; + } else if (clangDeps) { + sourceFiles = clangDeps->fileDependencies; + } + + // DirectDependencies + auto directDependencies = resolveDirectDependencies( + instance, ModuleDependencyID(module.first, module.second), cache, + ASTDelegate); + + // Generate a swiftscan_clang_details_t object based on the dependency kind + auto getModuleDetails = [&]() -> swiftscan_module_details_t { + swiftscan_module_details_s *details = new swiftscan_module_details_s; + if (swiftTextualDeps) { + swiftscan_string_ref_t moduleInterfacePath = + swiftTextualDeps->swiftInterfaceFile.hasValue() + ? create_clone( + swiftTextualDeps->swiftInterfaceFile.getValue().c_str()) + : create_null(); + swiftscan_string_ref_t bridgingHeaderPath = + swiftTextualDeps->bridgingHeaderFile.hasValue() + ? create_clone( + swiftTextualDeps->bridgingHeaderFile.getValue().c_str()) + : create_null(); + + details->kind = SWIFTSCAN_DEPENDENCY_INFO_SWIFT_TEXTUAL; + + details->swift_textual_details = { + moduleInterfacePath, + create_set(swiftTextualDeps->compiledModuleCandidates), + bridgingHeaderPath, + create_set(swiftTextualDeps->bridgingSourceFiles), + create_set(swiftTextualDeps->bridgingModuleDependencies), + create_set(swiftTextualDeps->buildCommandLine), + create_set(swiftTextualDeps->extraPCMArgs), + create_clone(swiftTextualDeps->contextHash.c_str()), + swiftTextualDeps->isFramework}; + } else if (swiftPlaceholderDeps) { + details->kind = SWIFTSCAN_DEPENDENCY_INFO_SWIFT_PLACEHOLDER; + details->swift_placeholder_details = { + create_clone(swiftPlaceholderDeps->compiledModulePath.c_str()), + create_clone(swiftPlaceholderDeps->moduleDocPath.c_str()), + create_clone(swiftPlaceholderDeps->sourceInfoPath.c_str())}; + } else if (swiftBinaryDeps) { + details->kind = SWIFTSCAN_DEPENDENCY_INFO_SWIFT_BINARY; + details->swift_binary_details = { + create_clone(swiftBinaryDeps->compiledModulePath.c_str()), + create_clone(swiftBinaryDeps->moduleDocPath.c_str()), + create_clone(swiftBinaryDeps->sourceInfoPath.c_str())}; + } else { + // Clang module details + details->kind = SWIFTSCAN_DEPENDENCY_INFO_CLANG; + details->clang_details = { + create_clone(clangDeps->moduleMapFile.c_str()), + create_clone(clangDeps->contextHash.c_str()), + create_set(clangDeps->nonPathCommandLine)}; + } + return details; + }; + + swiftscan_dependency_info_s *moduleInfo = new swiftscan_dependency_info_s; + dependencySet->modules[i] = moduleInfo; + + std::string encodedModuleName = createEncodedModuleKindAndName(module); + auto ttt = create_clone(encodedModuleName.c_str()); + moduleInfo->module_name = ttt; + moduleInfo->module_path = create_clone(modulePath.c_str()); + moduleInfo->source_files = create_set(sourceFiles); + + // Create a direct dependencies set according to the output format + std::vector bridgedDependencyNames; + for (const auto &dep : directDependencies) { + std::string dependencyKindAndName; + switch (dep.second) { + case ModuleDependenciesKind::SwiftTextual: + dependencyKindAndName = "swiftTextual"; + break; + case ModuleDependenciesKind::SwiftBinary: + dependencyKindAndName = "swiftBinary"; + break; + case ModuleDependenciesKind::SwiftPlaceholder: + dependencyKindAndName = "swiftPlaceholder"; + break; + case ModuleDependenciesKind::Clang: + dependencyKindAndName = "clang"; + break; + } + dependencyKindAndName += ":"; + dependencyKindAndName += dep.first; + bridgedDependencyNames.push_back(dependencyKindAndName); + } + + moduleInfo->direct_dependencies = create_set(bridgedDependencyNames); + moduleInfo->details = getModuleDetails(); + } + + swiftscan_dependency_graph_t result = new swiftscan_dependency_graph_s; + result->main_module_name = create_clone(mainModuleName.c_str()); + result->dependencies = dependencySet; + return result; +} + +static bool diagnoseCycle(CompilerInstance &instance, + ModuleDependenciesCache &cache, + ModuleDependencyID mainId, + InterfaceSubContextDelegate &astDelegate) { + llvm::SetVector, + std::set> + openSet; + llvm::SetVector, + std::set> + closeSet; + // Start from the main module. + openSet.insert(mainId); + while (!openSet.empty()) { + auto &lastOpen = openSet.back(); + auto beforeSize = openSet.size(); + for (auto dep : + resolveDirectDependencies(instance, lastOpen, cache, astDelegate)) { + if (closeSet.count(dep)) + continue; + if (openSet.insert(dep)) { + break; + } else { + // Find a cycle, diagnose. + auto startIt = std::find(openSet.begin(), openSet.end(), dep); + assert(startIt != openSet.end()); + llvm::SmallString<64> buffer; + for (auto it = startIt; it != openSet.end(); ++it) { + buffer.append(it->first); + buffer.append((it->second == ModuleDependenciesKind::SwiftTextual || + it->second == ModuleDependenciesKind::SwiftBinary) + ? ".swiftmodule" + : ".pcm"); + buffer.append(" -> "); + } + buffer.append(startIt->first); + buffer.append( + (startIt->second == ModuleDependenciesKind::SwiftTextual || + startIt->second == ModuleDependenciesKind::SwiftBinary) + ? ".swiftmodule" + : ".pcm"); + instance.getASTContext().Diags.diagnose( + SourceLoc(), diag::scanner_find_cycle, buffer.str()); + return true; + } + } + // No new node added. We can close this node + if (openSet.size() == beforeSize) { + closeSet.insert(openSet.back()); + openSet.pop_back(); + } else { + assert(openSet.size() == beforeSize + 1); + } + } + assert(openSet.empty()); + return false; +} + +using CompilerArgInstanceCacheMap = + llvm::StringMap, + std::unique_ptr>>; + +static bool +forEachBatchEntry(CompilerInstance &invocationInstance, + ModuleDependenciesCache &invocationCache, + CompilerArgInstanceCacheMap *versionedPCMInstanceCache, + llvm::StringSaver &saver, + const std::vector &batchInput, + llvm::function_ref + scanningAction) { + const CompilerInvocation &invok = invocationInstance.getInvocation(); + bool localSubInstanceMap = false; + CompilerArgInstanceCacheMap *subInstanceMap; + if (versionedPCMInstanceCache) + subInstanceMap = versionedPCMInstanceCache; + else { + subInstanceMap = new CompilerArgInstanceCacheMap; + localSubInstanceMap = true; + } + + auto &diags = invocationInstance.getDiags(); + ForwardingDiagnosticConsumer FDC(invocationInstance.getDiags()); + + for (auto &entry : batchInput) { + CompilerInstance *pInstance = nullptr; + ModuleDependenciesCache *pCache = nullptr; + if (entry.arguments.empty()) { + // Use the compiler's instance if no arguments are specified. + pInstance = &invocationInstance; + pCache = &invocationCache; + } else if (subInstanceMap->count(entry.arguments)) { + // Use the previously created instance if we've seen the arguments + // before. + pInstance = (*subInstanceMap)[entry.arguments].first.get(); + pCache = (*subInstanceMap)[entry.arguments].second.get(); + // We must update the search paths of this instance to instead reflect those of the current + // invocation's. + for (auto &path : invocationInstance.getASTContext().SearchPathOpts.ImportSearchPaths) + pInstance->getASTContext().addSearchPath(path, false, false); + for (auto &path : invocationInstance.getASTContext().SearchPathOpts.FrameworkSearchPaths) + pInstance->getASTContext().addSearchPath(path.Path, true, path.IsSystem); + + } else { + // Create a new instance by the arguments and save it in the map. + subInstanceMap->insert( + {entry.arguments, + std::make_pair(std::make_unique(), + std::make_unique())}); + + pInstance = (*subInstanceMap)[entry.arguments].first.get(); + pCache = (*subInstanceMap)[entry.arguments].second.get(); + SmallVector args; + llvm::cl::TokenizeGNUCommandLine(entry.arguments, saver, args); + CompilerInvocation subInvok = invok; + pInstance->addDiagnosticConsumer(&FDC); + if (subInvok.parseArgs(args, diags)) { + invocationInstance.getDiags().diagnose( + SourceLoc(), diag::scanner_arguments_invalid, entry.arguments); + return true; + } + if (pInstance->setup(subInvok)) { + invocationInstance.getDiags().diagnose( + SourceLoc(), diag::scanner_arguments_invalid, entry.arguments); + return true; + } + } + assert(pInstance); + assert(pCache); + scanningAction(entry, *pInstance, *pCache); + } + + if (localSubInstanceMap) + delete subInstanceMap; + return false; +} + +static ModuleDependencies +identifyMainModuleDependencies(CompilerInstance &instance) { + ModuleDecl *mainModule = instance.getMainModule(); + // Main module file name. + auto newExt = file_types::getExtension(file_types::TY_SwiftModuleFile); + llvm::SmallString<32> mainModulePath = mainModule->getName().str(); + llvm::sys::path::replace_extension(mainModulePath, newExt); + + std::string apinotesVer = + (llvm::Twine("-fapinotes-swift-version=") + + instance.getASTContext() + .LangOpts.EffectiveLanguageVersion.asAPINotesVersionString()) + .str(); + // Compute the dependencies of the main module. + auto mainDependencies = ModuleDependencies::forMainSwiftModule( + {// ExtraPCMArgs + "-Xcc", "-target", "-Xcc", + instance.getASTContext().LangOpts.Target.str(), "-Xcc", apinotesVer}); + + // Compute Implicit dependencies of the main module + { + llvm::StringSet<> alreadyAddedModules; + for (auto fileUnit : mainModule->getFiles()) { + auto sf = dyn_cast(fileUnit); + if (!sf) + continue; + + mainDependencies.addModuleDependencies(*sf, alreadyAddedModules); + } + + const auto &importInfo = mainModule->getImplicitImportInfo(); + + // Swift standard library. + switch (importInfo.StdlibKind) { + case ImplicitStdlibKind::None: + case ImplicitStdlibKind::Builtin: + break; + + case ImplicitStdlibKind::Stdlib: + mainDependencies.addModuleDependency("Swift", &alreadyAddedModules); + break; + } + + // Add any implicit module names. + for (const auto &import : importInfo.AdditionalUnloadedImports) { + mainDependencies.addModuleDependency(import.module.getModulePath(), + &alreadyAddedModules); + } + + // Already-loaded, implicitly imported module names. + for (const auto &import : importInfo.AdditionalImports) { + mainDependencies.addModuleDependency( + import.module.importedModule->getNameStr(), &alreadyAddedModules); + } + + // Add the bridging header. + if (!importInfo.BridgingHeaderPath.empty()) { + mainDependencies.addBridgingHeader(importInfo.BridgingHeaderPath); + } + + // If we are to import the underlying Clang module of the same name, + // add a dependency with the same name to trigger the search. + if (importInfo.ShouldImportUnderlyingModule) { + mainDependencies.addModuleDependency(mainModule->getName().str(), + &alreadyAddedModules); + } + } + + return mainDependencies; +} + +} // namespace + +bool swift::dependencies::scanDependencies(CompilerInstance &instance) { + ASTContext &Context = instance.getASTContext(); + const FrontendOptions &opts = instance.getInvocation().getFrontendOptions(); + std::string path = opts.InputsAndOutputs.getSingleOutputFilename(); + std::error_code EC; + llvm::raw_fd_ostream out(path, EC, llvm::sys::fs::F_None); + // `-scan-dependencies` invocations use a single new instance + // of a module cache + ModuleDependenciesCache cache; + if (out.has_error() || EC) { + Context.Diags.diagnose(SourceLoc(), diag::error_opening_output, path, + EC.message()); + out.clear_error(); + return true; + } + + // Execute scan, and write JSON output to the output stream + auto dependenciesOrErr = performModuleScan(instance, cache); + if (dependenciesOrErr.getError()) + return true; + auto dependencies = std::move(*dependenciesOrErr); + // Write out the JSON description. + writeJSON(out, dependencies); + // This process succeeds regardless of whether any errors occurred. + // FIXME: We shouldn't need this, but it's masking bugs in our scanning + // logic where we don't create a fresh context when scanning Swift interfaces + // that includes their own command-line flags. + Context.Diags.resetHadAnyError(); + return false; +} + +bool swift::dependencies::prescanDependencies(CompilerInstance &instance) { + ASTContext &Context = instance.getASTContext(); + const FrontendOptions &opts = instance.getInvocation().getFrontendOptions(); + std::string path = opts.InputsAndOutputs.getSingleOutputFilename(); + std::error_code EC; + llvm::raw_fd_ostream out(path, EC, llvm::sys::fs::F_None); + // `-scan-dependencies` invocations use a single new instance + // of a module cache + ModuleDependenciesCache cache; + if (out.has_error() || EC) { + Context.Diags.diagnose(SourceLoc(), diag::error_opening_output, path, + EC.message()); + out.clear_error(); + return true; + } + + // Execute import prescan, and write JSON output to the output stream + auto importSetOrErr = performModulePrescan(instance); + if (importSetOrErr.getError()) + return true; + auto importSet = std::move(*importSetOrErr); + + // Serialize and output main module dependencies only and exit. + writePrescanJSON(out, importSet); + // This process succeeds regardless of whether any errors occurred. + // FIXME: We shouldn't need this, but it's masking bugs in our scanning + // logic where we don't create a fresh context when scanning Swift interfaces + // that includes their own command-line flags. + Context.Diags.resetHadAnyError(); + return false; +} + +bool swift::dependencies::batchScanDependencies( + CompilerInstance &instance, llvm::StringRef batchInputFile) { + // The primary cache used for scans carried out with the compiler instance + // we have created + ModuleDependenciesCache cache; + (void)instance.getMainModule(); + llvm::BumpPtrAllocator alloc; + llvm::StringSaver saver(alloc); + auto batchInput = + parseBatchScanInputFile(instance.getASTContext(), batchInputFile, saver); + if (!batchInput.hasValue()) + return true; + + auto batchScanResults = performBatchModuleScan( + instance, cache, /*versionedPCMInstanceCache*/ nullptr, saver, + *batchInput); + + // Write the result JSON to the specified output path, for each entry + auto ientries = batchInput->cbegin(); + auto iresults = batchScanResults.cbegin(); + for (; ientries != batchInput->end() and iresults != batchScanResults.end(); + ++ientries, ++iresults) { + std::error_code EC; + llvm::raw_fd_ostream out((*ientries).outputPath, EC, llvm::sys::fs::F_None); + if ((*iresults).getError()) + return true; + + writeJSON(out, **iresults); + } + return false; +} + +bool swift::dependencies::batchPrescanDependencies( + CompilerInstance &instance, llvm::StringRef batchInputFile) { + // The primary cache used for scans carried out with the compiler instance + // we have created + ModuleDependenciesCache cache; + (void)instance.getMainModule(); + llvm::BumpPtrAllocator alloc; + llvm::StringSaver saver(alloc); + auto batchInput = + parseBatchScanInputFile(instance.getASTContext(), batchInputFile, saver); + if (!batchInput.hasValue()) + return true; + + auto batchPrescanResults = + performBatchModulePrescan(instance, cache, saver, *batchInput); + + // Write the result JSON to the specified output path, for each entry + auto ientries = batchInput->cbegin(); + auto iresults = batchPrescanResults.cbegin(); + for (; + ientries != batchInput->end() and iresults != batchPrescanResults.end(); + ++ientries, ++iresults) { + std::error_code EC; + llvm::raw_fd_ostream out((*ientries).outputPath, EC, llvm::sys::fs::F_None); + if ((*iresults).getError()) + return true; + + writePrescanJSON(out, **iresults); + } + return false; +} + +llvm::ErrorOr +swift::dependencies::performModuleScan(CompilerInstance &instance, + ModuleDependenciesCache &cache) { + ModuleDecl *mainModule = instance.getMainModule(); + // First, identify the dependencies of the main module + auto mainDependencies = identifyMainModuleDependencies(instance); + // Add the main module. + StringRef mainModuleName = mainModule->getNameStr(); + llvm::SetVector, + std::set> + allModules; + + allModules.insert({mainModuleName.str(), mainDependencies.getKind()}); + cache.recordDependencies(mainModuleName, std::move(mainDependencies)); + auto &ctx = instance.getASTContext(); + auto ModuleCachePath = getModuleCachePathFromClang( + ctx.getClangModuleLoader()->getClangInstance()); + auto &FEOpts = instance.getInvocation().getFrontendOptions(); + ModuleInterfaceLoaderOptions LoaderOpts(FEOpts); + InterfaceSubContextDelegateImpl ASTDelegate( + ctx.SourceMgr, ctx.Diags, ctx.SearchPathOpts, ctx.LangOpts, + ctx.ClangImporterOpts, LoaderOpts, + /*buildModuleCacheDirIfAbsent*/ false, ModuleCachePath, + FEOpts.PrebuiltModuleCachePath, + FEOpts.SerializeModuleInterfaceDependencyHashes, + FEOpts.shouldTrackSystemDependencies()); + + // Explore the dependencies of every module. + for (unsigned currentModuleIdx = 0; currentModuleIdx < allModules.size(); + ++currentModuleIdx) { + auto module = allModules[currentModuleIdx]; + auto discoveredModules = + resolveDirectDependencies(instance, module, cache, ASTDelegate); + allModules.insert(discoveredModules.begin(), discoveredModules.end()); + } + + // We have all explicit imports now, resolve cross import overlays. + discoverCrosssImportOverlayDependencies( + instance, mainModuleName, + /*All transitive dependencies*/ allModules.getArrayRef().slice(1), cache, + ASTDelegate, [&](ModuleDependencyID id) { allModules.insert(id); }); + + // Dignose cycle in dependency graph. + if (diagnoseCycle(instance, cache, /*MainModule*/ allModules.front(), + ASTDelegate)) + return std::make_error_code(std::errc::not_supported); + auto dependencyGraph = generateFullDependencyGraph( + instance, cache, ASTDelegate, allModules.getArrayRef()); + // Update the dependency tracker. + if (auto depTracker = instance.getDependencyTracker()) { + for (auto module : allModules) { + auto deps = cache.findDependencies(module.first, module.second); + if (!deps) + continue; + + if (auto swiftDeps = deps->getAsSwiftTextualModule()) { + if (auto swiftInterfaceFile = swiftDeps->swiftInterfaceFile) + depTracker->addDependency(*swiftInterfaceFile, /*IsSystem=*/false); + for (const auto &sourceFile : swiftDeps->sourceFiles) + depTracker->addDependency(sourceFile, /*IsSystem=*/false); + for (const auto &bridgingSourceFile : swiftDeps->bridgingSourceFiles) + depTracker->addDependency(bridgingSourceFile, /*IsSystem=*/false); + } else if (auto clangDeps = deps->getAsClangModule()) { + if (!clangDeps->moduleMapFile.empty()) + depTracker->addDependency(clangDeps->moduleMapFile, + /*IsSystem=*/false); + for (const auto &sourceFile : clangDeps->fileDependencies) + depTracker->addDependency(sourceFile, /*IsSystem=*/false); + } + } + } + + return dependencyGraph; +} + +llvm::ErrorOr +swift::dependencies::performModulePrescan(CompilerInstance &instance) { + // Execute import prescan, and write JSON output to the output stream + auto mainDependencies = identifyMainModuleDependencies(instance); + auto *importSet = new swiftscan_import_set_s; + importSet->imports = create_set(mainDependencies.getModuleDependencies()); + return importSet; +} + +std::vector> +swift::dependencies::performBatchModuleScan( + CompilerInstance &invocationInstance, + ModuleDependenciesCache &invocationCache, + CompilerArgInstanceCacheMap *versionedPCMInstanceCache, + llvm::StringSaver &saver, const std::vector &batchInput) { + std::vector> batchScanResult; + batchScanResult.reserve(batchInput.size()); + + // Perform a full dependency scan for each batch entry module + forEachBatchEntry( + invocationInstance, invocationCache, versionedPCMInstanceCache, saver, + batchInput, + [&batchScanResult](BatchScanInput entry, CompilerInstance &instance, + ModuleDependenciesCache &cache) { + StringRef moduleName = entry.moduleName; + bool isClang = !entry.isSwift; + ASTContext &ctx = instance.getASTContext(); + auto &FEOpts = instance.getInvocation().getFrontendOptions(); + ModuleInterfaceLoaderOptions LoaderOpts(FEOpts); + auto ModuleCachePath = getModuleCachePathFromClang( + ctx.getClangModuleLoader()->getClangInstance()); + + llvm::SetVector, + std::set> + allModules; + InterfaceSubContextDelegateImpl ASTDelegate( + ctx.SourceMgr, ctx.Diags, ctx.SearchPathOpts, ctx.LangOpts, + ctx.ClangImporterOpts, LoaderOpts, + /*buildModuleCacheDirIfAbsent*/ false, ModuleCachePath, + FEOpts.PrebuiltModuleCachePath, + FEOpts.SerializeModuleInterfaceDependencyHashes, + FEOpts.shouldTrackSystemDependencies()); + Optional rootDeps; + if (isClang) { + // Loading the clang module using Clang importer. + // This action will populate the cache with the main module's + // dependencies. + rootDeps = ctx.getModuleDependencies(moduleName, /*IsClang*/ true, + cache, ASTDelegate); + } else { + // Loading the swift module's dependencies. + rootDeps = + ctx.getSwiftModuleDependencies(moduleName, cache, ASTDelegate); + } + if (!rootDeps.hasValue()) { + // We cannot find the clang module, abort. + batchScanResult.push_back( + std::make_error_code(std::errc::invalid_argument)); + return; + } + // Add the main module. + allModules.insert( + {moduleName.str(), isClang ? ModuleDependenciesKind::Clang + : ModuleDependenciesKind::SwiftTextual}); + + // Explore the dependencies of every module. + for (unsigned currentModuleIdx = 0; + currentModuleIdx < allModules.size(); ++currentModuleIdx) { + auto module = allModules[currentModuleIdx]; + auto discoveredModules = + resolveDirectDependencies(instance, module, cache, ASTDelegate); + allModules.insert(discoveredModules.begin(), discoveredModules.end()); + } + + batchScanResult.push_back(generateFullDependencyGraph( + instance, cache, ASTDelegate, allModules.getArrayRef())); + }); + + return batchScanResult; +} + +std::vector> +swift::dependencies::performBatchModulePrescan( + CompilerInstance &instance, ModuleDependenciesCache &cache, + llvm::StringSaver &saver, const std::vector &batchInput) { + std::vector> batchPrescanResult; + + // Perform a full dependency scan for each batch entry module + forEachBatchEntry( + instance, cache, /*versionedPCMInstanceCache*/ nullptr, saver, batchInput, + [&batchPrescanResult](BatchScanInput entry, CompilerInstance &instance, + ModuleDependenciesCache &cache) { + StringRef moduleName = entry.moduleName; + bool isClang = !entry.isSwift; + ASTContext &ctx = instance.getASTContext(); + auto &FEOpts = instance.getInvocation().getFrontendOptions(); + ModuleInterfaceLoaderOptions LoaderOpts(FEOpts); + auto ModuleCachePath = getModuleCachePathFromClang( + ctx.getClangModuleLoader()->getClangInstance()); + llvm::SetVector, + std::set> + allModules; + InterfaceSubContextDelegateImpl ASTDelegate( + ctx.SourceMgr, ctx.Diags, ctx.SearchPathOpts, ctx.LangOpts, + ctx.ClangImporterOpts, LoaderOpts, + /*buildModuleCacheDirIfAbsent*/ false, ModuleCachePath, + FEOpts.PrebuiltModuleCachePath, + FEOpts.SerializeModuleInterfaceDependencyHashes, + FEOpts.shouldTrackSystemDependencies()); + Optional rootDeps; + if (isClang) { + // Loading the clang module using Clang importer. + // This action will populate the cache with the main module's + // dependencies. + rootDeps = ctx.getModuleDependencies(moduleName, /*IsClang*/ + true, cache, ASTDelegate); + } else { + // Loading the swift module's dependencies. + rootDeps = + ctx.getSwiftModuleDependencies(moduleName, cache, ASTDelegate); + } + if (!rootDeps.hasValue()) { + // We cannot find the clang module, abort. + batchPrescanResult.push_back( + std::make_error_code(std::errc::invalid_argument)); + return; + } + + auto *importSet = new swiftscan_import_set_s; + importSet->imports = create_set(rootDeps->getModuleDependencies()); + batchPrescanResult.push_back(importSet); + }); + + return batchPrescanResult; +} diff --git a/lib/DependencyScan/StringUtils.cpp b/lib/DependencyScan/StringUtils.cpp new file mode 100644 index 00000000000..47a81033e77 --- /dev/null +++ b/lib/DependencyScan/StringUtils.cpp @@ -0,0 +1,62 @@ +//===- StringUtils.cpp - Routines for manipulating swiftscan_string_ref_t -===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/DependencyScan/StringUtils.h" +#include +#include + +namespace swift { +namespace dependencies { + +swiftscan_string_ref_t create_null() { + swiftscan_string_ref_t str; + str.data = nullptr; + str.length = 0; + return str; +} + +swiftscan_string_ref_t create_clone(const char *string) { + if (!string) + return create_null(); + + if (string[0] == '\0') + return create_null(); + + swiftscan_string_ref_t str; + str.data = strdup(string); + str.length = strlen(string); + return str; +} + +swiftscan_string_set_t *create_set(const std::vector &strings) { + swiftscan_string_set_t *set = new swiftscan_string_set_t; + set->count = strings.size(); + set->strings = new swiftscan_string_ref_t[set->count]; + for (unsigned SI = 0, SE = set->count; SI < SE; ++SI) + set->strings[SI] = create_clone(strings[SI].c_str()); + return set; +} + +swiftscan_string_set_t *create_set(int count, const char **strings) { + swiftscan_string_set_t *set = new swiftscan_string_set_t; + set->count = count; + set->strings = new swiftscan_string_ref_t[set->count]; + for (unsigned SI = 0, SE = set->count; SI < SE; ++SI) + set->strings[SI] = create_clone(strings[SI]); + return set; +} + +const char *get_C_string(swiftscan_string_ref_t string) { + return static_cast(string.data); +} +} // namespace dependencies +} // namespace swift diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index 84e6cab6271..603163429d5 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -379,8 +379,6 @@ ArgsToFrontendOptionsConverter::determineRequestedAction(const ArgList &args) { return FrontendOptions::ActionType::EmitImportedModules; if (Opt.matches(OPT_scan_dependencies)) return FrontendOptions::ActionType::ScanDependencies; - if (Opt.matches(OPT_scan_clang_dependencies)) - return FrontendOptions::ActionType::ScanClangDependencies; if (Opt.matches(OPT_parse)) return FrontendOptions::ActionType::Parse; if (Opt.matches(OPT_resolve_imports)) diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 3d5732b2ca4..a14ec7e20fb 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -321,22 +321,11 @@ void CompilerInstance::setupDependencyTrackerIfNeeded() { DepTracker = std::make_unique(*collectionMode); } -void CompilerInstance::setUpModuleDependencyCacheIfNeeded() { - const auto &Invocation = getInvocation(); - const auto &opts = Invocation.getFrontendOptions(); - if (opts.RequestedAction == FrontendOptions::ActionType::ScanDependencies || - opts.RequestedAction == FrontendOptions::ActionType::ScanClangDependencies) { - ModDepCache = std::make_unique(); - } -} - bool CompilerInstance::setup(const CompilerInvocation &Invok) { Invocation = Invok; setupDependencyTrackerIfNeeded(); - setUpModuleDependencyCacheIfNeeded(); - // If initializing the overlay file system fails there's no sense in // continuing because the compiler will read the wrong files. if (setUpVirtualFileSystemOverlays()) diff --git a/lib/Frontend/FrontendOptions.cpp b/lib/Frontend/FrontendOptions.cpp index b2579fdf268..812e007c1c2 100644 --- a/lib/Frontend/FrontendOptions.cpp +++ b/lib/Frontend/FrontendOptions.cpp @@ -66,7 +66,6 @@ bool FrontendOptions::needsProperModuleName(ActionType action) { case ActionType::DumpTypeInfo: case ActionType::EmitPCM: case ActionType::ScanDependencies: - case ActionType::ScanClangDependencies: return true; } llvm_unreachable("Unknown ActionType"); @@ -80,7 +79,6 @@ bool FrontendOptions::shouldActionOnlyParse(ActionType action) { case ActionType::DumpInterfaceHash: case ActionType::EmitImportedModules: case ActionType::ScanDependencies: - case ActionType::ScanClangDependencies: case ActionType::PrintVersion: case ActionType::PrintFeature: return true; @@ -98,7 +96,6 @@ bool FrontendOptions::doesActionRequireSwiftStandardLibrary(ActionType action) { case ActionType::DumpInterfaceHash: case ActionType::EmitImportedModules: case ActionType::ScanDependencies: - case ActionType::ScanClangDependencies: case ActionType::PrintVersion: case ActionType::EmitPCH: case ActionType::EmitPCM: @@ -146,7 +143,6 @@ bool FrontendOptions::doesActionRequireInputs(ActionType action) { case ActionType::DumpInterfaceHash: case ActionType::EmitImportedModules: case ActionType::ScanDependencies: - case ActionType::ScanClangDependencies: case ActionType::EmitPCH: case ActionType::EmitPCM: case ActionType::DumpPCM: @@ -191,7 +187,6 @@ bool FrontendOptions::doesActionPerformEndOfPipelineActions(ActionType action) { case ActionType::DumpInterfaceHash: case ActionType::EmitImportedModules: case ActionType::ScanDependencies: - case ActionType::ScanClangDependencies: case ActionType::CompileModuleFromInterface: case ActionType::TypecheckModuleFromInterface: case ActionType::ResolveImports: @@ -308,7 +303,6 @@ FrontendOptions::formatForPrincipalOutputFileForAction(ActionType action) { return TY_ClangModuleFile; case ActionType::ScanDependencies: - case ActionType::ScanClangDependencies: return TY_JSONDependencies; case ActionType::PrintFeature: return TY_JSONFeatures; @@ -352,7 +346,6 @@ bool FrontendOptions::canActionEmitDependencies(ActionType action) { case ActionType::EmitImportedModules: case ActionType::EmitPCM: case ActionType::ScanDependencies: - case ActionType::ScanClangDependencies: return true; } llvm_unreachable("unhandled action"); @@ -378,7 +371,6 @@ bool FrontendOptions::canActionEmitReferenceDependencies(ActionType action) { case ActionType::EmitPCM: case ActionType::DumpPCM: case ActionType::ScanDependencies: - case ActionType::ScanClangDependencies: case ActionType::PrintVersion: case ActionType::PrintFeature: return false; @@ -432,7 +424,6 @@ bool FrontendOptions::canActionEmitModuleSummary(ActionType action) { case ActionType::EmitPCM: case ActionType::DumpPCM: case ActionType::ScanDependencies: - case ActionType::ScanClangDependencies: case ActionType::Typecheck: case ActionType::MergeModules: case ActionType::EmitModuleOnly: @@ -471,7 +462,6 @@ bool FrontendOptions::canActionEmitObjCHeader(ActionType action) { case ActionType::EmitPCM: case ActionType::DumpPCM: case ActionType::ScanDependencies: - case ActionType::ScanClangDependencies: case ActionType::PrintVersion: case ActionType::PrintFeature: return false; @@ -511,7 +501,6 @@ bool FrontendOptions::canActionEmitLoadedModuleTrace(ActionType action) { case ActionType::EmitPCM: case ActionType::DumpPCM: case ActionType::ScanDependencies: - case ActionType::ScanClangDependencies: case ActionType::PrintVersion: case ActionType::PrintFeature: return false; @@ -557,7 +546,6 @@ bool FrontendOptions::canActionEmitModule(ActionType action) { case ActionType::EmitPCM: case ActionType::DumpPCM: case ActionType::ScanDependencies: - case ActionType::ScanClangDependencies: case ActionType::PrintVersion: case ActionType::PrintFeature: return false; @@ -604,7 +592,6 @@ bool FrontendOptions::canActionEmitInterface(ActionType action) { case ActionType::EmitPCM: case ActionType::DumpPCM: case ActionType::ScanDependencies: - case ActionType::ScanClangDependencies: case ActionType::PrintFeature: return false; case ActionType::Typecheck: @@ -652,7 +639,6 @@ bool FrontendOptions::doesActionProduceOutput(ActionType action) { case ActionType::EmitPCM: case ActionType::DumpPCM: case ActionType::ScanDependencies: - case ActionType::ScanClangDependencies: case ActionType::PrintFeature: return true; @@ -700,7 +686,6 @@ bool FrontendOptions::doesActionProduceTextualOutput(ActionType action) { case ActionType::DumpTypeInfo: case ActionType::DumpPCM: case ActionType::ScanDependencies: - case ActionType::ScanClangDependencies: case ActionType::PrintVersion: case ActionType::PrintFeature: return true; @@ -728,7 +713,6 @@ bool FrontendOptions::doesActionGenerateSIL(ActionType action) { case ActionType::EmitPCM: case ActionType::DumpPCM: case ActionType::ScanDependencies: - case ActionType::ScanClangDependencies: case ActionType::PrintVersion: case ActionType::PrintFeature: return false; @@ -777,7 +761,6 @@ bool FrontendOptions::doesActionGenerateIR(ActionType action) { case ActionType::EmitPCM: case ActionType::DumpPCM: case ActionType::ScanDependencies: - case ActionType::ScanClangDependencies: case ActionType::PrintVersion: case ActionType::PrintFeature: return false; diff --git a/lib/FrontendTool/CMakeLists.txt b/lib/FrontendTool/CMakeLists.txt index 67e725145de..46559f1bd91 100644 --- a/lib/FrontendTool/CMakeLists.txt +++ b/lib/FrontendTool/CMakeLists.txt @@ -4,7 +4,6 @@ add_swift_host_library(swiftFrontendTool STATIC ImportedModules.cpp LoadedModuleTrace.cpp MakeStyleDependencies.cpp - ScanDependencies.cpp TBD.cpp) add_dependencies(swiftFrontendTool swift-syntax-generated-headers @@ -15,6 +14,7 @@ target_link_libraries(swiftFrontendTool INTERFACE target_link_libraries(swiftFrontendTool PRIVATE swiftClangImporter swiftDemangling + swiftDependencyScan swiftFrontend swiftIDE swiftImmediate diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 5fabb28057f..3ca50a0fc5a 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -21,8 +21,8 @@ //===----------------------------------------------------------------------===// #include "swift/FrontendTool/FrontendTool.h" +#include "swift/DependencyScan/ScanDependencies.h" #include "Dependencies.h" -#include "ScanDependencies.h" #include "TBD.h" #include "swift/Subsystems.h" #include "swift/AST/DiagnosticsFrontend.h" @@ -1134,10 +1134,17 @@ withSemanticAnalysis(CompilerInstance &Instance, FrontendObserver *observer, static bool performScanDependencies(CompilerInstance &Instance) { auto batchScanInput = Instance.getASTContext().SearchPathOpts.BatchScanInputFilePath; + ModuleDependenciesCache SingleUseCache; if (batchScanInput.empty()) { - return scanDependencies(Instance); + if (Instance.getInvocation().getFrontendOptions().ImportPrescan) + return dependencies::prescanDependencies(Instance); + else + return dependencies::scanDependencies(Instance); } else { - return batchScanModuleDependencies(Instance, batchScanInput); + if (Instance.getInvocation().getFrontendOptions().ImportPrescan) + return dependencies::batchPrescanDependencies(Instance, batchScanInput); + else + return dependencies::batchScanDependencies(Instance, batchScanInput); } } @@ -1224,8 +1231,6 @@ static bool performAction(CompilerInstance &Instance, // MARK: Dependency Scanning Actions case FrontendOptions::ActionType::ScanDependencies: return performScanDependencies(Instance); - case FrontendOptions::ActionType::ScanClangDependencies: - return scanClangDependencies(Instance); // MARK: General Compilation Actions case FrontendOptions::ActionType::Parse: diff --git a/lib/FrontendTool/ScanDependencies.cpp b/lib/FrontendTool/ScanDependencies.cpp deleted file mode 100644 index 4c544376247..00000000000 --- a/lib/FrontendTool/ScanDependencies.cpp +++ /dev/null @@ -1,969 +0,0 @@ -//===--- ScanDependencies.cpp -- Scans the dependencies of a module -------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#include "ScanDependencies.h" -#include "swift/AST/ASTContext.h" -#include "swift/AST/Decl.h" -#include "swift/AST/DiagnosticEngine.h" -#include "swift/AST/DiagnosticsFrontend.h" -#include "swift/AST/Module.h" -#include "swift/AST/ModuleDependencies.h" -#include "swift/AST/ModuleLoader.h" -#include "swift/AST/SourceFile.h" -#include "swift/ClangImporter/ClangImporter.h" -#include "swift/Basic/Defer.h" -#include "swift/Basic/LLVM.h" -#include "swift/Basic/STLExtras.h" -#include "swift/Frontend/Frontend.h" -#include "swift/Frontend/FrontendOptions.h" -#include "swift/Frontend/ModuleInterfaceLoader.h" -#include "swift/Strings.h" -#include "clang/Basic/Module.h" -#include "llvm/ADT/SetVector.h" -#include "llvm/ADT/StringMap.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/StringSet.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/StringSaver.h" -#include "llvm/Support/YAMLTraits.h" -#include "llvm/Support/YAMLParser.h" -#include - -using namespace swift; -using namespace llvm::yaml; - -namespace { -struct BatchScanInput { - StringRef moduleName; - StringRef arguments; - StringRef outputPath; - bool isSwift; -}; - -static std::string getScalaNodeText(Node *N) { - SmallString<32> Buffer; - return cast(N)->getValue(Buffer).str(); -} - -/// Parse an entry like this, where the "platforms" key-value pair is optional: -/// { -/// "swiftModuleName": "Foo", -/// "arguments": "-target 10.15", -/// "output": "../Foo.json" -/// }, -static bool parseBatchInputEntries(ASTContext &Ctx, llvm::StringSaver &saver, - Node *Node, std::vector &result) { - auto *SN = cast(Node); - if (!SN) - return true; - for (auto It = SN->begin(); It != SN->end(); ++It) { - auto *MN = cast(&*It); - BatchScanInput entry; - Optional> Platforms; - for (auto &Pair: *MN) { - auto Key = getScalaNodeText(Pair.getKey()); - auto* Value = Pair.getValue(); - if (Key == "clangModuleName") { - entry.moduleName = saver.save(getScalaNodeText(Value)); - entry.isSwift = false; - } else if (Key == "swiftModuleName") { - entry.moduleName = saver.save(getScalaNodeText(Value)); - entry.isSwift = true; - } else if (Key == "arguments") { - entry.arguments = saver.save(getScalaNodeText(Value)); - } else if (Key == "output") { - entry.outputPath = saver.save(getScalaNodeText(Value)); - } else { - // Future proof. - continue; - } - } - if (entry.moduleName.empty()) - return true; - if (entry.outputPath.empty()) - return true; - result.emplace_back(std::move(entry)); - } - return false; -} - -static Optional> -parseBatchScanInputFile(ASTContext &ctx, StringRef batchInputPath, - llvm::StringSaver &saver) { - assert(!batchInputPath.empty()); - namespace yaml = llvm::yaml; - std::vector result; - - // Load the input file. - llvm::ErrorOr> FileBufOrErr = - llvm::MemoryBuffer::getFile(batchInputPath); - if (!FileBufOrErr) { - ctx.Diags.diagnose(SourceLoc(), diag::batch_scan_input_file_missing, - batchInputPath); - return None; - } - StringRef Buffer = FileBufOrErr->get()->getBuffer(); - - // Use a new source manager instead of the one from ASTContext because we - // don't want the Json file to be persistent. - SourceManager SM; - yaml::Stream Stream(llvm::MemoryBufferRef(Buffer, batchInputPath), - SM.getLLVMSourceMgr()); - for (auto DI = Stream.begin(); DI != Stream.end(); ++ DI) { - assert(DI != Stream.end() && "Failed to read a document"); - yaml::Node *N = DI->getRoot(); - assert(N && "Failed to find a root"); - if (parseBatchInputEntries(ctx, saver, N, result)) { - ctx.Diags.diagnose(SourceLoc(), diag::batch_scan_input_file_corrupted, - batchInputPath); - return None; - } - } - return result; -} -} - -/// Find all of the imported Clang modules starting with the given module name. -static void findAllImportedClangModules(ASTContext &ctx, StringRef moduleName, - ModuleDependenciesCache &cache, - std::vector &allModules, - llvm::StringSet<> &knownModules) { - if (!knownModules.insert(moduleName).second) - return; - allModules.push_back(moduleName.str()); - - auto dependencies = cache.findDependencies( - moduleName, ModuleDependenciesKind::Clang); - if (!dependencies) - return; - - for (const auto &dep : dependencies->getModuleDependencies()) { - findAllImportedClangModules(ctx, dep, cache, allModules, knownModules); - } -} - -/// Resolve the direct dependencies of the given module. -static std::vector resolveDirectDependencies( - CompilerInstance &instance, ModuleDependencyID module, - ModuleDependenciesCache &cache, - InterfaceSubContextDelegate &ASTDelegate) { - auto &ctx = instance.getASTContext(); - auto knownDependencies = *cache.findDependencies(module.first, module.second); - auto isSwift = knownDependencies.isSwiftTextualModule(); - - // Find the dependencies of every module this module directly depends on. - std::set result; - for (auto dependsOn : knownDependencies.getModuleDependencies()) { - // Figure out what kind of module we need. - bool onlyClangModule = !isSwift || module.first == dependsOn; - - // Retrieve the dependencies for this module. - if (auto found = ctx.getModuleDependencies( - dependsOn, onlyClangModule, cache, ASTDelegate)) { - result.insert({dependsOn, found->getKind()}); - } - } - - if (isSwift) { - // A record of all of the Clang modules referenced from this Swift module. - std::vector allClangModules; - llvm::StringSet<> knownModules; - - // If the Swift module has a bridging header, add those dependencies. - if (knownDependencies.getBridgingHeader()) { - auto clangImporter = - static_cast(ctx.getClangModuleLoader()); - if (!clangImporter->addBridgingHeaderDependencies(module.first, cache)) { - // Grab the updated module dependencies. - // FIXME: This is such a hack. - knownDependencies = *cache.findDependencies(module.first, module.second); - - // Add the Clang modules referenced from the bridging header to the - // set of Clang modules we know about. - auto swiftDeps = knownDependencies.getAsSwiftTextualModule(); - for (const auto &clangDep : swiftDeps->bridgingModuleDependencies) { - findAllImportedClangModules(ctx, clangDep, cache, allClangModules, - knownModules); - } - } - } - - // Find all of the Clang modules this Swift module depends on. - for (const auto &dep : result) { - if (dep.second != ModuleDependenciesKind::Clang) - continue; - - findAllImportedClangModules(ctx, dep.first, cache, allClangModules, - knownModules); - } - - // Look for overlays for each of the Clang modules. The Swift module - // directly depends on these. - for (const auto &clangDep : allClangModules) { - if (auto found = ctx.getModuleDependencies( - clangDep, /*onlyClangModule=*/false, cache, ASTDelegate)) { - // ASTContext::getModuleDependencies returns dependencies for a module with a given name. - // This Clang module may have the same name as the Swift module we are resolving, so we - // need to make sure we don't add a dependency from a Swift module to itself. - if ((found->getKind() == ModuleDependenciesKind::SwiftTextual || - found->getKind() == ModuleDependenciesKind::SwiftBinary || - found->getKind() == ModuleDependenciesKind::SwiftPlaceholder) && - clangDep != module.first) { - result.insert({clangDep, found->getKind()}); - } - } - } - } - return std::vector(result.begin(), result.end()); -} - -static void discoverCrosssImportOverlayDependencies( - CompilerInstance &instance, StringRef mainModuleName, - ArrayRef allDependencies, - ModuleDependenciesCache &cache, InterfaceSubContextDelegate &ASTDelegate, - llvm::function_ref action) { - // Modules explicitly imported. Only these can be secondary module. - llvm::SetVector newOverlays; - for (auto dep: allDependencies) { - auto moduleName = dep.first; - auto dependencies = *cache.findDependencies(moduleName, dep.second); - // Collect a map from secondary module name to cross-import overlay names. - auto overlayMap = dependencies.collectCrossImportOverlayNames( - instance.getASTContext(), moduleName); - if (overlayMap.empty()) - continue; - std::for_each(allDependencies.begin(), allDependencies.end(), - [&](ModuleDependencyID Id) { - // check if any explicitly imported modules can serve as a secondary - // module, and add the overlay names to the dependencies list. - for (auto overlayName: overlayMap[Id.first]) { - if (std::find_if(allDependencies.begin(), allDependencies.end(), - [&](ModuleDependencyID Id) { return Id.first == overlayName.str(); }) - == allDependencies.end()) { - newOverlays.insert(overlayName); - } - } - }); - } - // No new cross-import overlays are found, return. - if (newOverlays.empty()) - return; - // Construct a dummy main to resolve the newly discovered cross import overlays. - StringRef dummyMainName = "DummyMainModuleForResolvingCrossImportOverlays"; - auto dummyMainDependencies = ModuleDependencies::forMainSwiftModule({}); - - // Update main module's dependencies to include these new overlays. - auto mainDep = *cache.findDependencies(mainModuleName, - ModuleDependenciesKind::SwiftTextual); - std::for_each(newOverlays.begin(), newOverlays.end(), [&](Identifier modName) { - dummyMainDependencies.addModuleDependency(modName.str()); - mainDep.addModuleDependency(modName.str()); - }); - cache.updateDependencies({mainModuleName.str(), - ModuleDependenciesKind::SwiftTextual}, mainDep); - - // Record the dummy main module's direct dependencies. The dummy main module - // only directly depend on these newly discovered overlay modules. - cache.recordDependencies(dummyMainName, dummyMainDependencies); - llvm::SetVector, - std::set> allModules; - - // Seed the all module list from the dummpy main module. - allModules.insert({dummyMainName.str(), dummyMainDependencies.getKind()}); - - // Explore the dependencies of every module. - for (unsigned currentModuleIdx = 0; - currentModuleIdx < allModules.size(); - ++currentModuleIdx) { - auto module = allModules[currentModuleIdx]; - auto discoveredModules = resolveDirectDependencies(instance, module, - cache, ASTDelegate); - allModules.insert(discoveredModules.begin(), discoveredModules.end()); - } - // Report any discovered modules to the clients, which include all overlays - // and their dependencies. - std::for_each(/* +1 to exclude dummy main*/allModules.begin() + 1, - allModules.end(), action); -} - -/// Write a single JSON field. -namespace { - template - void writeJSONSingleField(llvm::raw_ostream &out, - StringRef fieldName, - const T &value, - unsigned indentLevel, - bool trailingComma); - - /// Write a string value as JSON. - void writeJSONValue(llvm::raw_ostream &out, - StringRef value, - unsigned indentLevel) { - out << "\""; - out << value; - out << "\""; - } - - /// Write a boolean value as JSON. - void writeJSONValue(llvm::raw_ostream &out, - bool value, - unsigned indentLevel) { - out.write_escaped(value ? "true" : "false"); - } - - /// Write a module identifier. - void writeJSONValue(llvm::raw_ostream &out, - const ModuleDependencyID &module, - unsigned indentLevel) { - out << "{\n"; - std::string moduleKind; - if (module.second == ModuleDependenciesKind::SwiftTextual) - moduleKind = "swift"; - else if (module.second == ModuleDependenciesKind::SwiftBinary) - // FIXME: rename to be consistent in the clients (swift-driver) - moduleKind = "swiftPrebuiltExternal"; - else if (module.second == ModuleDependenciesKind::SwiftPlaceholder) - moduleKind = "swiftPlaceholder"; - else - moduleKind = "clang"; - - writeJSONSingleField( - out, - moduleKind, - module.first, - indentLevel + 1, - /*trailingComma=*/false); - - out.indent(indentLevel * 2); - out << "}"; - } - - /// Write a JSON array. - template - void writeJSONValue(llvm::raw_ostream &out, - ArrayRef values, - unsigned indentLevel) { - out << "[\n"; - - for (const auto &value: values) { - - out.indent((indentLevel + 1) * 2); - - writeJSONValue(out, value, indentLevel + 1); - - if (&value != &values.back()) { - out << ","; - } - out << "\n"; - } - - out.indent(indentLevel * 2); - out << "]"; - } - - /// Write a JSON array. - template - void writeJSONValue(llvm::raw_ostream &out, - const std::vector &values, - unsigned indentLevel) { - writeJSONValue(out, llvm::makeArrayRef(values), indentLevel); - } - - /// Write a single JSON field. - template - void writeJSONSingleField(llvm::raw_ostream &out, - StringRef fieldName, - const T &value, - unsigned indentLevel, - bool trailingComma) { - out.indent(indentLevel * 2); - writeJSONValue(out, fieldName, indentLevel); - out << ": "; - writeJSONValue(out, value, indentLevel); - if (trailingComma) - out << ","; - out << "\n"; - } -} - -static void writePrescanJSON(llvm::raw_ostream &out, - const ModuleDependencies &mainModuleDependencies) { - // Write out a JSON containing all main module imports. - out << "{\n"; - SWIFT_DEFER { - out << "}\n"; - }; - - writeJSONSingleField(out, "imports", mainModuleDependencies.getModuleDependencies(), 0, false); -} - -static void writeJSON(llvm::raw_ostream &out, - CompilerInstance &instance, - ModuleDependenciesCache &cache, - InterfaceSubContextDelegate &ASTDelegate, - ArrayRef allModules) { - // Write out a JSON description of all of the dependencies. - out << "{\n"; - SWIFT_DEFER { - out << "}\n"; - }; - - // Name of the main module. - writeJSONSingleField(out, "mainModuleName", allModules.front().first, - /*indentLevel=*/1, /*trailingComma=*/true); - - // Write out all of the modules. - out << " \"modules\": [\n"; - SWIFT_DEFER { - out << " ]\n"; - }; - for (const auto &module : allModules) { - auto directDependencies = resolveDirectDependencies( - instance, ModuleDependencyID(module.first, module.second), cache, - ASTDelegate); - - // Grab the completed module dependencies. - auto moduleDeps = *cache.findDependencies(module.first, module.second); - - // The module we are describing. - out.indent(2 * 2); - writeJSONValue(out, module, 2); - out << ",\n"; - - out.indent(2 * 2); - out << "{\n"; - - auto swiftPlaceholderDeps = moduleDeps.getAsPlaceholderDependencyModule(); - auto swiftTextualDeps = moduleDeps.getAsSwiftTextualModule(); - auto swiftBinaryDeps = moduleDeps.getAsSwiftBinaryModule(); - auto clangDeps = moduleDeps.getAsClangModule(); - - // Module path. - const char *modulePathSuffix = - moduleDeps.isSwiftModule() ? ".swiftmodule" : ".pcm"; - - std::string modulePath; - if (swiftPlaceholderDeps) - modulePath = swiftPlaceholderDeps->compiledModulePath; - else if (swiftBinaryDeps) - modulePath = swiftBinaryDeps->compiledModulePath; - else - modulePath = module.first + modulePathSuffix; - - writeJSONSingleField(out, "modulePath", modulePath, /*indentLevel=*/3, - /*trailingComma=*/true); - - // Source files. - if (swiftTextualDeps) { - writeJSONSingleField(out, "sourceFiles", swiftTextualDeps->sourceFiles, 3, - /*trailingComma=*/true); - } else if (clangDeps) { - writeJSONSingleField(out, "sourceFiles", clangDeps->fileDependencies, 3, - /*trailingComma=*/true); - } - - // Direct dependencies. - if (swiftTextualDeps || swiftBinaryDeps || clangDeps) - writeJSONSingleField(out, "directDependencies", directDependencies, 3, - /*trailingComma=*/true); - - // Swift and Clang-specific details. - out.indent(3 * 2); - out << "\"details\": {\n"; - out.indent(4 * 2); - if (swiftTextualDeps) { - out << "\"swift\": {\n"; - - /// Swift interface file, if there is one. The main module, for example, will not have - /// an interface file. - if (swiftTextualDeps->swiftInterfaceFile) { - writeJSONSingleField(out, "moduleInterfacePath", - *swiftTextualDeps->swiftInterfaceFile, 5, - /*trailingComma=*/true); - writeJSONSingleField(out, "contextHash", - swiftTextualDeps->contextHash, 5, - /*trailingComma=*/true); - out.indent(5 * 2); - out << "\"commandLine\": [\n"; - for (auto &arg :swiftTextualDeps->buildCommandLine) { - - out.indent(6 * 2); - out << "\"" << arg << "\""; - if (&arg != &swiftTextualDeps->buildCommandLine.back()) - out << ","; - out << "\n"; - } - out.indent(5 * 2); - out << "],\n"; - out.indent(5 * 2); - out << "\"compiledModuleCandidates\": [\n"; - for (auto &candidate: swiftTextualDeps->compiledModuleCandidates) { - out.indent(6 * 2); - out << "\"" << candidate << "\""; - if (&candidate != &swiftTextualDeps->compiledModuleCandidates.back()) - out << ","; - out << "\n"; - } - out.indent(5 * 2); - out << "],\n"; - } - writeJSONSingleField( - out, "isFramework", - swiftTextualDeps->isFramework, 5, - /*trailingComma=*/!swiftTextualDeps->extraPCMArgs.empty() || - swiftTextualDeps->bridgingHeaderFile.hasValue()); - if (!swiftTextualDeps->extraPCMArgs.empty()) { - out.indent(5 * 2); - out << "\"extraPcmArgs\": [\n"; - for (auto &arg : swiftTextualDeps->extraPCMArgs) { - out.indent(6 * 2); - out << "\"" << arg << "\""; - if (&arg != &swiftTextualDeps->extraPCMArgs.back()) - out << ","; - out << "\n"; - } - out.indent(5 * 2); - out << (swiftTextualDeps->bridgingHeaderFile.hasValue() ? "],\n" : "]\n"); - } - /// Bridging header and its source file dependencies, if any. - if (swiftTextualDeps->bridgingHeaderFile) { - out.indent(5 * 2); - out << "\"bridgingHeader\": {\n"; - writeJSONSingleField(out, "path", *swiftTextualDeps->bridgingHeaderFile, 6, - /*trailingComma=*/true); - writeJSONSingleField(out, "sourceFiles", swiftTextualDeps->bridgingSourceFiles, - 6, - /*trailingComma=*/true); - writeJSONSingleField(out, "moduleDependencies", - swiftTextualDeps->bridgingModuleDependencies, 6, - /*trailingComma=*/false); - out.indent(5 * 2); - out << "}\n"; - } - } else if (swiftPlaceholderDeps) { - out << "\"swiftPlaceholder\": {\n"; - - // Module doc file - if (swiftPlaceholderDeps->moduleDocPath != "") - writeJSONSingleField(out, "moduleDocPath", - swiftPlaceholderDeps->moduleDocPath, - /*indentLevel=*/5, - /*trailingComma=*/true); - - // Module Source Info file - if (swiftPlaceholderDeps->moduleDocPath != "") - writeJSONSingleField(out, "moduleSourceInfoPath", - swiftPlaceholderDeps->sourceInfoPath, - /*indentLevel=*/5, - /*trailingComma=*/false); - } else if (swiftBinaryDeps) { - out << "\"swiftPrebuiltExternal\": {\n"; - assert(swiftBinaryDeps->compiledModulePath != "" && - "Expected .swiftmodule for a Binary Swift Module Dependency."); - writeJSONSingleField(out, "compiledModulePath", - swiftBinaryDeps->compiledModulePath, - /*indentLevel=*/5, - /*trailingComma=*/true); - // Module doc file - if (swiftBinaryDeps->moduleDocPath != "") - writeJSONSingleField(out, "moduleDocPath", - swiftBinaryDeps->moduleDocPath, - /*indentLevel=*/5, - /*trailingComma=*/true); - - // Module Source Info file - if (swiftBinaryDeps->moduleDocPath != "") - writeJSONSingleField(out, "moduleSourceInfoPath", - swiftBinaryDeps->sourceInfoPath, - /*indentLevel=*/5, - /*trailingComma=*/false); - } else { - out << "\"clang\": {\n"; - - // Module map file. - writeJSONSingleField(out, "moduleMapPath", clangDeps->moduleMapFile, 5, - /*trailingComma=*/true); - - // Context hash. - writeJSONSingleField(out, "contextHash", clangDeps->contextHash, 5, - /*trailingComma=*/true); - - // Command line. - writeJSONSingleField(out, "commandLine", clangDeps->nonPathCommandLine, 5, - /*trailingComma=*/false); - } - - out.indent(4 * 2); - out << "}\n"; - out.indent(3 * 2); - out << "}\n"; - out.indent(2 * 2); - out << "}"; - - if (&module != &allModules.back()) - out << ","; - out << "\n"; - } -} - -static bool diagnoseCycle(CompilerInstance &instance, - ModuleDependenciesCache &cache, - ModuleDependencyID mainId, - InterfaceSubContextDelegate &astDelegate) { - llvm::SetVector, - std::set> openSet; - llvm::SetVector, - std::set> closeSet; - // Start from the main module. - openSet.insert(mainId); - while(!openSet.empty()) { - auto &lastOpen = openSet.back(); - auto beforeSize = openSet.size(); - for (auto dep: resolveDirectDependencies(instance, lastOpen, cache, - astDelegate)) { - if (closeSet.count(dep)) - continue; - if (openSet.insert(dep)) { - break; - } else { - // Find a cycle, diagnose. - auto startIt = std::find(openSet.begin(), openSet.end(), dep); - assert(startIt != openSet.end()); - llvm::SmallString<64> buffer; - for (auto it = startIt; it != openSet.end(); ++ it) { - buffer.append(it->first); - buffer.append((it->second == ModuleDependenciesKind::SwiftTextual || - it->second == ModuleDependenciesKind::SwiftBinary)? - ".swiftmodule": ".pcm"); - buffer.append(" -> "); - } - buffer.append(startIt->first); - buffer.append((startIt->second == ModuleDependenciesKind::SwiftTextual || - startIt->second == ModuleDependenciesKind::SwiftBinary)? - ".swiftmodule": ".pcm"); - instance.getASTContext().Diags.diagnose(SourceLoc(), - diag::scanner_find_cycle, - buffer.str()); - return true; - } - } - // No new node added. We can close this node - if (openSet.size() == beforeSize) { - closeSet.insert(openSet.back()); - openSet.pop_back(); - } else { - assert(openSet.size() == beforeSize + 1); - } - } - assert(openSet.empty()); - return false; -} - -static bool scanModuleDependencies(CompilerInstance &instance, - StringRef moduleName, - bool isClang, - StringRef outputPath) { - ASTContext &ctx = instance.getASTContext(); - auto &FEOpts = instance.getInvocation().getFrontendOptions(); - ModuleInterfaceLoaderOptions LoaderOpts(FEOpts); - auto ModuleCachePath = getModuleCachePathFromClang(ctx - .getClangModuleLoader()->getClangInstance()); - - llvm::SetVector, - std::set> allModules; - // Retrieve the instance's module dependency cache. - ModuleDependenciesCache *cache = instance.getModuleDependencyCache(); - assert(cache && - "Dependency Scanner expected a ModuleDependenciesCache on a compiler instance."); - InterfaceSubContextDelegateImpl ASTDelegate(ctx.SourceMgr, ctx.Diags, - ctx.SearchPathOpts, ctx.LangOpts, - ctx.ClangImporterOpts, - LoaderOpts, - /*buildModuleCacheDirIfAbsent*/false, - ModuleCachePath, - FEOpts.PrebuiltModuleCachePath, - FEOpts.SerializeModuleInterfaceDependencyHashes, - FEOpts.shouldTrackSystemDependencies()); - std::error_code EC; - llvm::raw_fd_ostream out(outputPath, EC, llvm::sys::fs::F_None); - Optional rootDeps; - if (isClang) { - // Loading the clang module using Clang importer. - // This action will populate the cache with the main module's dependencies. - rootDeps = ctx.getModuleDependencies(moduleName, /*IsClang*/true, *cache, - ASTDelegate); - } else { - // Loading the swift module's dependencies. - rootDeps = ctx.getSwiftModuleDependencies(moduleName, *cache, ASTDelegate); - } - if (!rootDeps.hasValue()) { - // We cannot find the clang module, abort. - return true; - } - // Add the main module. - allModules.insert({moduleName.str(), isClang ? ModuleDependenciesKind::Clang: - ModuleDependenciesKind::SwiftTextual}); - - // Output module prescan. - if (FEOpts.ImportPrescan) { - writePrescanJSON(out, rootDeps.getValue()); - return false; - } - - // Explore the dependencies of every module. - for (unsigned currentModuleIdx = 0; - currentModuleIdx < allModules.size(); - ++currentModuleIdx) { - auto module = allModules[currentModuleIdx]; - auto discoveredModules = - resolveDirectDependencies(instance, module, *cache, ASTDelegate); - allModules.insert(discoveredModules.begin(), discoveredModules.end()); - } - // Write out the JSON description. - writeJSON(out, instance, *cache, ASTDelegate, allModules.getArrayRef()); - return false; -} - -bool swift::scanClangDependencies(CompilerInstance &instance) { - return scanModuleDependencies(instance, - instance.getMainModule()->getNameStr(), - /*isClang*/true, - instance.getInvocation().getFrontendOptions() - .InputsAndOutputs.getSingleOutputFilename()); -} - -bool swift::batchScanModuleDependencies(CompilerInstance &instance, - llvm::StringRef batchInputFile) { - const CompilerInvocation &invok = instance.getInvocation(); - - (void)instance.getMainModule(); - llvm::BumpPtrAllocator alloc; - llvm::StringSaver saver(alloc); - auto results = parseBatchScanInputFile(instance.getASTContext(), - batchInputFile, saver); - if (!results.hasValue()) - return true; - auto &diags = instance.getDiags(); - ForwardingDiagnosticConsumer FDC(diags); - // Keep track of all compiler instances we have created. - llvm::StringMap> subInstanceMap; - for (auto &entry: *results) { - CompilerInstance *pInstance = nullptr; - if (entry.arguments.empty()) { - // Use the compiler's instance if no arguments are specified. - pInstance = &instance; - } else if (subInstanceMap.count(entry.arguments)) { - // Use the previously created instance if we've seen the arguments before. - pInstance = subInstanceMap[entry.arguments].get(); - } else { - // Create a new instance by the arguments and save it in the map. - pInstance = subInstanceMap.insert({entry.arguments, - std::make_unique()}).first->getValue().get(); - SmallVector args; - llvm::cl::TokenizeGNUCommandLine(entry.arguments, saver, args); - CompilerInvocation subInvok = invok; - pInstance->addDiagnosticConsumer(&FDC); - if (subInvok.parseArgs(args, diags)) { - instance.getDiags().diagnose(SourceLoc(), diag::scanner_arguments_invalid, - entry.arguments); - return true; - } - if (pInstance->setup(subInvok)) { - instance.getDiags().diagnose(SourceLoc(), diag::scanner_arguments_invalid, - entry.arguments); - return true; - } - } - assert(pInstance); - // Scan using the chosen compiler instance. - if (scanModuleDependencies(*pInstance, entry.moduleName, !entry.isSwift, - entry.outputPath)) { - return true; - } - } - return false; -} - -bool swift::scanDependencies(CompilerInstance &instance) { - ASTContext &Context = instance.getASTContext(); - ModuleDecl *mainModule = instance.getMainModule(); - const CompilerInvocation &invocation = instance.getInvocation(); - const FrontendOptions &opts = invocation.getFrontendOptions(); - - std::string path = opts.InputsAndOutputs.getSingleOutputFilename(); - std::error_code EC; - llvm::raw_fd_ostream out(path, EC, llvm::sys::fs::F_None); - - if (out.has_error() || EC) { - Context.Diags.diagnose(SourceLoc(), diag::error_opening_output, path, - EC.message()); - out.clear_error(); - return true; - } - - // Main module file name. - auto newExt = file_types::getExtension(file_types::TY_SwiftModuleFile); - llvm::SmallString<32> mainModulePath = mainModule->getName().str(); - llvm::sys::path::replace_extension(mainModulePath, newExt); - - std::string apinotesVer = (llvm::Twine("-fapinotes-swift-version=") - + instance.getASTContext().LangOpts.EffectiveLanguageVersion - .asAPINotesVersionString()).str(); - // Compute the dependencies of the main module. - auto mainDependencies = - ModuleDependencies::forMainSwiftModule({ - // ExtraPCMArgs - "-Xcc", "-target", "-Xcc", instance.getASTContext().LangOpts.Target.str(), - "-Xcc", apinotesVer - }); - - // Compute Implicit dependencies of the main module - { - llvm::StringSet<> alreadyAddedModules; - for (auto fileUnit : mainModule->getFiles()) { - auto sf = dyn_cast(fileUnit); - if (!sf) - continue; - - mainDependencies.addModuleDependencies(*sf, alreadyAddedModules); - } - - const auto &importInfo = mainModule->getImplicitImportInfo(); - - // Swift standard library. - switch (importInfo.StdlibKind) { - case ImplicitStdlibKind::None: - case ImplicitStdlibKind::Builtin: - break; - - case ImplicitStdlibKind::Stdlib: - mainDependencies.addModuleDependency("Swift", &alreadyAddedModules); - break; - } - - // Add any implicit module names. - for (const auto &import : importInfo.AdditionalUnloadedImports) { - mainDependencies.addModuleDependency(import.module.getModulePath(), &alreadyAddedModules); - } - - // Already-loaded, implicitly imported module names. - for (const auto &import : importInfo.AdditionalImports) { - mainDependencies.addModuleDependency(import.module.importedModule->getNameStr(), &alreadyAddedModules); - } - - // Add the bridging header. - if (!importInfo.BridgingHeaderPath.empty()) { - mainDependencies.addBridgingHeader(importInfo.BridgingHeaderPath); - } - - // If we are to import the underlying Clang module of the same name, - // add a dependency with the same name to trigger the search. - if (importInfo.ShouldImportUnderlyingModule) { - mainDependencies.addModuleDependency(mainModule->getName().str(), - &alreadyAddedModules); - } - } - - // If import-prescan is specified, discover and serialize main module dependencies only and exit. - if (opts.ImportPrescan) { - writePrescanJSON(out, mainDependencies); - // This process succeeds regardless of whether any errors occurred. - // FIXME: We shouldn't need this, but it's masking bugs in our scanning - // logic where we don't create a fresh context when scanning Swift interfaces - // that includes their own command-line flags. - Context.Diags.resetHadAnyError(); - return false; - } - - // Add the main module. - StringRef mainModuleName = mainModule->getNameStr(); - llvm::SetVector, - std::set> allModules; - - allModules.insert({mainModuleName.str(), mainDependencies.getKind()}); - - // Retrieve the instance's module dependency cache. - ModuleDependenciesCache *cache = instance.getModuleDependencyCache(); - assert(cache && - "Dependency Scanner expected a ModuleDependenciesCache on a compiler instance."); - cache->recordDependencies(mainModuleName, std::move(mainDependencies)); - - auto &ctx = instance.getASTContext(); - auto ModuleCachePath = getModuleCachePathFromClang(ctx - .getClangModuleLoader()->getClangInstance()); - auto &FEOpts = instance.getInvocation().getFrontendOptions(); - ModuleInterfaceLoaderOptions LoaderOpts(FEOpts); - InterfaceSubContextDelegateImpl ASTDelegate(ctx.SourceMgr, ctx.Diags, - ctx.SearchPathOpts, ctx.LangOpts, - ctx.ClangImporterOpts, - LoaderOpts, - /*buildModuleCacheDirIfAbsent*/false, - ModuleCachePath, - FEOpts.PrebuiltModuleCachePath, - FEOpts.SerializeModuleInterfaceDependencyHashes, - FEOpts.shouldTrackSystemDependencies()); - - // Explore the dependencies of every module. - for (unsigned currentModuleIdx = 0; - currentModuleIdx < allModules.size(); - ++currentModuleIdx) { - auto module = allModules[currentModuleIdx]; - auto discoveredModules = - resolveDirectDependencies(instance, module, *cache, ASTDelegate); - allModules.insert(discoveredModules.begin(), discoveredModules.end()); - } - - // We have all explicit imports now, resolve cross import overlays. - discoverCrosssImportOverlayDependencies(instance, mainModuleName, - /*All transitive dependencies*/allModules.getArrayRef().slice(1), *cache, - ASTDelegate, [&](ModuleDependencyID id) { - allModules.insert(id); - }); - - // Dignose cycle in dependency graph. - if (diagnoseCycle(instance, *cache, /*MainModule*/allModules.front(), ASTDelegate)) - return true; - - // Write out the JSON description. - writeJSON(out, instance, *cache, ASTDelegate, allModules.getArrayRef()); - - // Update the dependency tracker. - if (auto depTracker = instance.getDependencyTracker()) { - for (auto module : allModules) { - auto deps = cache->findDependencies(module.first, module.second); - if (!deps) - continue; - - if (auto swiftDeps = deps->getAsSwiftTextualModule()) { - if (auto swiftInterfaceFile = swiftDeps->swiftInterfaceFile) - depTracker->addDependency(*swiftInterfaceFile, /*IsSystem=*/false); - for (const auto &sourceFile : swiftDeps->sourceFiles) - depTracker->addDependency(sourceFile, /*IsSystem=*/false); - for (const auto &bridgingSourceFile : swiftDeps->bridgingSourceFiles) - depTracker->addDependency(bridgingSourceFile, /*IsSystem=*/false); - } else if (auto clangDeps = deps->getAsClangModule()) { - if (!clangDeps->moduleMapFile.empty()) - depTracker->addDependency(clangDeps->moduleMapFile, /*IsSystem=*/false); - for (const auto &sourceFile : clangDeps->fileDependencies) - depTracker->addDependency(sourceFile, /*IsSystem=*/false); - } - } - } - - // This process succeeds regardless of whether any errors occurred. - // FIXME: We shouldn't need this, but it's masking bugs in our scanning - // logic where we don't create a fresh context when scanning Swift interfaces - // that includes their own command-line flags. - Context.Diags.resetHadAnyError(); - return false; -} diff --git a/lib/FrontendTool/ScanDependencies.h b/lib/FrontendTool/ScanDependencies.h deleted file mode 100644 index fab7d26bbb1..00000000000 --- a/lib/FrontendTool/ScanDependencies.h +++ /dev/null @@ -1,36 +0,0 @@ -//===--- ScanDependencies.h -- Scans the dependencies of a module ------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_FRONTENDTOOL_SCANDEPENDENCIES_H -#define SWIFT_FRONTENDTOOL_SCANDEPENDENCIES_H - -#include "llvm/ADT/StringRef.h" - -namespace swift { - -class CompilerInvocation; -class CompilerInstance; - -/// Batch scan the dependencies for modules specified in \c batchInputFile. -bool batchScanModuleDependencies(CompilerInstance &instance, - llvm::StringRef batchInputFile); - -/// Scans the dependencies of the main module of \c instance. -bool scanDependencies(CompilerInstance &instance); - -/// Scans the dependencies of the underlying clang module of the main module -/// of \c instance. -bool scanClangDependencies(CompilerInstance &instance); - -} // end namespace swift - -#endif diff --git a/test/ScanDependencies/module_deps_clang_only.swift b/test/ScanDependencies/module_deps_clang_only.swift deleted file mode 100644 index 34ac0c6eeef..00000000000 --- a/test/ScanDependencies/module_deps_clang_only.swift +++ /dev/null @@ -1,21 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: mkdir -p %t/clang-module-cache -// RUN: %target-swift-frontend -scan-clang-dependencies -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 -disable-implicit-swift-modules -module-name C - -// Check the contents of the JSON output -// RUN: %FileCheck %s < %t/deps.json - -// CHECK: "mainModuleName": "C", -// CHECK-NEXT: "modules": [ -// CHECK-NEXT: { -// CHECK-NEXT: "clang": "C" -// CHECK-NEXT: }, -// CHECK-NEXT: { -// CHECK-NEXT: "modulePath": "C.pcm", -// CHECK-NEXT: "sourceFiles": [ - - -// CHECK: "directDependencies": [ -// CHECK-NEXT: { -// CHECK-NEXT: "clang": "B" -// CHECK-NEXT: } diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index ea610709e2f..21b17311979 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -28,6 +28,7 @@ add_swift_tool_subdirectory(swift-llvm-opt) add_swift_tool_subdirectory(swift-api-digester) add_swift_tool_subdirectory(swift-ast-script) add_swift_tool_subdirectory(swift-refactor) +add_swift_tool_subdirectory(libSwiftScan) if(SWIFT_BUILD_SYNTAXPARSERLIB) add_swift_tool_subdirectory(libSwiftSyntaxParser) if(SWIFT_INCLUDE_TESTS) diff --git a/tools/libSwiftScan/CMakeLists.txt b/tools/libSwiftScan/CMakeLists.txt new file mode 100644 index 00000000000..6448e4e3356 --- /dev/null +++ b/tools/libSwiftScan/CMakeLists.txt @@ -0,0 +1,28 @@ +include(SwiftWindowsSupport) +swift_swap_compiler_if_needed("libSwiftScan") + +# Use an 'internal' name, this is primarily intended for SwiftDriver to import. +set(SWIFT_SCAN_LIB_NAME "_InternalSwiftScan") + +set(LLVM_EXPORTED_SYMBOL_FILE + ${CMAKE_CURRENT_SOURCE_DIR}/libSwiftScan.exports) + +add_swift_host_library(libSwiftScan SHARED + libSwiftScan.cpp + c-include-check.c) + +add_dependencies(libSwiftScan + clang + swiftDependencyScan) + +target_link_libraries(libSwiftScan PRIVATE + swiftDependencyScan) + +set_target_properties(libSwiftScan + PROPERTIES + OUTPUT_NAME ${SWIFT_SCAN_LIB_NAME}) + +add_llvm_symbol_exports(libSwiftScan ${LLVM_EXPORTED_SYMBOL_FILE}) + +# Adds -dead_strip option +add_link_opts(libSwiftScan) diff --git a/tools/libSwiftScan/c-include-check.c b/tools/libSwiftScan/c-include-check.c new file mode 100644 index 00000000000..b79179ca7d9 --- /dev/null +++ b/tools/libSwiftScan/c-include-check.c @@ -0,0 +1,5 @@ +// This file serves as a sanity check to ensure that the header is parseable +// from C and that no C++ code has sneaked in. + +#include "swift-c/DependencyScan/DependencyScan.h" +typedef swiftscan_module_details_t _check_module_details_exists; diff --git a/tools/libSwiftScan/libSwiftScan.cpp b/tools/libSwiftScan/libSwiftScan.cpp new file mode 100644 index 00000000000..48ff6537c03 --- /dev/null +++ b/tools/libSwiftScan/libSwiftScan.cpp @@ -0,0 +1,463 @@ +//===------------ DependencyScanImpl.cpp - Swift Compiler -----------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Implementation of the dependency scanning C API +// +//===----------------------------------------------------------------------===// + +#include "swift/Basic/LLVMInitialize.h" +#include "swift/DependencyScan/DependencyScanImpl.h" +#include "swift/DependencyScan/DependencyScanningTool.h" +#include "swift/DependencyScan/StringUtils.h" + +using namespace swift::dependencies; + +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(DependencyScanningTool, swiftscan_scanner_t); + +//=== Private Cleanup Functions -------------------------------------------===// + +/// Free the given string. +void swiftscan_string_dispose(swiftscan_string_ref_t string) { + if (string.data) + free(const_cast(string.data)); +} + +/// Free the given string set. +void swiftscan_string_set_dispose(swiftscan_string_set_t *set) { + for (unsigned SI = 0, SE = set->count; SI < SE; ++SI) + swiftscan_string_dispose(set->strings[SI]); + delete[] set->strings; + delete set; +} + +void swiftscan_dependency_info_details_dispose( + swiftscan_module_details_t details) { + swiftscan_module_details_s *details_impl = details; + switch (details_impl->kind) { + case SWIFTSCAN_DEPENDENCY_INFO_SWIFT_TEXTUAL: + swiftscan_string_dispose( + details_impl->swift_textual_details.module_interface_path); + swiftscan_string_set_dispose( + details_impl->swift_textual_details.compiled_module_candidates); + swiftscan_string_dispose( + details_impl->swift_textual_details.bridging_header_path); + swiftscan_string_set_dispose( + details_impl->swift_textual_details.bridging_source_files); + swiftscan_string_set_dispose( + details_impl->swift_textual_details.bridging_module_dependencies); + swiftscan_string_set_dispose( + details_impl->swift_textual_details.command_line); + swiftscan_string_set_dispose( + details_impl->swift_textual_details.extra_pcm_args); + swiftscan_string_dispose(details_impl->swift_textual_details.context_hash); + break; + case SWIFTSCAN_DEPENDENCY_INFO_SWIFT_BINARY: + swiftscan_string_dispose( + details_impl->swift_binary_details.compiled_module_path); + swiftscan_string_dispose( + details_impl->swift_binary_details.module_doc_path); + swiftscan_string_dispose( + details_impl->swift_binary_details.module_source_info_path); + break; + case SWIFTSCAN_DEPENDENCY_INFO_SWIFT_PLACEHOLDER: + swiftscan_string_dispose( + details_impl->swift_placeholder_details.compiled_module_path); + swiftscan_string_dispose( + details_impl->swift_placeholder_details.module_doc_path); + swiftscan_string_dispose( + details_impl->swift_placeholder_details.module_source_info_path); + break; + case SWIFTSCAN_DEPENDENCY_INFO_CLANG: + swiftscan_string_dispose(details_impl->clang_details.module_map_path); + swiftscan_string_dispose(details_impl->clang_details.context_hash); + swiftscan_string_set_dispose(details_impl->clang_details.command_line); + break; + } + delete details_impl; +} + +void swiftscan_dependency_info_dispose(swiftscan_dependency_info_t info) { + swiftscan_string_dispose(info->module_name); + swiftscan_string_dispose(info->module_path); + swiftscan_string_set_dispose(info->source_files); + swiftscan_string_set_dispose(info->direct_dependencies); + swiftscan_dependency_info_details_dispose(info->details); + delete info; +} + +void swiftscan_dependency_set_dispose(swiftscan_dependency_set_t *set) { + for (size_t i = 0; i < set->count; ++i) { + swiftscan_dependency_info_dispose(set->modules[i]); + } + delete[] set->modules; + delete set; +} + +//=== Scanner Functions ---------------------------------------------------===// + +swiftscan_scanner_t swiftscan_scanner_create(void) { + INITIALIZE_LLVM(); + return wrap(new DependencyScanningTool()); +} + +void swiftscan_scanner_dispose(swiftscan_scanner_t c_scanner) { + delete unwrap(c_scanner); +} + +swiftscan_dependency_graph_t +swiftscan_dependency_graph_create(swiftscan_scanner_t scanner, + swiftscan_scan_invocation_t invocation) { + DependencyScanningTool *ScanningTool = unwrap(scanner); + int argc = invocation->argv->count; + std::vector Compilation; + for (int i = 0; i < argc; ++i) + Compilation.push_back(get_C_string(invocation->argv->strings[i])); + + // Execute the scan and bridge the result + auto ScanResult = ScanningTool->getDependencies(Compilation, {}); + if (ScanResult.getError()) + return nullptr; + auto DependencyGraph = std::move(*ScanResult); + return DependencyGraph; +} + +swiftscan_batch_scan_result_t * +swiftscan_batch_scan_result_create(swiftscan_scanner_t scanner, + swiftscan_batch_scan_input_t *batch_input, + swiftscan_scan_invocation_t invocation) { + DependencyScanningTool *ScanningTool = unwrap(scanner); + int argc = invocation->argv->count; + std::vector Compilation; + for (int i = 0; i < argc; ++i) + Compilation.push_back(get_C_string(invocation->argv->strings[i])); + + std::vector BatchInput; + for (size_t i = 0; i < batch_input->count; ++i) { + swiftscan_batch_scan_entry_s *Entry = batch_input->modules[i]; + BatchInput.push_back({get_C_string(Entry->module_name), + get_C_string(Entry->arguments), + /*outputPath*/ "", Entry->is_swift}); + } + + // Execute the scan and bridge the result + auto BatchScanResult = + ScanningTool->getDependencies(Compilation, BatchInput, {}); + swiftscan_batch_scan_result_t *Result = new swiftscan_batch_scan_result_t; + auto ResultGraphs = new swiftscan_dependency_graph_t[BatchScanResult.size()]; + for (size_t i = 0; i < BatchScanResult.size(); ++i) { + auto &ResultOrErr = BatchScanResult[i]; + if (ResultOrErr.getError()) { + ResultGraphs[i] = nullptr; + continue; + } + + ResultGraphs[i] = ResultOrErr.get(); + } + + Result->results = ResultGraphs; + Result->count = BatchScanResult.size(); + return Result; +} + +swiftscan_import_set_t +swiftscan_import_set_create(swiftscan_scanner_t scanner, + swiftscan_scan_invocation_t invocation) { + DependencyScanningTool *ScanningTool = unwrap(scanner); + int argc = invocation->argv->count; + std::vector Compilation; + for (int i = 0; i < argc; ++i) + Compilation.push_back(get_C_string(invocation->argv->strings[i])); + + // Execute the scan and bridge the result + auto PreScanResult = ScanningTool->getImports(Compilation); + if (PreScanResult.getError()) + return nullptr; + auto ImportSet = std::move(*PreScanResult); + return ImportSet; +} + +//=== Dependency Result Functions -----------------------------------------===// + +swiftscan_string_ref_t swiftscan_dependency_graph_get_main_module_name( + swiftscan_dependency_graph_t result) { + return result->main_module_name; +} + +swiftscan_dependency_set_t *swiftscan_dependency_graph_get_dependencies( + swiftscan_dependency_graph_t result) { + return result->dependencies; +} + +//=== Module Dependency Info query APIs -----------------------------------===// + +swiftscan_string_ref_t +swiftscan_module_info_get_module_name(swiftscan_dependency_info_t info) { + return info->module_name; +} + +swiftscan_string_ref_t +swiftscan_module_info_get_module_path(swiftscan_dependency_info_t info) { + return info->module_path; +} + +swiftscan_string_set_t * +swiftscan_module_info_get_source_files(swiftscan_dependency_info_t info) { + return info->source_files; +} + +swiftscan_string_set_t *swiftscan_module_info_get_direct_dependencies( + swiftscan_dependency_info_t info) { + return info->direct_dependencies; +} + +swiftscan_module_details_t +swiftscan_module_info_get_details(swiftscan_dependency_info_t info) { + return info->details; +} + +//=== Swift Textual Module Details query APIs -----------------------------===// + +swiftscan_dependency_info_kind_t +swiftscan_module_detail_get_kind(swiftscan_module_details_t details) { + return details->kind; +} + +swiftscan_string_ref_t swiftscan_swift_textual_detail_get_module_interface_path( + swiftscan_module_details_t details) { + return details->swift_textual_details.module_interface_path; +} + +swiftscan_string_set_t * +swiftscan_swift_textual_detail_get_compiled_module_candidates( + swiftscan_module_details_t details) { + return details->swift_textual_details.compiled_module_candidates; +} + +swiftscan_string_ref_t swiftscan_swift_textual_detail_get_bridging_header_path( + swiftscan_module_details_t details) { + return details->swift_textual_details.bridging_header_path; +} + +swiftscan_string_set_t * +swiftscan_swift_textual_detail_get_bridging_source_files( + swiftscan_module_details_t details) { + return details->swift_textual_details.bridging_source_files; +} + +swiftscan_string_set_t * +swiftscan_swift_textual_detail_get_bridging_module_dependencies( + swiftscan_module_details_t details) { + return details->swift_textual_details.bridging_module_dependencies; +} + +swiftscan_string_set_t *swiftscan_swift_textual_detail_get_command_line( + swiftscan_module_details_t details) { + return details->swift_textual_details.command_line; +} + +swiftscan_string_set_t *swiftscan_swift_textual_detail_get_extra_pcm_args( + swiftscan_module_details_t details) { + return details->swift_textual_details.extra_pcm_args; +} + +swiftscan_string_ref_t swiftscan_swift_textual_detail_get_context_hash( + swiftscan_module_details_t details) { + return details->swift_textual_details.context_hash; +} + +bool swiftscan_swift_textual_detail_get_is_framework( + swiftscan_module_details_t details) { + return details->swift_textual_details.is_framework; +} + +//=== Swift Binary Module Details query APIs ------------------------------===// + +swiftscan_string_ref_t swiftscan_swift_binary_detail_get_compiled_module_path( + swiftscan_module_details_t details) { + return details->swift_binary_details.compiled_module_path; +} + +swiftscan_string_ref_t swiftscan_swift_binary_detail_get_module_doc_path( + swiftscan_module_details_t details) { + return details->swift_binary_details.module_doc_path; +} + +swiftscan_string_ref_t +swiftscan_swift_binary_detail_get_module_source_info_path( + swiftscan_module_details_t details) { + return details->swift_binary_details.module_source_info_path; +} + +//=== Swift Placeholder Module Details query APIs -------------------------===// + +swiftscan_string_ref_t +swiftscan_swift_placeholder_detail_get_compiled_module_path( + swiftscan_module_details_t details) { + return details->swift_placeholder_details.module_source_info_path; +} + +swiftscan_string_ref_t swiftscan_swift_placeholder_detail_get_module_doc_path( + swiftscan_module_details_t details) { + return details->swift_placeholder_details.module_source_info_path; +} + +swiftscan_string_ref_t +swiftscan_swift_placeholder_detail_get_module_source_info_path( + swiftscan_module_details_t details) { + return details->swift_placeholder_details.module_source_info_path; +} + +//=== Clang Module Details query APIs -------------------------------------===// + +swiftscan_string_ref_t +swiftscan_clang_detail_get_module_map_path(swiftscan_module_details_t details) { + return details->clang_details.module_map_path; +} + +swiftscan_string_ref_t +swiftscan_clang_detail_get_context_hash(swiftscan_module_details_t details) { + return details->clang_details.context_hash; +} + +swiftscan_string_set_t * +swiftscan_clang_detail_get_command_line(swiftscan_module_details_t details) { + return details->clang_details.command_line; +} + +//=== Batch Scan Input Functions ------------------------------------------===// + +swiftscan_batch_scan_input_t *swiftscan_batch_scan_input_create() { + return new swiftscan_batch_scan_input_t; +} + +void swiftscan_batch_scan_input_set_modules( + swiftscan_batch_scan_input_t *input, int count, + swiftscan_batch_scan_entry_t *modules) { + input->count = count; + input->modules = modules; +} + +//=== Batch Scan Entry Functions ------------------------------------------===// + +swiftscan_batch_scan_entry_t swiftscan_batch_scan_entry_create() { + return new swiftscan_batch_scan_entry_s; +} + +void swiftscan_batch_scan_entry_set_module_name( + swiftscan_batch_scan_entry_t entry, const char *name) { + entry->module_name = create_clone(name); +} + +void swiftscan_batch_scan_entry_set_arguments( + swiftscan_batch_scan_entry_t entry, const char *arguments) { + entry->arguments = create_clone(arguments); +} + +void swiftscan_batch_scan_entry_set_is_swift(swiftscan_batch_scan_entry_t entry, + bool is_swift) { + entry->is_swift = is_swift; +} + +swiftscan_string_ref_t +swiftscan_batch_scan_entry_get_module_name(swiftscan_batch_scan_entry_t entry) { + return entry->module_name; +} + +swiftscan_string_ref_t +swiftscan_batch_scan_entry_get_arguments(swiftscan_batch_scan_entry_t entry) { + return entry->arguments; +} + +bool swiftscan_batch_scan_entry_get_is_swift( + swiftscan_batch_scan_entry_t entry) { + return entry->is_swift; +} + +//=== Prescan Result Functions --------------------------------------------===// + +swiftscan_string_set_t * +swiftscan_import_set_get_imports(swiftscan_import_set_t result) { + return result->imports; +} + +//=== Scanner Invocation Functions ----------------------------------------===// + +swiftscan_scan_invocation_t swiftscan_scan_invocation_create() { + return new swiftscan_scan_invocation_s; +} + +void swiftscan_scan_invocation_set_working_directory( + swiftscan_scan_invocation_t invocation, const char *working_directory) { + invocation->working_directory = create_clone(working_directory); +} + +SWIFTSCAN_PUBLIC void +swiftscan_scan_invocation_set_argv(swiftscan_scan_invocation_t invocation, + int argc, const char **argv) { + invocation->argv = create_set(argc, argv); +} + +swiftscan_string_ref_t swiftscan_scan_invocation_get_working_directory( + swiftscan_scan_invocation_t invocation) { + return invocation->working_directory; +} + +int swiftscan_scan_invocation_get_argc(swiftscan_scan_invocation_t invocation) { + return invocation->argv->count; +} + +swiftscan_string_set_t * +swiftscan_scan_invocation_get_argv(swiftscan_scan_invocation_t invocation) { + return invocation->argv; +} + +//=== Public Cleanup Functions --------------------------------------------===// + +void swiftscan_dependency_graph_dispose(swiftscan_dependency_graph_t result) { + swiftscan_string_dispose(result->main_module_name); + swiftscan_dependency_set_dispose(result->dependencies); + delete result; +} + +void swiftscan_import_set_dispose(swiftscan_import_set_t result) { + swiftscan_string_set_dispose(result->imports); + delete result; +} + +void swiftscan_batch_scan_entry_dispose(swiftscan_batch_scan_entry_t entry) { + swiftscan_string_dispose(entry->module_name); + swiftscan_string_dispose(entry->arguments); + delete entry; +} + +void swiftscan_batch_scan_input_dispose(swiftscan_batch_scan_input_t *input) { + for (size_t i = 0; i < input->count; ++i) { + swiftscan_batch_scan_entry_dispose(input->modules[i]); + } + delete[] input->modules; + delete input; +} + +void swiftscan_batch_scan_result_dispose( + swiftscan_batch_scan_result_t *result) { + for (size_t i = 0; i < result->count; ++i) { + swiftscan_dependency_graph_dispose(result->results[i]); + } + delete[] result->results; + delete result; +} + +void swiftscan_scan_invocation_dispose(swiftscan_scan_invocation_t invocation) { + swiftscan_string_dispose(invocation->working_directory); + swiftscan_string_set_dispose(invocation->argv); + delete invocation; +} diff --git a/tools/libSwiftScan/libSwiftScan.exports b/tools/libSwiftScan/libSwiftScan.exports new file mode 100644 index 00000000000..d6e4721bfff --- /dev/null +++ b/tools/libSwiftScan/libSwiftScan.exports @@ -0,0 +1,53 @@ +swiftscan_dependency_graph_get_main_module_name +swiftscan_dependency_graph_get_dependencies +swiftscan_module_info_get_module_name +swiftscan_module_info_get_module_path +swiftscan_module_info_get_source_files +swiftscan_module_info_get_direct_dependencies +swiftscan_module_info_get_details +swiftscan_module_detail_get_kind +swiftscan_swift_textual_detail_get_module_interface_path +swiftscan_swift_textual_detail_get_compiled_module_candidates +swiftscan_swift_textual_detail_get_bridging_header_path +swiftscan_swift_textual_detail_get_bridging_source_files +swiftscan_swift_textual_detail_get_bridging_module_dependencies +swiftscan_swift_textual_detail_get_command_line +swiftscan_swift_textual_detail_get_extra_pcm_args +swiftscan_swift_textual_detail_get_context_hash +swiftscan_swift_textual_detail_get_is_framework +swiftscan_swift_binary_detail_get_compiled_module_path +swiftscan_swift_binary_detail_get_module_doc_path +swiftscan_swift_binary_detail_get_module_source_info_path +swiftscan_swift_placeholder_detail_get_compiled_module_path +swiftscan_swift_placeholder_detail_get_module_doc_path +swiftscan_swift_placeholder_detail_get_module_source_info_path +swiftscan_clang_detail_get_module_map_path +swiftscan_clang_detail_get_context_hash +swiftscan_clang_detail_get_command_line +swiftscan_batch_scan_input_set_modules +swiftscan_batch_scan_entry_set_module_name +swiftscan_batch_scan_entry_set_arguments +swiftscan_batch_scan_entry_set_is_swift +swiftscan_batch_scan_entry_get_module_name +swiftscan_batch_scan_entry_get_arguments +swiftscan_batch_scan_entry_get_is_swift +swiftscan_import_set_get_imports +swiftscan_scan_invocation_set_working_directory +swiftscan_scan_invocation_set_argv +swiftscan_scan_invocation_get_working_directory +swiftscan_scan_invocation_get_argc +swiftscan_scan_invocation_get_argv +swiftscan_scan_invocation_create +swiftscan_batch_scan_input_create +swiftscan_batch_scan_entry_create +swiftscan_dependency_graph_create +swiftscan_batch_scan_result_create +swiftscan_import_set_create +swiftscan_scanner_create +swiftscan_scan_invocation_dispose +swiftscan_batch_scan_input_dispose +swiftscan_batch_scan_entry_dispose +swiftscan_dependency_graph_dispose +swiftscan_batch_scan_result_dispose +swiftscan_import_set_dispose +swiftscan_scanner_dispose diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index ca552a07b4d..0f74d033a77 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -7,6 +7,7 @@ if(SWIFT_INCLUDE_TOOLS) add_subdirectory(AST) add_subdirectory(Basic) add_subdirectory(ClangImporter) + add_subdirectory(DependencyScan) add_subdirectory(Driver) add_subdirectory(FrontendTool) add_subdirectory(Localization) diff --git a/unittests/DependencyScan/CMakeLists.txt b/unittests/DependencyScan/CMakeLists.txt new file mode 100644 index 00000000000..e9d9d5895bd --- /dev/null +++ b/unittests/DependencyScan/CMakeLists.txt @@ -0,0 +1,11 @@ +add_swift_unittest(swiftScanTests + ScanFixture.cpp + ModuleDeps.cpp) + +target_link_libraries(swiftScanTests + PRIVATE + swiftDependencyScan + libSwiftScan) + +target_compile_definitions(swiftScanTests PRIVATE + SWIFTLIB_DIR=\"${SWIFTLIB_DIR}\") diff --git a/unittests/DependencyScan/ModuleDeps.cpp b/unittests/DependencyScan/ModuleDeps.cpp new file mode 100644 index 00000000000..33ae5a12693 --- /dev/null +++ b/unittests/DependencyScan/ModuleDeps.cpp @@ -0,0 +1,187 @@ +//===---------------------- ModuleDeps.cpp --------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "ScanFixture.h" +#include "swift/Basic/Platform.h" +#include "swift/Basic/Defer.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include "gtest/gtest.h" +#include + +using namespace swift; +using namespace swift::unittest; +using namespace swift::dependencies; + +static std::string createFilename(StringRef base, StringRef name) { + SmallString<256> path = base; + llvm::sys::path::append(path, name); + return llvm::Twine(path).str(); +} + +static bool emitFileWithContents(StringRef path, StringRef contents, + std::string *pathOut = nullptr) { + int FD; + if (llvm::sys::fs::openFileForWrite(path, FD)) + return true; + if (pathOut) + *pathOut = path.str(); + llvm::raw_fd_ostream file(FD, /*shouldClose=*/true); + file << contents; + return false; +} + +static bool emitFileWithContents(StringRef base, StringRef name, + StringRef contents, + std::string *pathOut = nullptr) { + return emitFileWithContents(createFilename(base, name), contents, pathOut); +} + +TEST_F(ScanTest, TestModuleDeps) { + SmallString<256> tempDir; + ASSERT_FALSE(llvm::sys::fs::createUniqueDirectory("ScanTest.TestModuleDeps", tempDir)); + SWIFT_DEFER { llvm::sys::fs::remove_directories(tempDir); }; + + // Create test input file + std::string TestPathStr = createFilename(tempDir, "foo.swift"); + ASSERT_FALSE(emitFileWithContents(tempDir, "foo.swift", "import A\n")); + llvm::dbgs() << "Input File: " << TestPathStr << "\n"; + + // Create includes + std::string IncludeDirPath = createFilename(tempDir, "include"); + ASSERT_FALSE(llvm::sys::fs::create_directory(IncludeDirPath)); + std::string CHeadersDirPath = createFilename(IncludeDirPath, "CHeaders"); + ASSERT_FALSE(llvm::sys::fs::create_directory(CHeadersDirPath)); + std::string SwiftDirPath = createFilename(IncludeDirPath, "Swift"); + ASSERT_FALSE(llvm::sys::fs::create_directory(SwiftDirPath)); + + // Create imported module Swift interface files + ASSERT_FALSE(emitFileWithContents(SwiftDirPath, "A.swiftinterface", + "// swift-interface-format-version: 1.0\n\ +// swift-module-flags: -module-name A\n\ +import Swift\n\ +@_exported import A\n\ +public func overlayFuncA() { }\n")); + ASSERT_FALSE(emitFileWithContents(SwiftDirPath, "E.swiftinterface", + "// swift-interface-format-version: 1.0\n\ +// swift-module-flags: -module-name E\n\ +import Swift\n\ +public func funcE()\n")); + ASSERT_FALSE(emitFileWithContents(SwiftDirPath, "F.swiftinterface", + "// swift-interface-format-version: 1.0\n\ +// swift-module-flags: -module-name\n\ +import Swift\n\ +@_exported import F\n\ +public func funcF() { }")); + ASSERT_FALSE(emitFileWithContents(SwiftDirPath, "G.swiftinterface", + "// swift-interface-format-version: 1.0\n\ +// swift-module-flags: -module-name G -swift-version 5 -target x86_64-apple-macosx10.9\n\ +#if swift(>=5.0)\n\ +@_exported import G\n\ +import Swift\n\ +public func overlayFuncG() { }\n\ +let stringG : String = \"Build\"\n\ +#endif")); + + // Create imported module C modulemap/headers + ASSERT_FALSE( + emitFileWithContents(CHeadersDirPath, "A.h", "void funcA(void);")); + ASSERT_FALSE(emitFileWithContents(CHeadersDirPath, "B.h", "#include \"A.h\"\ +void funcB(void);")); + ASSERT_FALSE(emitFileWithContents(CHeadersDirPath, "C.h", "#include \"B.h\"\n\ +void funcC(void);\ +const char* stringC() { return \"Module\"; }")); + ASSERT_FALSE( + emitFileWithContents(CHeadersDirPath, "D.h", "void funcD(void);")); + ASSERT_FALSE( + emitFileWithContents(CHeadersDirPath, "F.h", "void funcF(void);")); + ASSERT_FALSE(emitFileWithContents( + CHeadersDirPath, "G.h", + "#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 110000\n\ +#include \"X.h\"\n\ +#endif\n\ +void funcG(void);")); + ASSERT_FALSE( + emitFileWithContents(CHeadersDirPath, "X.h", "void funcX(void);")); + ASSERT_FALSE(emitFileWithContents(CHeadersDirPath, "Bridging.h", + "#include \"BridgingOther.h\"\n\ +int bridging_other(void);")); + ASSERT_FALSE(emitFileWithContents(CHeadersDirPath, "BridgingOther.h", + "#include \"F.h\"\n\ +int bridging_other(void);")); + + ASSERT_FALSE( + emitFileWithContents(CHeadersDirPath, "module.modulemap", "module A {\n\ +header \"A.h\"\n\ +export *\n\ +}\n\ +module B {\n\ +header \"B.h\"\n\ +export *\n\ +}\n\ +module C {\n\ +header \"C.h\"\n\ +export *\n\ +}\n\ +module D {\n\ +header \"D.h\"\n\ +export *\n\ +}\n\ +module F {\n\ +header \"F.h\"\n\ +export *\n\ +}\n\ +module G {\n\ +header \"G.h\"\n\ +export *\n\ +}\n\ +module X {\n\ +header \"X.h\"\n\ +export *\n\ +}")); + + // Paths to shims and stdlib + llvm::SmallString<128> ShimsLibDir = StdLibDir; + llvm::sys::path::append(ShimsLibDir, "shims"); + auto Target = llvm::Triple(llvm::sys::getDefaultTargetTriple()); + llvm::sys::path::append(StdLibDir, getPlatformNameForTriple(Target)); + + std::vector CommandStrArr = { + std::string("'") + TestPathStr + std::string("'"), + std::string("-I ") + std::string("'") + SwiftDirPath + std::string("'"), + std::string("-I ") + std::string("'") + CHeadersDirPath + std::string("'"), + std::string("-I ") + std::string("'") + StdLibDir.str().str() + std::string("'"), + std::string("-I ") + std::string("'") + ShimsLibDir.str().str() + std::string("'") + }; + + // On Windows we need to add an extra escape for path separator characters because otherwise + // the command line tokenizer will treat them as escape characters. + for (size_t i = 0; i < CommandStrArr.size(); ++i) { + std::replace(CommandStrArr[i].begin(), CommandStrArr[i].end(), '\\', '/'); + } + + std::vector Command; + llvm::dbgs() << "Compiler Command: \n"; + for (auto &command : CommandStrArr) { + Command.push_back(command.c_str()); + llvm::dbgs() << command.c_str() << "\n"; + } + auto DependenciesOrErr = ScannerTool.getDependencies(Command, {}); + ASSERT_FALSE(DependenciesOrErr.getError()); + auto Dependencies = DependenciesOrErr.get(); + // TODO: Output/verify dependency graph correctness + // llvm::dbgs() << "Deps: " << Dependencies << "\n"; + + swiftscan_dependency_graph_dispose(Dependencies); +} diff --git a/unittests/DependencyScan/ScanFixture.cpp b/unittests/DependencyScan/ScanFixture.cpp new file mode 100644 index 00000000000..bf0b0922489 --- /dev/null +++ b/unittests/DependencyScan/ScanFixture.cpp @@ -0,0 +1,31 @@ +//===-------- ScanFixture.cpp - Dependency scanning tests -*- C++ -------*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "ScanFixture.h" +#include "swift/Basic/LLVMInitialize.h" +#include "llvm/Support/FileSystem.h" +#include "gtest/gtest.h" + +using namespace swift; +using namespace swift::unittest; +using namespace swift::dependencies; + +ScanTest::ScanTest() : ScannerTool(), StdLibDir(SWIFTLIB_DIR) { + INITIALIZE_LLVM(); + // Create a temporary filesystem workspace for this test. + llvm::sys::fs::createUniqueDirectory("ScanTest.Workspace", + TemporaryTestWorkspace); +} + +ScanTest::~ScanTest() { + llvm::sys::fs::remove_directories(TemporaryTestWorkspace); +} diff --git a/unittests/DependencyScan/ScanFixture.h b/unittests/DependencyScan/ScanFixture.h new file mode 100644 index 00000000000..b68f624b0d3 --- /dev/null +++ b/unittests/DependencyScan/ScanFixture.h @@ -0,0 +1,40 @@ +//===-------- ScanFixture.h - Dependency scanning tests -*- C++ ---------*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift-c/DependencyScan/DependencyScan.h" +#include "swift/DependencyScan/DependencyScanningTool.h" +#include "gtest/gtest.h" +#include + +using namespace swift::dependencies; + +namespace swift { +namespace unittest { + +class ScanTest : public ::testing::Test { +public: + ScanTest(); + ~ScanTest(); + +protected: + // The tool used to execute tests' scanning queries + DependencyScanningTool ScannerTool; + + // Test workspace directory + llvm::SmallString<256> TemporaryTestWorkspace; + + // Path to where the Swift standard library can be found + llvm::SmallString<128> StdLibDir; +}; + +} // end namespace unittest +} // end namespace swift