[ScanDependency] Respect working directory set inside invocation

Teach scanner to respect the working directory set in the invocation
through scanner C API.

Also add test infrastructure to testing scanner from C API. Break up
DependencyScan lib into two so the swift-scan-test and remain small
without understanding swift AST.

rdar://127626011
This commit is contained in:
Steven Wu
2024-05-07 15:49:52 -07:00
parent 7c69bec9b4
commit 47598bbe8f
11 changed files with 723 additions and 568 deletions

View File

@@ -0,0 +1,30 @@
//===-------------- 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_DEPENDENCY_SCAN_JSON_H
#define SWIFT_DEPENDENCY_SCAN_JSON_H
#include "swift-c/DependencyScan/DependencyScan.h"
#include "llvm/Support/raw_ostream.h"
namespace swift::dependencies {
void writePrescanJSON(llvm::raw_ostream &out,
swiftscan_import_set_t importSet);
void writeJSON(llvm::raw_ostream &out,
swiftscan_dependency_graph_t fullDependencies);
} // namespace swift::dependencies
#endif // SWIFT_DEPENDENCY_SCAN_JSON_H

View File

@@ -87,14 +87,15 @@ public:
/// occurred, \c swiftscan_dependency_result_t otherwise.
llvm::ErrorOr<swiftscan_dependency_graph_t>
getDependencies(ArrayRef<const char *> Command,
const llvm::StringSet<> &PlaceholderModules);
const llvm::StringSet<> &PlaceholderModules,
StringRef WorkingDirectory);
/// 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<swiftscan_import_set_t>
getImports(ArrayRef<const char *> Command);
getImports(ArrayRef<const char *> Command, StringRef WorkingDirectory);
/// Collect the full module dependency graph for the input collection of
/// module names (batch inputs) and output them to the
@@ -104,7 +105,8 @@ public:
std::vector<llvm::ErrorOr<swiftscan_dependency_graph_t>>
getDependencies(ArrayRef<const char *> Command,
const std::vector<BatchScanInput> &BatchInput,
const llvm::StringSet<> &PlaceholderModules);
const llvm::StringSet<> &PlaceholderModules,
StringRef WorkingDirectory);
/// Writes the current `SharedCache` instance to a specified FileSystem path.
void serializeCache(llvm::StringRef path);
@@ -121,13 +123,15 @@ public:
/// Using the specified invocation command, instantiate a CompilerInstance
/// that will be used for this scan.
llvm::ErrorOr<ScanQueryInstance>
initCompilerInstanceForScan(ArrayRef<const char *> Command);
initCompilerInstanceForScan(ArrayRef<const char *> Command,
StringRef WorkingDirectory);
private:
/// Using the specified invocation command, initialize the scanner instance
/// for this scan. Returns the `CompilerInstance` that will be used.
llvm::ErrorOr<ScanQueryInstance>
initScannerForAction(ArrayRef<const char *> Command);
initScannerForAction(ArrayRef<const char *> Command,
StringRef WorkingDirectory);
/// Shared cache of module dependencies, re-used by individual full-scan queries
/// during the lifetime of this Tool.

View File

@@ -1,16 +1,20 @@
add_swift_host_library(swiftDependencyScanImpl STATIC
DependencyScanJSON.cpp
StringUtils.cpp)
add_swift_host_library(swiftDependencyScan STATIC
DependencyScanningTool.cpp
ModuleDependencyCacheSerialization.cpp
ModuleDependencyScanner.cpp
ScanDependencies.cpp
StringUtils.cpp)
ModuleDependencyScanner.cpp
ScanDependencies.cpp)
target_link_libraries(swiftDependencyScan INTERFACE
clangBasic)
target_link_libraries(swiftDependencyScan PRIVATE
swiftClangImporter
swiftAST
swiftSerialization)
swiftClangImporter
swiftAST
swiftSerialization)
target_link_libraries(swiftDependencyScan PUBLIC
swiftDependencyScanImpl)

View File

