diff --git a/include/swift-c/DependencyScan/DependencyScan.h b/include/swift-c/DependencyScan/DependencyScan.h index b4bcf5ed090b6..85b8249716a7d 100644 --- a/include/swift-c/DependencyScan/DependencyScan.h +++ b/include/swift-c/DependencyScan/DependencyScan.h @@ -300,13 +300,17 @@ swiftscan_batch_scan_result_dispose(swiftscan_batch_scan_result_t *result); SWIFTSCAN_PUBLIC void swiftscan_scan_invocation_dispose(swiftscan_scan_invocation_t invocation); -//=== Feature-Query Functions -----------------------------------------===// +//=== Feature-Query Functions ---------------------------------------------===// SWIFTSCAN_PUBLIC swiftscan_string_set_t * swiftscan_compiler_supported_arguments_query(); SWIFTSCAN_PUBLIC swiftscan_string_set_t * swiftscan_compiler_supported_features_query(); +//=== Target-Info Functions -----------------------------------------------===// +SWIFTSCAN_PUBLIC swiftscan_string_ref_t +swiftscan_compiler_target_info_query(swiftscan_scan_invocation_t invocation); + //=== Scanner Functions ---------------------------------------------------===// /// Container of the configuration state and shared cache for dependency diff --git a/include/swift/Basic/TargetInfo.h b/include/swift/Basic/TargetInfo.h new file mode 100644 index 0000000000000..eae57eddd47a4 --- /dev/null +++ b/include/swift/Basic/TargetInfo.h @@ -0,0 +1,40 @@ +//===--- TargetInfo.h - Target Info Output ---------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file provides a high-level API for emitting target info +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_TARGETINFO_H +#define SWIFT_TARGETINFO_H + +#include "swift/Basic/LLVM.h" + +namespace llvm { +class Triple; +class VersionTuple; +} + +namespace swift { +class CompilerInvocation; + +namespace targetinfo { +void printTargetInfo(const CompilerInvocation &invocation, + llvm::raw_ostream &out); + +void printTripleInfo(const llvm::Triple &triple, + llvm::Optional runtimeVersion, + llvm::raw_ostream &out); +} // namespace targetinfo +} // namespace swift + +#endif diff --git a/include/swift/DependencyScan/DependencyScanningTool.h b/include/swift/DependencyScan/DependencyScanningTool.h index a23c38ae479dc..615433fa78efa 100644 --- a/include/swift/DependencyScan/DependencyScanningTool.h +++ b/include/swift/DependencyScan/DependencyScanningTool.h @@ -24,6 +24,10 @@ namespace swift { namespace dependencies { +/// Given a set of arguments to a print-target-info frontend tool query, produce the +/// JSON target info. +llvm::ErrorOr getTargetInfo(ArrayRef Command); + /// The high-level implementation of the dependency scanner that runs on /// an individual worker thread. class DependencyScanningTool { diff --git a/lib/Basic/CMakeLists.txt b/lib/Basic/CMakeLists.txt index 2ee6cdf1b87db..014690ddb6da8 100644 --- a/lib/Basic/CMakeLists.txt +++ b/lib/Basic/CMakeLists.txt @@ -68,6 +68,7 @@ add_swift_host_library(swiftBasic STATIC StableHasher.cpp Statistic.cpp StringExtras.cpp + TargetInfo.cpp TaskQueue.cpp ThreadSafeRefCounted.cpp Unicode.cpp diff --git a/lib/Basic/TargetInfo.cpp b/lib/Basic/TargetInfo.cpp new file mode 100644 index 0000000000000..1bcd9f51b4971 --- /dev/null +++ b/lib/Basic/TargetInfo.cpp @@ -0,0 +1,154 @@ +//===--- TargetInfo.cpp - Target information printing --------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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/Basic/TargetInfo.h" +#include "swift/Basic/Version.h" +#include "swift/Basic/Platform.h" +#include "swift/Frontend/Frontend.h" + +#include "llvm/Support/raw_ostream.h" + +using namespace swift; + +/// Print information about a +static void printCompatibilityLibrary( + llvm::VersionTuple runtimeVersion, llvm::VersionTuple maxVersion, + StringRef filter, StringRef libraryName, bool &printedAny, + llvm::raw_ostream &out) { + if (runtimeVersion > maxVersion) + return; + + if (printedAny) { + out << ","; + } + + out << "\n"; + out << " {\n"; + + out << " \"libraryName\": \""; + out.write_escaped(libraryName); + out << "\",\n"; + + out << " \"filter\": \""; + out.write_escaped(filter); + out << "\"\n"; + out << " }"; + + printedAny = true; +} + +/// Print information about the selected target in JSON. +void targetinfo::printTargetInfo(const CompilerInvocation &invocation, + llvm::raw_ostream &out) { + out << "{\n"; + + // Compiler version, as produced by --version. + out << " \"compilerVersion\": \""; + out.write_escaped(version::getSwiftFullVersion( + version::Version::getCurrentLanguageVersion())); + out << "\",\n"; + + // Target triple and target variant triple. + auto runtimeVersion = + invocation.getIRGenOptions().AutolinkRuntimeCompatibilityLibraryVersion; + auto &langOpts = invocation.getLangOptions(); + out << " \"target\": "; + printTripleInfo(langOpts.Target, runtimeVersion, out); + out << ",\n"; + + if (auto &variant = langOpts.TargetVariant) { + out << " \"targetVariant\": "; + printTripleInfo(*variant, runtimeVersion, out); + out << ",\n"; + } + + // Various paths. + auto &searchOpts = invocation.getSearchPathOptions(); + out << " \"paths\": {\n"; + + if (!searchOpts.SDKPath.empty()) { + out << " \"sdkPath\": \""; + out.write_escaped(searchOpts.SDKPath); + out << "\",\n"; + } + + auto outputPaths = [&](StringRef name, const std::vector &paths){ + out << " \"" << name << "\": [\n"; + llvm::interleave(paths, [&out](const std::string &path) { + out << " \""; + out.write_escaped(path); + out << "\""; + }, [&out] { + out << ",\n"; + }); + out << "\n ],\n"; + }; + + outputPaths("runtimeLibraryPaths", searchOpts.RuntimeLibraryPaths); + outputPaths("runtimeLibraryImportPaths", + searchOpts.RuntimeLibraryImportPaths); + + out << " \"runtimeResourcePath\": \""; + out.write_escaped(searchOpts.RuntimeResourcePath); + out << "\"\n"; + + out << " }\n"; + + out << "}\n"; +} + +// Print information about the target triple in JSON. +void targetinfo::printTripleInfo(const llvm::Triple &triple, + llvm::Optional runtimeVersion, + llvm::raw_ostream &out) { + out << "{\n"; + + out << " \"triple\": \""; + out.write_escaped(triple.getTriple()); + out << "\",\n"; + + out << " \"unversionedTriple\": \""; + out.write_escaped(getUnversionedTriple(triple).getTriple()); + out << "\",\n"; + + out << " \"moduleTriple\": \""; + out.write_escaped(getTargetSpecificModuleTriple(triple).getTriple()); + out << "\",\n"; + + if (runtimeVersion) { + out << " \"swiftRuntimeCompatibilityVersion\": \""; + out.write_escaped(runtimeVersion->getAsString()); + out << "\",\n"; + + // Compatibility libraries that need to be linked. + out << " \"compatibilityLibraries\": ["; + bool printedAnyCompatibilityLibrary = false; + #define BACK_DEPLOYMENT_LIB(Version, Filter, LibraryName) \ + printCompatibilityLibrary( \ + *runtimeVersion, llvm::VersionTuple Version, #Filter, LibraryName, \ + printedAnyCompatibilityLibrary, out); + #include "swift/Frontend/BackDeploymentLibs.def" + + if (printedAnyCompatibilityLibrary) { + out << "\n "; + } + out << " ],\n"; + } else { + out << " \"compatibilityLibraries\": [ ],\n"; + } + + out << " \"librariesRequireRPath\": " + << (tripleRequiresRPathForSwiftLibrariesInOS(triple) ? "true" : "false") + << "\n"; + + out << " }"; +} diff --git a/lib/DependencyScan/DependencyScanningTool.cpp b/lib/DependencyScan/DependencyScanningTool.cpp index b74e88dc44608..c5054681ec55d 100644 --- a/lib/DependencyScan/DependencyScanningTool.cpp +++ b/lib/DependencyScan/DependencyScanningTool.cpp @@ -12,9 +12,11 @@ #include "swift/DependencyScan/DependencyScanningTool.h" #include "swift/DependencyScan/SerializedModuleDependencyCacheFormat.h" +#include "swift/DependencyScan/StringUtils.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsFrontend.h" #include "swift/Basic/LLVMInitialize.h" +#include "swift/Basic/TargetInfo.h" #include "swift/DependencyScan/DependencyScanImpl.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" @@ -24,6 +26,36 @@ namespace swift { namespace dependencies { +llvm::ErrorOr getTargetInfo(ArrayRef Command) { + // We must reset option occurences because we are handling an unrelated + // command-line to those possibly parsed parsed before using the same tool. + // We must do so because LLVM options parsing is done using a managed + // static `GlobalParser`. + llvm::cl::ResetAllOptionOccurrences(); + // Parse arguments. + std::string CommandString; + for (const auto *c : Command) { + CommandString.append(c); + CommandString.append(" "); + } + SmallVector Args; + llvm::BumpPtrAllocator Alloc; + llvm::StringSaver Saver(Alloc); + llvm::cl::TokenizeGNUCommandLine(CommandString, Saver, Args); + SourceManager dummySM; + DiagnosticEngine DE(dummySM); + CompilerInvocation Invocation; + if (Invocation.parseArgs(Args, DE)) { + return std::make_error_code(std::errc::invalid_argument); + } + + // Store the result to a string. + std::string ResultStr; + llvm::raw_string_ostream StrOS(ResultStr); + swift::targetinfo::printTargetInfo(Invocation, StrOS); + return create_clone(ResultStr.c_str()); +} + DependencyScanningTool::DependencyScanningTool() : SharedCache(std::make_unique()), VersionedPCMInstanceCacheCache( diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index e23155e476ce3..560afb32d4c42 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -49,6 +49,7 @@ #include "swift/Basic/Statistic.h" #include "swift/Basic/UUID.h" #include "swift/Basic/Version.h" +#include "swift/Basic/TargetInfo.h" #include "swift/Option/Options.h" #include "swift/Frontend/Frontend.h" #include "swift/Frontend/AccumulatingDiagnosticConsumer.h" @@ -1810,141 +1811,6 @@ createJSONFixItDiagnosticConsumerIfNeeded( }); } -/// Print information about a -static void printCompatibilityLibrary( - llvm::VersionTuple runtimeVersion, llvm::VersionTuple maxVersion, - StringRef filter, StringRef libraryName, bool &printedAny, - llvm::raw_ostream &out) { - if (runtimeVersion > maxVersion) - return; - - if (printedAny) { - out << ","; - } - - out << "\n"; - out << " {\n"; - - out << " \"libraryName\": \""; - out.write_escaped(libraryName); - out << "\",\n"; - - out << " \"filter\": \""; - out.write_escaped(filter); - out << "\"\n"; - out << " }"; - - printedAny = true; -} - -/// Print information about the target triple in JSON. -static void printTripleInfo(const llvm::Triple &triple, - llvm::Optional runtimeVersion, - llvm::raw_ostream &out) { - out << "{\n"; - - out << " \"triple\": \""; - out.write_escaped(triple.getTriple()); - out << "\",\n"; - - out << " \"unversionedTriple\": \""; - out.write_escaped(getUnversionedTriple(triple).getTriple()); - out << "\",\n"; - - out << " \"moduleTriple\": \""; - out.write_escaped(getTargetSpecificModuleTriple(triple).getTriple()); - out << "\",\n"; - - if (runtimeVersion) { - out << " \"swiftRuntimeCompatibilityVersion\": \""; - out.write_escaped(runtimeVersion->getAsString()); - out << "\",\n"; - - // Compatibility libraries that need to be linked. - out << " \"compatibilityLibraries\": ["; - bool printedAnyCompatibilityLibrary = false; - #define BACK_DEPLOYMENT_LIB(Version, Filter, LibraryName) \ - printCompatibilityLibrary( \ - *runtimeVersion, llvm::VersionTuple Version, #Filter, LibraryName, \ - printedAnyCompatibilityLibrary, out); - #include "swift/Frontend/BackDeploymentLibs.def" - - if (printedAnyCompatibilityLibrary) { - out << "\n "; - } - out << " ],\n"; - } else { - out << " \"compatibilityLibraries\": [ ],\n"; - } - - out << " \"librariesRequireRPath\": " - << (tripleRequiresRPathForSwiftLibrariesInOS(triple) ? "true" : "false") - << "\n"; - - out << " }"; - -} - -/// Print information about the selected target in JSON. -static void printTargetInfo(const CompilerInvocation &invocation, - llvm::raw_ostream &out) { - out << "{\n"; - - // Compiler version, as produced by --version. - out << " \"compilerVersion\": \""; - out.write_escaped(version::getSwiftFullVersion( - version::Version::getCurrentLanguageVersion())); - out << "\",\n"; - - // Target triple and target variant triple. - auto runtimeVersion = - invocation.getIRGenOptions().AutolinkRuntimeCompatibilityLibraryVersion; - auto &langOpts = invocation.getLangOptions(); - out << " \"target\": "; - printTripleInfo(langOpts.Target, runtimeVersion, out); - out << ",\n"; - - if (auto &variant = langOpts.TargetVariant) { - out << " \"targetVariant\": "; - printTripleInfo(*variant, runtimeVersion, out); - out << ",\n"; - } - - // Various paths. - auto &searchOpts = invocation.getSearchPathOptions(); - out << " \"paths\": {\n"; - - if (!searchOpts.SDKPath.empty()) { - out << " \"sdkPath\": \""; - out.write_escaped(searchOpts.SDKPath); - out << "\",\n"; - } - - auto outputPaths = [&](StringRef name, const std::vector &paths){ - out << " \"" << name << "\": [\n"; - llvm::interleave(paths, [&out](const std::string &path) { - out << " \""; - out.write_escaped(path); - out << "\""; - }, [&out] { - out << ",\n"; - }); - out << "\n ],\n"; - }; - - outputPaths("runtimeLibraryPaths", searchOpts.RuntimeLibraryPaths); - outputPaths("runtimeLibraryImportPaths", - searchOpts.RuntimeLibraryImportPaths); - - out << " \"runtimeResourcePath\": \""; - out.write_escaped(searchOpts.RuntimeResourcePath); - out << "\"\n"; - - out << " }\n"; - - out << "}\n"; -} - /// A PrettyStackTraceEntry to print frontend information useful for debugging. class PrettyStackTraceFrontend : public llvm::PrettyStackTraceEntry { const LangOptions &LangOpts; @@ -2115,7 +1981,7 @@ int swift::performFrontend(ArrayRef Args, } if (Invocation.getFrontendOptions().PrintTargetInfo) { - printTargetInfo(Invocation, llvm::outs()); + swift::targetinfo::printTargetInfo(Invocation, llvm::outs()); return finishDiagProcessing(0, /*verifierEnabled*/ false); } diff --git a/tools/libSwiftScan/libSwiftScan.cpp b/tools/libSwiftScan/libSwiftScan.cpp index 5953f04147352..58891f4f58565 100644 --- a/tools/libSwiftScan/libSwiftScan.cpp +++ b/tools/libSwiftScan/libSwiftScan.cpp @@ -501,6 +501,19 @@ static void addFrontendFlagOption(llvm::opt::OptTable &table, } } +swiftscan_string_ref_t +swiftscan_compiler_target_info_query(swiftscan_scan_invocation_t invocation) { + 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])); + + auto TargetInfo = getTargetInfo(Compilation); + if (TargetInfo.getError()) + return create_null(); + return TargetInfo.get(); +} + swiftscan_string_set_t * swiftscan_compiler_supported_arguments_query() { std::unique_ptr table = swift::createSwiftOptTable(); diff --git a/tools/libSwiftScan/libSwiftScan.exports b/tools/libSwiftScan/libSwiftScan.exports index 343c17038c9f6..23dcdaf05bf4a 100644 --- a/tools/libSwiftScan/libSwiftScan.exports +++ b/tools/libSwiftScan/libSwiftScan.exports @@ -55,6 +55,7 @@ swiftscan_import_set_dispose swiftscan_scanner_dispose swiftscan_compiler_supported_arguments_query swiftscan_compiler_supported_features_query +swiftscan_compiler_target_info_query swiftscan_scanner_cache_serialize swiftscan_scanner_cache_load swiftscan_scanner_cache_reset diff --git a/unittests/DependencyScan/CMakeLists.txt b/unittests/DependencyScan/CMakeLists.txt index 477fc711dc13b..08549d7a9bc2e 100644 --- a/unittests/DependencyScan/CMakeLists.txt +++ b/unittests/DependencyScan/CMakeLists.txt @@ -1,6 +1,7 @@ add_swift_unittest(swiftScanTests ScanFixture.cpp Features.cpp + PrintTarget.cpp ModuleDeps.cpp) target_link_libraries(swiftScanTests diff --git a/unittests/DependencyScan/PrintTarget.cpp b/unittests/DependencyScan/PrintTarget.cpp new file mode 100644 index 0000000000000..5e63987be1278 --- /dev/null +++ b/unittests/DependencyScan/PrintTarget.cpp @@ -0,0 +1,50 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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-c/DependencyScan/DependencyScan.h" +#include "swift/DependencyScan/StringUtils.h" +#include "swift/Basic/LLVM.h" +#include "swift/Option/Options.h" +#include "llvm/ADT/StringRef.h" +#include +#include +#include "gtest/gtest.h" + +using namespace swift; +using namespace swift::unittest; + +TEST_F(ScanTest, TestTargetInfoQuery) { + std::vector CommandStrArr = { + std::string("-print-target-info"), + std::string("-target"), std::string("x86_64-apple-macosx12.0")}; + // 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 Compilation; + for (auto &str : CommandStrArr) + Compilation.push_back(str.c_str()); + + auto targetInfo = getTargetInfo(Compilation); + if (targetInfo.getError()) { + llvm::errs() << "For compilation: "; + for (auto &str : Compilation) + llvm::errs() << str << " "; + llvm::errs() << "\nERROR:" << targetInfo.getError().message() << "\n"; + } + + auto targetInfoStr = std::string(get_C_string(targetInfo.get())); + EXPECT_NE(targetInfoStr.find("\"triple\": \"x86_64-apple-macosx12.0\""), std::string::npos); + EXPECT_NE(targetInfoStr.find("\"librariesRequireRPath\": false"), std::string::npos); +}