Files
swift-mirror/lib/DependencyScan/DependencyScanJSON.cpp
Artem Chikin f0518f64ec [Dependency Scanning] Add a field of source-imported dependencies
For the main source module, provide info on which dependencies are directly imported into the user program, explicitly ('import' statement) or implicitly (e.g. stdlib). Thist list does not include Swift overlay dependencies, cross-import dependencies, bridging header dependencies.
2025-04-30 10:17:45 -07:00

631 lines
23 KiB
C++

//===--- 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/Assertions.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 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 << "]";
}
/// 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) {
out.indent(indentLevel * 2);
writeJSONValue(out, fieldName, indentLevel);
out << ": ";
auto updatedIndentLevel = indentLevel;
writeJSONValue(out, value, updatedIndentLevel);
if (trailingComma)
out << ",";
out << "\n";
}
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 << "}";
}
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";
}
void writeLinkLibraries(llvm::raw_ostream &out,
const swiftscan_link_library_set_t *link_libraries,
unsigned indentLevel, bool trailingComma) {
out.indent(indentLevel * 2);
out << "\"linkLibraries\": ";
out << "[\n";
for (size_t i = 0; i < link_libraries->count; ++i) {
const auto &llInfo = *link_libraries->link_libraries[i];
out.indent((indentLevel + 1) * 2);
out << "{\n";
auto entryIndentLevel = ((indentLevel + 2) * 2);
out.indent(entryIndentLevel);
out << "\"linkName\": ";
writeJSONValue(out, llInfo.name, indentLevel);
out << ",\n";
out.indent(entryIndentLevel);
out << "\"isStatic\": ";
writeJSONValue(out, llInfo.isStatic, entryIndentLevel);
out << ",\n";
out.indent(entryIndentLevel);
out << "\"isFramework\": ";
writeJSONValue(out, llInfo.isFramework, entryIndentLevel);
out << ",\n";
out.indent(entryIndentLevel);
out << "\"shouldForceLoad\": ";
writeJSONValue(out, llInfo.forceLoad, entryIndentLevel);
out << "\n";
out.indent((indentLevel + 1) * 2);
out << "}";
if (i != link_libraries->count - 1) {
out << ",";
}
out << "\n";
}
out.indent(indentLevel * 2);
out << "]";
if (trailingComma)
out << ",";
out << "\n";
}
static void
writeMacroDependencies(llvm::raw_ostream &out,
const swiftscan_macro_dependency_set_t *macro_deps,
unsigned indentLevel, bool trailingComma) {
if (!macro_deps)
return;
out.indent(indentLevel * 2);
out << "\"macroDependencies\": ";
out << "[\n";
for (size_t i = 0; i < macro_deps->count; ++i) {
const auto &macroInfo = *macro_deps->macro_dependencies[i];
out.indent((indentLevel + 1) * 2);
out << "{\n";
auto entryIndentLevel = ((indentLevel + 2) * 2);
out.indent(entryIndentLevel);
out << "\"moduleName\": ";
writeJSONValue(out, macroInfo.moduleName, indentLevel);
out << ",\n";
out.indent(entryIndentLevel);
out << "\"libraryPath\": ";
writeJSONValue(out, macroInfo.libraryPath, entryIndentLevel);
out << ",\n";
out.indent(entryIndentLevel);
out << "\"executablePath\": ";
writeJSONValue(out, macroInfo.executablePath, entryIndentLevel);
out << "\n";
out.indent((indentLevel + 1) * 2);
out << "}";
if (i != macro_deps->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);
writeLinkLibraries(out, moduleInfo.link_libraries,
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 hasBridgingHeader =
(swiftTextualDeps->bridging_header_path.data &&
get_C_string(swiftTextualDeps->bridging_header_path)[0] != '\0') ||
swiftTextualDeps->bridging_pch_command_line->count != 0;
bool hasOverlayDependencies =
swiftTextualDeps->swift_overlay_module_dependencies &&
swiftTextualDeps->swift_overlay_module_dependencies->count > 0;
bool hasSourceImportedDependencies =
swiftTextualDeps->source_import_module_dependencies &&
swiftTextualDeps->source_import_module_dependencies->count > 0;
bool commaAfterBridgingHeaderPath = hasOverlayDependencies;
bool commaAfterFramework =
hasBridgingHeader || commaAfterBridgingHeaderPath;
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);
}
if (swiftTextualDeps->chained_bridging_header_path.length != 0) {
writeJSONSingleField(out, "chainedBridgingHeaderPath",
swiftTextualDeps->chained_bridging_header_path, 5,
/*trailingComma=*/true);
}
writeJSONSingleField(out, "userModuleVersion",
swiftTextualDeps->user_module_version, 5,
/*trailingComma=*/true);
writeMacroDependencies(out, swiftTextualDeps->macro_dependencies, 5,
/*trailingComma=*/true);
if (hasSourceImportedDependencies) {
writeDependencies(out, swiftTextualDeps->source_import_module_dependencies,
"sourceImportedDependencies", 5,
/*trailingComma=*/true);
}
writeJSONSingleField(out, "isFramework", swiftTextualDeps->is_framework,
5, commaAfterFramework);
/// Bridging header and its source file dependencies, if any.
if (hasBridgingHeader) {
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);
writeJSONSingleField(out, "userModuleVersion",
swiftBinaryDeps->user_module_version,
/*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);
}
writeMacroDependencies(out, swiftBinaryDeps->macro_dependencies, 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,
clangDeps->cas_fs_root_id.length != 0 ||
clangDeps->clang_include_tree.length != 0 ||
clangDeps->module_cache_key.length != 0);
if (clangDeps->cas_fs_root_id.length != 0)
writeJSONSingleField(out, "casFSRootID", clangDeps->cas_fs_root_id, 5,
clangDeps->clang_include_tree.length != 0 ||
clangDeps->module_cache_key.length != 0);
if (clangDeps->clang_include_tree.length != 0)
writeJSONSingleField(out, "clangIncludeTree",
clangDeps->clang_include_tree, 5,
clangDeps->module_cache_key.length != 0);
if (clangDeps->module_cache_key.length != 0)
writeJSONSingleField(out, "moduleCacheKey", clangDeps->module_cache_key,
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";
}
}
} // namespace swift::dependencies