@@ -0,0 +1,555 @@
//===--- DependencyScanJSON.cpp -- JSON output for dependencies -----------===//
//
// 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/DependencyScanJSON.h"
#include "swift/Basic/Defer.h"
#include "swift/DependencyScan/DependencyScanImpl.h"
#include "swift/DependencyScan/StringUtils.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
using namespace swift;
using namespace swift::dependencies;
using namespace swift::c_string_utils;
using namespace llvm;
namespace {
std::string quote(StringRef unquoted) {
llvm::SmallString<128> buffer;
llvm::raw_svector_ostream os(buffer);
for (const auto ch : unquoted) {
if (ch == '\\')
os << '\\';
if (ch == '"')
os << '\\';
os << ch;
}
return buffer.str().str();
}
} // namespace
/// Write a single JSON field.
template <typename T>
void writeJSONSingleField(llvm::raw_ostream &out, StringRef fieldName,
const T &value, unsigned indentLevel,
bool trailingComma, bool nested = false);
/// Write a string value as JSON.
void writeJSONValue(llvm::raw_ostream &out, StringRef value,
unsigned indentLevel) {
out << "\"";
out << quote(value);
out << "\"";
}
void writeJSONValue(llvm::raw_ostream &out, swiftscan_string_ref_t value,
unsigned indentLevel) {
out << "\"";
out << quote(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 <typename T>
void writeJSONValue(llvm::raw_ostream &out, ArrayRef<T> 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 <typename T>
void writeJSONValue(llvm::raw_ostream &out, const std::vector<T> &values,
unsigned indentLevel) {
writeJSONValue(out, llvm::ArrayRef(values), indentLevel);
}
/// Write a single JSON field.
template <typename T>
void writeJSONSingleField(llvm::raw_ostream &out, StringRef fieldName,
const T &value, unsigned indentLevel,
bool trailingComma, bool nested) {
out.indent(indentLevel * 2);
writeJSONValue(out, fieldName, indentLevel);
out << ": ";
auto updatedIndentLevel = indentLevel;
if (nested) {
// This is a hack to "fix" a format for a value that should be a nested
// set of strings. Currently only capturedPCMArgs (clang) is expected to
// in the nested format, which supposedly only contains one set of strings.
// Adjust the indentation to account for the nested brackets.
updatedIndentLevel += 1;
out << "[\n";
out.indent(updatedIndentLevel * 2);
}
writeJSONValue(out, value, updatedIndentLevel);
if (nested) {
// If nested, add an extra closing brack with a correct indentation.
out << "\n";
out.indent(indentLevel * 2);
out << "]";
}
if (trailingComma)
out << ",";
out << "\n";
}
static void writeDependencies(llvm::raw_ostream &out,
const swiftscan_string_set_t *dependencies,
std::string dependenciesKind,
unsigned indentLevel, bool trailingComma) {
out.indent(indentLevel * 2);
out << "\"" + dependenciesKind + "\": ";
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;
}
namespace swift::dependencies {
void writePrescanJSON(llvm::raw_ostream &out,
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);
}
void writeJSON(llvm::raw_ostream &out,
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 if (clangDeps || swiftTextualDeps)
modulePath = get_C_string(moduleInfo.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)
writeDependencies(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.
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);
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 << "\"" << quote(candidate) << "\"";
if (i != count - 1)
out << ",";
out << "\n";
}
out.indent(5 * 2);
out << "],\n";
}
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 << "\"" << quote(arg) << "\"";
if (i != count - 1)
out << ",";
out << "\n";
}
out.indent(5 * 2);
out << "],\n";
writeJSONSingleField(out, "contextHash", swiftTextualDeps->context_hash,
5,
/*trailingComma=*/true);
bool hasBridgingHeaderPath =
swiftTextualDeps->bridging_header_path.data &&
get_C_string(swiftTextualDeps->bridging_header_path)[0] != '\0';
bool hasOverlayDependencies =
swiftTextualDeps->swift_overlay_module_dependencies &&
swiftTextualDeps->swift_overlay_module_dependencies->count > 0;
bool commaAfterBridgingHeaderPath = hasOverlayDependencies;
bool commaAfterExtraPcmArgs =
hasBridgingHeaderPath || commaAfterBridgingHeaderPath;
bool commaAfterFramework =
swiftTextualDeps->extra_pcm_args->count != 0 || commaAfterExtraPcmArgs;
if (swiftTextualDeps->cas_fs_root_id.length != 0) {
writeJSONSingleField(out, "casFSRootID",
swiftTextualDeps->cas_fs_root_id, 5,
/*trailingComma=*/true);
}
if (swiftTextualDeps->module_cache_key.length != 0) {
writeJSONSingleField(out, "moduleCacheKey",
swiftTextualDeps->module_cache_key, 5,
/*trailingComma=*/true);
}
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 << "\"" << quote(arg) << "\"";
if (i != count - 1)
out << ",";
out << "\n";
}
out.indent(5 * 2);
out << (commaAfterExtraPcmArgs ? "],\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);
if (swiftTextualDeps->bridging_header_include_tree.length != 0) {
writeJSONSingleField(out, "includeTree",
swiftTextualDeps->bridging_header_include_tree,
6, /*trailingComma=*/true);
}
writeJSONSingleField(out, "moduleDependencies",
swiftTextualDeps->bridging_module_dependencies, 6,
/*trailingComma=*/true);
out.indent(6 * 2);
out << "\"commandLine\": [\n";
for (int i = 0,
count = swiftTextualDeps->bridging_pch_command_line->count;
i < count; ++i) {
const auto &arg = get_C_string(
swiftTextualDeps->bridging_pch_command_line->strings[i]);
out.indent(7 * 2);
out << "\"" << quote(arg) << "\"";
if (i != count - 1)
out << ",";
out << "\n";
}
out.indent(6 * 2);
out << "]\n";
out.indent(5 * 2);
out << (commaAfterBridgingHeaderPath ? "},\n" : "}\n");
}
if (hasOverlayDependencies) {
writeDependencies(out, swiftTextualDeps->swift_overlay_module_dependencies,
"swiftOverlayDependencies", 5,
/*trailingComma=*/false);
}
} 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) {
bool hasOverlayDependencies =
swiftBinaryDeps->swift_overlay_module_dependencies &&
swiftBinaryDeps->swift_overlay_module_dependencies->count > 0;
out << "\"swiftPrebuiltExternal\": {\n";
assert(swiftBinaryDeps->compiled_module_path.data &&
get_C_string(swiftBinaryDeps->compiled_module_path)[0] != '\0' &&
"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=*/true);
if (swiftBinaryDeps->module_cache_key.length != 0) {
writeJSONSingleField(out, "moduleCacheKey",
swiftBinaryDeps->module_cache_key, 5,
/*trailingComma=*/true);
}
// Module Header Dependencies
if (swiftBinaryDeps->header_dependency.length != 0)
writeJSONSingleField(out, "headerDependency",
swiftBinaryDeps->header_dependency, 5,
/*trailingComma=*/true);
// Module Header Module Dependencies
if (swiftBinaryDeps->header_dependencies_module_dependnecies->count != 0)
writeJSONSingleField(out, "headerModuleDependencies",
swiftBinaryDeps->header_dependencies_module_dependnecies, 5,
/*trailingComma=*/true);
// Module Header Source Files
if (swiftBinaryDeps->header_dependencies_source_files->count != 0)
writeJSONSingleField(out, "headerDependenciesSourceFiles",
swiftBinaryDeps->header_dependencies_source_files, 5,
/*trailingComma=*/true);
if (hasOverlayDependencies) {
writeDependencies(out, swiftBinaryDeps->swift_overlay_module_dependencies,
"swiftOverlayDependencies", 5,
/*trailingComma=*/true);
}
writeJSONSingleField(out, "isFramework", swiftBinaryDeps->is_framework,
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=*/true);
if (clangDeps->cas_fs_root_id.length != 0)
writeJSONSingleField(out, "casFSRootID", clangDeps->cas_fs_root_id, 5,
/*trailingComma=*/true);
if (clangDeps->clang_include_tree.length != 0)
writeJSONSingleField(out, "clangIncludeTree",
clangDeps->clang_include_tree, 5,
/*trailingComma=*/true);
if (clangDeps->module_cache_key.length != 0)
writeJSONSingleField(out, "moduleCacheKey", clangDeps->module_cache_key,
5,
/*trailingComma=*/true);
// Captured PCM arguments.
writeJSONSingleField(out, "capturedPCMArgs", clangDeps->captured_pcm_args, 5,
/*trailingComma=*/false, /*nested=*/true);
}
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";
}
}
} // namespace swift::dependencies

View File

@@ -148,10 +148,10 @@ DependencyScanningTool::DependencyScanningTool()
llvm::ErrorOr<swiftscan_dependency_graph_t>
DependencyScanningTool::getDependencies(
ArrayRef<const char *> Command,
const llvm::StringSet<> &PlaceholderModules) {
ArrayRef<const char *> Command, const llvm::StringSet<> &PlaceholderModules,
StringRef WorkingDirectory) {
// The primary instance used to scan the query Swift source-code
auto QueryContextOrErr = initScannerForAction(Command);
auto QueryContextOrErr = initScannerForAction(Command, WorkingDirectory);
if (std::error_code EC = QueryContextOrErr.getError())
return EC;
auto QueryContext = std::move(*QueryContextOrErr);
@@ -173,9 +173,10 @@ DependencyScanningTool::getDependencies(
}
llvm::ErrorOr<swiftscan_import_set_t>
DependencyScanningTool::getImports(ArrayRef<const char *> Command) {
DependencyScanningTool::getImports(ArrayRef<const char *> Command,
StringRef WorkingDirectory) {
// The primary instance used to scan the query Swift source-code
auto QueryContextOrErr = initScannerForAction(Command);
auto QueryContextOrErr = initScannerForAction(Command, WorkingDirectory);
if (std::error_code EC = QueryContextOrErr.getError())
return EC;
auto QueryContext = std::move(*QueryContextOrErr);
@@ -199,9 +200,9 @@ std::vector<llvm::ErrorOr<swiftscan_dependency_graph_t>>
DependencyScanningTool::getDependencies(
ArrayRef<const char *> Command,
const std::vector<BatchScanInput> &BatchInput,
const llvm::StringSet<> &PlaceholderModules) {
const llvm::StringSet<> &PlaceholderModules, StringRef WorkingDirectory) {
// The primary instance used to scan Swift modules
auto QueryContextOrErr = initScannerForAction(Command);
auto QueryContextOrErr = initScannerForAction(Command, WorkingDirectory);
if (std::error_code EC = QueryContextOrErr.getError())
return std::vector<llvm::ErrorOr<swiftscan_dependency_graph_t>>(
BatchInput.size(), std::make_error_code(std::errc::invalid_argument));
@@ -264,17 +265,17 @@ void DependencyScanningTool::resetDiagnostics() {
llvm::ErrorOr<ScanQueryInstance>
DependencyScanningTool::initScannerForAction(
ArrayRef<const char *> Command) {
ArrayRef<const char *> Command, StringRef WorkingDirectory) {
// The remainder of this method operates on shared state in the
// scanning service and global LLVM state with:
// llvm::cl::ResetAllOptionOccurrences
llvm::sys::SmartScopedLock<true> Lock(DependencyScanningToolStateLock);
return initCompilerInstanceForScan(Command);
return initCompilerInstanceForScan(Command, WorkingDirectory);
}
llvm::ErrorOr<ScanQueryInstance>
DependencyScanningTool::initCompilerInstanceForScan(
ArrayRef<const char *> CommandArgs) {
ArrayRef<const char *> CommandArgs, StringRef WorkingDir) {
// State unique to an individual scan
auto Instance = std::make_unique<CompilerInstance>();
auto ScanDiagnosticConsumer = std::make_unique<DependencyScanDiagnosticCollector>();
@@ -291,8 +292,9 @@ DependencyScanningTool::initCompilerInstanceForScan(
}
CompilerInvocation Invocation;
SmallString<128> WorkingDirectory;
llvm::sys::fs::current_path(WorkingDirectory);
SmallString<128> WorkingDirectory(WorkingDir);
if (WorkingDirectory.empty())
llvm::sys::fs::current_path(WorkingDirectory);
// We must reset option occurrences because we are handling an unrelated
// command-line to those possibly parsed before using the same tool.

View File

@@ -29,6 +29,7 @@
#include "swift/Basic/STLExtras.h"
#include "swift/ClangImporter/ClangImporter.h"
#include "swift/DependencyScan/DependencyScanImpl.h"
#include "swift/DependencyScan/DependencyScanJSON.h"
#include "swift/DependencyScan/DependencyScanningTool.h"
#include "swift/DependencyScan/ModuleDependencyScanner.h"
#include "swift/DependencyScan/ScanDependencies.h"
@@ -531,541 +532,10 @@ pruneUnusedVFSOverlays(ModuleDependencyID moduleID,
return llvm::Error::success();
}
namespace {
std::string quote(StringRef unquoted) {
llvm::SmallString<128> buffer;
llvm::raw_svector_ostream os(buffer);
for (const auto ch : unquoted) {
if (ch == '\\')
os << '\\';
if (ch == '"')
os << '\\';
os << ch;
}
return buffer.str().str();
}
}
/// Write a single JSON field.
template <typename T>
void writeJSONSingleField(llvm::raw_ostream &out, StringRef fieldName,
const T &value, unsigned indentLevel,
bool trailingComma, bool nested = false);
/// Write a string value as JSON.
void writeJSONValue(llvm::raw_ostream &out, StringRef value,
unsigned indentLevel) {
out << "\"";
out << quote(value);
out << "\"";
}
void writeJSONValue(llvm::raw_ostream &out, swiftscan_string_ref_t value,
unsigned indentLevel) {
out << "\"";
out << quote(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 <typename T>
void writeJSONValue(llvm::raw_ostream &out, ArrayRef<T> 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 <typename T>
void writeJSONValue(llvm::raw_ostream &out, const std::vector<T> &values,
unsigned indentLevel) {
writeJSONValue(out, llvm::ArrayRef(values), indentLevel);
}
/// Write a single JSON field.
template <typename T>
void writeJSONSingleField(llvm::raw_ostream &out, StringRef fieldName,
const T &value, unsigned indentLevel,
bool trailingComma, bool nested) {
out.indent(indentLevel * 2);
writeJSONValue(out, fieldName, indentLevel);
out << ": ";
auto updatedIndentLevel = indentLevel;
if (nested) {
// This is a hack to "fix" a format for a value that should be a nested
// set of strings. Currently only capturedPCMArgs (clang) is expected to
// in the nested format, which supposedly only contains one set of strings.
// Adjust the indentation to account for the nested brackets.
updatedIndentLevel += 1;
out << "[\n";
out.indent(updatedIndentLevel * 2);
}
writeJSONValue(out, value, updatedIndentLevel);
if (nested) {
// If nested, add an extra closing brack with a correct indentation.
out << "\n";
out.indent(indentLevel * 2);
out << "]";
}
if (trailingComma)
out << ",";
out << "\n";
}
void writeDependencies(llvm::raw_ostream &out,
const swiftscan_string_set_t *dependencies,
std::string dependenciesKind,
unsigned indentLevel, bool trailingComma) {
out.indent(indentLevel * 2);
out << "\"" + dependenciesKind + "\": ";
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 if (clangDeps || swiftTextualDeps)
modulePath = get_C_string(moduleInfo.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)
writeDependencies(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.
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);
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 << "\"" << quote(candidate) << "\"";
if (i != count - 1)
out << ",";
out << "\n";
}
out.indent(5 * 2);
out << "],\n";
}
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 << "\"" << quote(arg) << "\"";
if (i != count - 1)
out << ",";
out << "\n";
}
out.indent(5 * 2);
out << "],\n";
writeJSONSingleField(out, "contextHash", swiftTextualDeps->context_hash,
5,
/*trailingComma=*/true);
bool hasBridgingHeaderPath =
swiftTextualDeps->bridging_header_path.data &&
get_C_string(swiftTextualDeps->bridging_header_path)[0] != '\0';
bool hasOverlayDependencies =
swiftTextualDeps->swift_overlay_module_dependencies &&
swiftTextualDeps->swift_overlay_module_dependencies->count > 0;
bool commaAfterBridgingHeaderPath = hasOverlayDependencies;
bool commaAfterExtraPcmArgs =
hasBridgingHeaderPath || commaAfterBridgingHeaderPath;
bool commaAfterFramework =
swiftTextualDeps->extra_pcm_args->count != 0 || commaAfterExtraPcmArgs;
if (swiftTextualDeps->cas_fs_root_id.length != 0) {
writeJSONSingleField(out, "casFSRootID",
swiftTextualDeps->cas_fs_root_id, 5,
/*trailingComma=*/true);
}
if (swiftTextualDeps->module_cache_key.length != 0) {
writeJSONSingleField(out, "moduleCacheKey",
swiftTextualDeps->module_cache_key, 5,
/*trailingComma=*/true);
}
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 << "\"" << quote(arg) << "\"";
if (i != count - 1)
out << ",";
out << "\n";
}
out.indent(5 * 2);
out << (commaAfterExtraPcmArgs ? "],\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);
if (swiftTextualDeps->bridging_header_include_tree.length != 0) {
writeJSONSingleField(out, "includeTree",
swiftTextualDeps->bridging_header_include_tree,
6, /*trailingComma=*/true);
}
writeJSONSingleField(out, "moduleDependencies",
swiftTextualDeps->bridging_module_dependencies, 6,
/*trailingComma=*/true);
out.indent(6 * 2);
out << "\"commandLine\": [\n";
for (int i = 0,
count = swiftTextualDeps->bridging_pch_command_line->count;
i < count; ++i) {
const auto &arg = get_C_string(
swiftTextualDeps->bridging_pch_command_line->strings[i]);
out.indent(7 * 2);
out << "\"" << quote(arg) << "\"";
if (i != count - 1)
out << ",";
out << "\n";
}
out.indent(6 * 2);
out << "]\n";
out.indent(5 * 2);
out << (commaAfterBridgingHeaderPath ? "},\n" : "}\n");
}
if (hasOverlayDependencies) {
writeDependencies(out, swiftTextualDeps->swift_overlay_module_dependencies,
"swiftOverlayDependencies", 5,
/*trailingComma=*/false);
}
} 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) {
bool hasOverlayDependencies =
swiftBinaryDeps->swift_overlay_module_dependencies &&
swiftBinaryDeps->swift_overlay_module_dependencies->count > 0;
out << "\"swiftPrebuiltExternal\": {\n";
assert(swiftBinaryDeps->compiled_module_path.data &&
get_C_string(swiftBinaryDeps->compiled_module_path)[0] != '\0' &&
"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=*/true);
if (swiftBinaryDeps->module_cache_key.length != 0) {
writeJSONSingleField(out, "moduleCacheKey",
swiftBinaryDeps->module_cache_key, 5,
/*trailingComma=*/true);
}
// Module Header Dependencies
if (swiftBinaryDeps->header_dependency.length != 0)
writeJSONSingleField(out, "headerDependency",
swiftBinaryDeps->header_dependency, 5,
/*trailingComma=*/true);
// Module Header Module Dependencies
if (swiftBinaryDeps->header_dependencies_module_dependnecies->count != 0)
writeJSONSingleField(out, "headerModuleDependencies",
swiftBinaryDeps->header_dependencies_module_dependnecies, 5,
/*trailingComma=*/true);
// Module Header Source Files
if (swiftBinaryDeps->header_dependencies_source_files->count != 0)
writeJSONSingleField(out, "headerDependenciesSourceFiles",
swiftBinaryDeps->header_dependencies_source_files, 5,
/*trailingComma=*/true);
if (hasOverlayDependencies) {
writeDependencies(out, swiftBinaryDeps->swift_overlay_module_dependencies,
"swiftOverlayDependencies", 5,
/*trailingComma=*/true);
}
writeJSONSingleField(out, "isFramework", swiftBinaryDeps->is_framework,
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=*/true);
if (clangDeps->cas_fs_root_id.length != 0)
writeJSONSingleField(out, "casFSRootID", clangDeps->cas_fs_root_id, 5,
/*trailingComma=*/true);
if (clangDeps->clang_include_tree.length != 0)
writeJSONSingleField(out, "clangIncludeTree",
clangDeps->clang_include_tree, 5,
/*trailingComma=*/true);
if (clangDeps->module_cache_key.length != 0)
writeJSONSingleField(out, "moduleCacheKey", clangDeps->module_cache_key,
5,
/*trailingComma=*/true);
// Captured PCM arguments.
writeJSONSingleField(out, "capturedPCMArgs", clangDeps->captured_pcm_args, 5,
/*trailingComma=*/false, /*nested=*/true);
}
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 bool writePrescanJSONToOutput(DiagnosticEngine &diags,
llvm::vfs::OutputBackend &backend,
StringRef path,
const swiftscan_import_set_t importSet) {
swiftscan_import_set_t importSet) {
return withOutputPath(diags, backend, path, [&](llvm::raw_pwrite_stream &os) {
writePrescanJSON(os, importSet);
return false;
@@ -1074,7 +544,7 @@ static bool writePrescanJSONToOutput(DiagnosticEngine &diags,
static bool writeJSONToOutput(DiagnosticEngine &diags,
llvm::vfs::OutputBackend &backend, StringRef path,
const swiftscan_dependency_graph_t dependencies) {
swiftscan_dependency_graph_t dependencies) {
return withOutputPath(diags, backend, path, [&](llvm::raw_pwrite_stream &os) {
writeJSON(os, dependencies);
return false;

View File

@@ -0,0 +1,24 @@
// RUN: %empty-directory(%t)
// RUN: split-file %s %t
// RUN: %target-swift-frontend -emit-module -module-name A -o %t/include/A.swiftmodule -swift-version 5 \
// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import -parse-stdlib \
// RUN: -emit-module-interface-path %t/include/A.swiftinterface -enable-library-evolution -I %t/internal -enable-testing \
// RUN: %t/A.swift
// RUN: %swift-scan-test -action scan_dependency -cwd %t -- %target-swift-frontend -emit-module -module-name Test \
// RUN: -swift-version 5 -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import -parse-stdlib \
// RUN: -I include %t/test.swift | %FileCheck %s
// CHECK: "mainModuleName": "Test"
// RUN: cd %t
// RUN: %swift-scan-test -action scan_dependency -- %target-swift-frontend -emit-module -module-name Test \
// RUN: -swift-version 5 -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import -parse-stdlib \
// RUN: -I include %t/test.swift | %FileCheck %s
//--- test.swift
import A
//--- A.swift
public func a() {}

View File

@@ -154,7 +154,9 @@ swiftscan_dependency_graph_create(swiftscan_scanner_t scanner,
Compilation.push_back(swift::c_string_utils::get_C_string(invocation->argv->strings[i]));
// Execute the scan and bridge the result
auto ScanResult = ScanningTool->getDependencies(Compilation, {});
auto ScanResult = ScanningTool->getDependencies(
Compilation, {},
swift::c_string_utils::get_C_string(invocation->working_directory));
if (ScanResult.getError())
return nullptr;
auto DependencyGraph = std::move(*ScanResult);
@@ -180,8 +182,9 @@ swiftscan_batch_scan_result_create(swiftscan_scanner_t scanner,
}
// Execute the scan and bridge the result
auto BatchScanResult =
ScanningTool->getDependencies(Compilation, BatchInput, {});
auto BatchScanResult = ScanningTool->getDependencies(
Compilation, BatchInput, {},
swift::c_string_utils::get_C_string(invocation->working_directory));
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) {
@@ -209,7 +212,9 @@ swiftscan_import_set_create(swiftscan_scanner_t scanner,
Compilation.push_back(swift::c_string_utils::get_C_string(invocation->argv->strings[i]));
// Execute the scan and bridge the result
auto PreScanResult = ScanningTool->getImports(Compilation);
auto PreScanResult = ScanningTool->getImports(
Compilation,
swift::c_string_utils::get_C_string(invocation->working_directory));
if (PreScanResult.getError())
return nullptr;
auto ImportSet = std::move(*PreScanResult);

View File

@@ -9,4 +9,5 @@ add_swift_host_tool(swift-scan-test
target_link_libraries(swift-scan-test
PRIVATE
swiftBasic
swiftDependencyScanImpl
libSwiftScan)

View File

@@ -17,6 +17,8 @@
#include "swift-c/DependencyScan/DependencyScan.h"
#include "swift/Basic/Defer.h"
#include "swift/Basic/FileTypes.h"
#include "swift/DependencyScan/DependencyScanJSON.h"
#include "swift/DependencyScan/StringUtils.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/CommandLine.h"
@@ -31,6 +33,7 @@ enum Actions {
compute_cache_key_from_index,
cache_query,
replay_result,
scan_dependency,
};
llvm::cl::OptionCategory Category("swift-scan-test Options");
@@ -43,13 +46,16 @@ llvm::cl::opt<std::string> Input("input", llvm::cl::desc("<file|index>"),
llvm::cl::opt<unsigned> Threads("threads",
llvm::cl::desc("<number of threads>"),
llvm::cl::cat(Category), cl::init(1));
llvm::cl::opt<std::string> WorkingDirectory("cwd", llvm::cl::desc("<path>"),
llvm::cl::cat(Category));
llvm::cl::opt<Actions>
Action("action", llvm::cl::desc("<action>"),
llvm::cl::values(clEnumVal(compute_cache_key, "compute cache key"),
clEnumVal(compute_cache_key_from_index,
"compute cache key from index"),
clEnumVal(cache_query, "cache query"),
clEnumVal(replay_result, "replay result")),
clEnumVal(replay_result, "replay result"),
clEnumVal(scan_dependency, "scan dependency")),
llvm::cl::cat(Category));
llvm::cl::list<std::string>
SwiftCommands(llvm::cl::Positional, llvm::cl::desc("<swift-frontend args>"),
@@ -185,13 +191,65 @@ static int action_replay_result(swiftscan_cas_t cas, const char *key,
return EXIT_SUCCESS;
}
static std::vector<const char *> createArgs(ArrayRef<std::string> Cmd,
StringSaver &Saver) {
static int action_scan_dependency(std::vector<const char *> &Args,
StringRef WorkingDirectory) {
auto scanner = swiftscan_scanner_create();
auto invocation = swiftscan_scan_invocation_create();
auto error = [&](StringRef msg) {
llvm::errs() << msg << "\n";
swiftscan_scan_invocation_dispose(invocation);
swiftscan_scanner_dispose(scanner);
return EXIT_FAILURE;
};
swiftscan_scan_invocation_set_working_directory(
invocation, WorkingDirectory.str().c_str());
swiftscan_scan_invocation_set_argv(invocation, Args.size(), Args.data());
auto graph = swiftscan_dependency_graph_create(scanner, invocation);
if (!graph)
return error("dependency scanning failed");
auto diags = swiftscan_dependency_graph_get_diagnostics(graph);
for (unsigned i = 0; i < diags->count; ++i) {
auto msg = swiftscan_diagnostic_get_message(diags->diagnostics[i]);
switch (swiftscan_diagnostic_get_severity(diags->diagnostics[i])) {
case SWIFTSCAN_DIAGNOSTIC_SEVERITY_ERROR:
llvm::errs() << "error: ";
break;
case SWIFTSCAN_DIAGNOSTIC_SEVERITY_WARNING:
llvm::errs() << "warning: ";
break;
case SWIFTSCAN_DIAGNOSTIC_SEVERITY_NOTE:
llvm::errs() << "note: ";
break;
case SWIFTSCAN_DIAGNOSTIC_SEVERITY_REMARK:
llvm::errs() << "remark: ";
break;
}
llvm::errs() << swift::c_string_utils::get_C_string(msg) << "\n";
}
swift::dependencies::writeJSON(llvm::outs(), graph);
swiftscan_scan_invocation_dispose(invocation);
swiftscan_scanner_dispose(scanner);
return EXIT_SUCCESS;
}
static std::vector<const char *>
createArgs(ArrayRef<std::string> Cmd, StringSaver &Saver, Actions Action) {
if (!Cmd.empty() && StringRef(Cmd.front()).ends_with("swift-frontend"))
Cmd = Cmd.drop_front();
// Quote all the arguments before passing to scanner. The scanner is currently
// tokenize the command-line again before parsing.
bool Quoted = Action == Actions::scan_dependency;
std::vector<const char *> Args;
for (auto A : Cmd) {
if (Quoted)
A = std::string("\"") + A + "\"";
StringRef Arg = Saver.save(A);
Args.push_back(Arg.data());
}
@@ -218,7 +276,7 @@ int main(int argc, char *argv[]) {
// Convert commands.
llvm::BumpPtrAllocator Alloc;
llvm::StringSaver Saver(Alloc);
auto Args = createArgs(SwiftCommands, Saver);
auto Args = createArgs(SwiftCommands, Saver, Action);
std::atomic<int> Ret = 0;
llvm::ThreadPool Pool(llvm::hardware_concurrency(Threads));
@@ -237,6 +295,8 @@ int main(int argc, char *argv[]) {
case replay_result:
Ret += action_replay_result(cas, CASID.c_str(), Args);
break;
case scan_dependency:
Ret += action_scan_dependency(Args, WorkingDirectory);
}
});
}

View File

@@ -174,7 +174,7 @@ export *\n\
for (auto &command : CommandStrArr) {
Command.push_back(command.c_str());
}
auto DependenciesOrErr = ScannerTool.getDependencies(Command, {});
auto DependenciesOrErr = ScannerTool.getDependencies(Command, {}, {});
ASSERT_FALSE(DependenciesOrErr.getError());
auto Dependencies = DependenciesOrErr.get();
// TODO: Output/verify dependency graph correctness
@@ -243,8 +243,8 @@ public func overlayFuncA() { }\n"));
CommandB.push_back(command.c_str());
}
auto instanceA = ScannerTool.initCompilerInstanceForScan(CommandA);
auto instanceB = ScannerTool.initCompilerInstanceForScan(CommandB);
auto instanceA = ScannerTool.initCompilerInstanceForScan(CommandA, {});
auto instanceB = ScannerTool.initCompilerInstanceForScan(CommandB, {});
// Ensure that scans that only differ in module name have distinct scanning context hashes
ASSERT_NE(instanceA->ScanInstance.get()->getInvocation().getModuleScanningHash(),
instanceB->ScanInstance.get()->getInvocation().getModuleScanningHash());