| //===------ DarwinToolChains.cpp - Job invocations (Darwin-specific) ------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2018 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 "ToolChains.h" |
| |
| #include "swift/Basic/Dwarf.h" |
| #include "swift/Basic/LLVM.h" |
| #include "swift/Basic/Platform.h" |
| #include "swift/Basic/Range.h" |
| #include "swift/Basic/STLExtras.h" |
| #include "swift/Basic/TaskQueue.h" |
| #include "swift/Config.h" |
| #include "swift/Driver/Compilation.h" |
| #include "swift/Driver/Driver.h" |
| #include "swift/Driver/Job.h" |
| #include "swift/Option/Options.h" |
| #include "clang/Basic/Version.h" |
| #include "clang/Driver/Util.h" |
| #include "llvm/ADT/StringSwitch.h" |
| #include "llvm/Option/Arg.h" |
| #include "llvm/Option/ArgList.h" |
| #include "llvm/ProfileData/InstrProf.h" |
| #include "llvm/Support/FileSystem.h" |
| #include "llvm/Support/Path.h" |
| #include "llvm/Support/Process.h" |
| #include "llvm/Support/Program.h" |
| |
| using namespace swift; |
| using namespace swift::driver; |
| using namespace llvm::opt; |
| |
| std::string |
| toolchains::Darwin::findProgramRelativeToSwiftImpl(StringRef name) const { |
| StringRef swiftPath = getDriver().getSwiftProgramPath(); |
| StringRef swiftBinDir = llvm::sys::path::parent_path(swiftPath); |
| |
| // See if we're in an Xcode toolchain. |
| bool hasToolchain = false; |
| llvm::SmallString<128> path{swiftBinDir}; |
| llvm::sys::path::remove_filename(path); // bin |
| llvm::sys::path::remove_filename(path); // usr |
| if (llvm::sys::path::extension(path) == ".xctoolchain") { |
| hasToolchain = true; |
| llvm::sys::path::remove_filename(path); // *.xctoolchain |
| llvm::sys::path::remove_filename(path); // Toolchains |
| llvm::sys::path::append(path, "usr", "bin"); |
| } |
| |
| StringRef paths[] = {swiftBinDir, path}; |
| auto pathsRef = llvm::makeArrayRef(paths); |
| if (!hasToolchain) |
| pathsRef = pathsRef.drop_back(); |
| |
| auto result = llvm::sys::findProgramByName(name, pathsRef); |
| if (result) |
| return result.get(); |
| return {}; |
| } |
| |
| ToolChain::InvocationInfo |
| toolchains::Darwin::constructInvocation(const InterpretJobAction &job, |
| const JobContext &context) const { |
| InvocationInfo II = ToolChain::constructInvocation(job, context); |
| |
| SmallString<128> runtimeLibraryPath; |
| getRuntimeLibraryPath(runtimeLibraryPath, context.Args, /*Shared=*/true); |
| |
| addPathEnvironmentVariableIfNeeded(II.ExtraEnvironment, "DYLD_LIBRARY_PATH", |
| ":", options::OPT_L, context.Args, |
| runtimeLibraryPath); |
| addPathEnvironmentVariableIfNeeded(II.ExtraEnvironment, "DYLD_FRAMEWORK_PATH", |
| ":", options::OPT_F, context.Args); |
| // FIXME: Add options::OPT_Fsystem paths to DYLD_FRAMEWORK_PATH as well. |
| return II; |
| } |
| |
| static StringRef |
| getDarwinLibraryNameSuffixForTriple(const llvm::Triple &triple, |
| bool distinguishSimulator = true) { |
| const DarwinPlatformKind kind = getDarwinPlatformKind(triple); |
| const DarwinPlatformKind effectiveKind = |
| distinguishSimulator ? kind : getNonSimulatorPlatform(kind); |
| switch (effectiveKind) { |
| case DarwinPlatformKind::MacOS: |
| return "osx"; |
| case DarwinPlatformKind::IPhoneOS: |
| return "ios"; |
| case DarwinPlatformKind::IPhoneOSSimulator: |
| return "iossim"; |
| case DarwinPlatformKind::TvOS: |
| return "tvos"; |
| case DarwinPlatformKind::TvOSSimulator: |
| return "tvossim"; |
| case DarwinPlatformKind::WatchOS: |
| return "watchos"; |
| case DarwinPlatformKind::WatchOSSimulator: |
| return "watchossim"; |
| } |
| llvm_unreachable("Unsupported Darwin platform"); |
| } |
| |
| std::string toolchains::Darwin::sanitizerRuntimeLibName(StringRef Sanitizer, |
| bool shared) const { |
| return (Twine("libclang_rt.") + Sanitizer + "_" + |
| getDarwinLibraryNameSuffixForTriple(this->getTriple()) + |
| (shared ? "_dynamic.dylib" : ".a")) |
| .str(); |
| } |
| |
| static void addLinkRuntimeLibRPath(const ArgList &Args, |
| ArgStringList &Arguments, |
| StringRef DarwinLibName, |
| const ToolChain &TC) { |
| // Adding the rpaths might negatively interact when other rpaths are involved, |
| // so we should make sure we add the rpaths last, after all user-specified |
| // rpaths. This is currently true from this place, but we need to be |
| // careful if this function is ever called before user's rpaths are emitted. |
| assert(DarwinLibName.endswith(".dylib") && "must be a dynamic library"); |
| |
| // Add @executable_path to rpath to support having the dylib copied with |
| // the executable. |
| Arguments.push_back("-rpath"); |
| Arguments.push_back("@executable_path"); |
| |
| // Add the path to the resource dir to rpath to support using the dylib |
| // from the default location without copying. |
| |
| SmallString<128> ClangLibraryPath; |
| TC.getClangLibraryPath(Args, ClangLibraryPath); |
| |
| Arguments.push_back("-rpath"); |
| Arguments.push_back(Args.MakeArgString(ClangLibraryPath)); |
| } |
| |
| static void addLinkSanitizerLibArgsForDarwin(const ArgList &Args, |
| ArgStringList &Arguments, |
| StringRef Sanitizer, |
| const ToolChain &TC, |
| bool shared = true) { |
| // Sanitizer runtime libraries requires C++. |
| Arguments.push_back("-lc++"); |
| // Add explicit dependency on -lc++abi, as -lc++ doesn't re-export |
| // all RTTI-related symbols that are used. |
| Arguments.push_back("-lc++abi"); |
| |
| auto LibName = TC.sanitizerRuntimeLibName(Sanitizer, shared); |
| TC.addLinkRuntimeLib(Args, Arguments, LibName); |
| |
| if (shared) |
| addLinkRuntimeLibRPath(Args, Arguments, LibName, TC); |
| } |
| |
| /// Runs <code>xcrun -f clang</code> in order to find the location of Clang for |
| /// the currently active Xcode. |
| /// |
| /// We get the "currently active" part by passing through the DEVELOPER_DIR |
| /// environment variable (along with the rest of the environment). |
| static bool findXcodeClangPath(llvm::SmallVectorImpl<char> &path) { |
| assert(path.empty()); |
| |
| auto xcrunPath = llvm::sys::findProgramByName("xcrun"); |
| if (!xcrunPath.getError()) { |
| // Explicitly ask for the default toolchain so that we don't find a Clang |
| // included with an open-source toolchain. |
| const char *args[] = {"-toolchain", "default", "-f", "clang", nullptr}; |
| sys::TaskQueue queue; |
| queue.addTask(xcrunPath->c_str(), args, /*Env=*/llvm::None, |
| /*Context=*/nullptr, |
| /*SeparateErrors=*/true); |
| queue.execute(nullptr, |
| [&path](sys::ProcessId PID, int returnCode, StringRef output, |
| StringRef errors, |
| sys::TaskProcessInformation ProcInfo, |
| void *unused) -> sys::TaskFinishedResponse { |
| if (returnCode == 0) { |
| output = output.rtrim(); |
| path.append(output.begin(), output.end()); |
| } |
| return sys::TaskFinishedResponse::ContinueExecution; |
| }); |
| } |
| |
| return !path.empty(); |
| } |
| |
| static void addVersionString(const ArgList &inputArgs, ArgStringList &arguments, |
| unsigned major, unsigned minor, unsigned micro) { |
| llvm::SmallString<8> buf; |
| llvm::raw_svector_ostream os{buf}; |
| os << major << '.' << minor << '.' << micro; |
| arguments.push_back(inputArgs.MakeArgString(os.str())); |
| } |
| |
| /// Returns true if the compiler depends on features provided by the ObjC |
| /// runtime that are not present on the deployment target indicated by |
| /// \p triple. |
| static bool wantsObjCRuntime(const llvm::Triple &triple) { |
| assert((!triple.isTvOS() || triple.isiOS()) && |
| "tvOS is considered a kind of iOS"); |
| |
| // When updating the versions listed here, please record the most recent |
| // feature being depended on and when it was introduced: |
| // |
| // - Make assigning 'nil' to an NSMutableDictionary subscript delete the |
| // entry, like it does for Swift.Dictionary, rather than trap. |
| if (triple.isiOS()) |
| return triple.isOSVersionLT(9); |
| if (triple.isMacOSX()) |
| return triple.isMacOSXVersionLT(10, 11); |
| if (triple.isWatchOS()) |
| return false; |
| llvm_unreachable("unknown Darwin OS"); |
| } |
| |
| ToolChain::InvocationInfo |
| toolchains::Darwin::constructInvocation(const LinkJobAction &job, |
| const JobContext &context) const { |
| assert(context.Output.getPrimaryOutputType() == file_types::TY_Image && |
| "Invalid linker output type."); |
| |
| if (context.Args.hasFlag(options::OPT_static_executable, |
| options::OPT_no_static_executable, false)) { |
| llvm::report_fatal_error("-static-executable is not supported on Darwin"); |
| } |
| |
| const Driver &D = getDriver(); |
| const llvm::Triple &Triple = getTriple(); |
| |
| // Configure the toolchain. |
| // By default, use the system `ld` to link. |
| const char *LD = "ld"; |
| if (const Arg *A = context.Args.getLastArg(options::OPT_tools_directory)) { |
| StringRef toolchainPath(A->getValue()); |
| |
| // If there is a 'ld' in the toolchain folder, use that instead. |
| if (auto toolchainLD = |
| llvm::sys::findProgramByName("ld", {toolchainPath})) { |
| LD = context.Args.MakeArgString(toolchainLD.get()); |
| } |
| } |
| |
| InvocationInfo II = {LD}; |
| ArgStringList &Arguments = II.Arguments; |
| |
| if (context.shouldUseInputFileList()) { |
| Arguments.push_back("-filelist"); |
| Arguments.push_back(context.getTemporaryFilePath("inputs", "LinkFileList")); |
| II.FilelistInfos.push_back({Arguments.back(), file_types::TY_Object, |
| FilelistInfo::WhichFiles::Input}); |
| } else { |
| addPrimaryInputsOfType(Arguments, context.Inputs, context.Args, |
| file_types::TY_Object); |
| } |
| |
| addInputsOfType(Arguments, context.InputActions, file_types::TY_Object); |
| |
| if (context.OI.CompilerMode == OutputInfo::Mode::SingleCompile) |
| addInputsOfType(Arguments, context.Inputs, context.Args, |
| file_types::TY_SwiftModuleFile, "-add_ast_path"); |
| else |
| addPrimaryInputsOfType(Arguments, context.Inputs, context.Args, |
| file_types::TY_SwiftModuleFile, "-add_ast_path"); |
| |
| // Add all .swiftmodule file inputs as arguments, preceded by the |
| // "-add_ast_path" linker option. |
| addInputsOfType(Arguments, context.InputActions, |
| file_types::TY_SwiftModuleFile, "-add_ast_path"); |
| |
| switch (job.getKind()) { |
| case LinkKind::None: |
| llvm_unreachable("invalid link kind"); |
| case LinkKind::Executable: |
| // The default for ld; no extra flags necessary. |
| break; |
| case LinkKind::DynamicLibrary: |
| Arguments.push_back("-dylib"); |
| break; |
| } |
| |
| assert(Triple.isOSDarwin()); |
| |
| // FIXME: If we used Clang as a linker instead of going straight to ld, |
| // we wouldn't have to replicate a bunch of Clang's logic here. |
| |
| // Always link the regular compiler_rt if it's present. |
| // |
| // Note: Normally we'd just add this unconditionally, but it's valid to build |
| // Swift and use it as a linker without building compiler_rt. |
| SmallString<128> CompilerRTPath; |
| getClangLibraryPath(context.Args, CompilerRTPath); |
| llvm::sys::path::append( |
| CompilerRTPath, |
| Twine("libclang_rt.") + |
| getDarwinLibraryNameSuffixForTriple(Triple, /*simulator*/false) + |
| ".a"); |
| if (llvm::sys::fs::exists(CompilerRTPath)) |
| Arguments.push_back(context.Args.MakeArgString(CompilerRTPath)); |
| |
| if (context.Args.hasFlag(options::OPT_link_objc_runtime, |
| options::OPT_no_link_objc_runtime, |
| /*Default=*/wantsObjCRuntime(Triple))) { |
| llvm::SmallString<128> ARCLiteLib(D.getSwiftProgramPath()); |
| llvm::sys::path::remove_filename(ARCLiteLib); // 'swift' |
| llvm::sys::path::remove_filename(ARCLiteLib); // 'bin' |
| llvm::sys::path::append(ARCLiteLib, "lib", "arc"); |
| |
| if (!llvm::sys::fs::is_directory(ARCLiteLib)) { |
| // If we don't have a 'lib/arc/' directory, find the "arclite" library |
| // relative to the Clang in the active Xcode. |
| ARCLiteLib.clear(); |
| if (findXcodeClangPath(ARCLiteLib)) { |
| llvm::sys::path::remove_filename(ARCLiteLib); // 'clang' |
| llvm::sys::path::remove_filename(ARCLiteLib); // 'bin' |
| llvm::sys::path::append(ARCLiteLib, "lib", "arc"); |
| } |
| } |
| |
| if (!ARCLiteLib.empty()) { |
| llvm::sys::path::append(ARCLiteLib, "libarclite_"); |
| ARCLiteLib += getPlatformNameForTriple(Triple); |
| ARCLiteLib += ".a"; |
| |
| Arguments.push_back("-force_load"); |
| Arguments.push_back(context.Args.MakeArgString(ARCLiteLib)); |
| |
| // Arclite depends on CoreFoundation. |
| Arguments.push_back("-framework"); |
| Arguments.push_back("CoreFoundation"); |
| } else { |
| // FIXME: We should probably diagnose this, but this is not a place where |
| // we can emit diagnostics. Silently ignore it for now. |
| } |
| } |
| |
| for (const Arg *arg : |
| context.Args.filtered(options::OPT_F, options::OPT_Fsystem)) { |
| Arguments.push_back("-F"); |
| Arguments.push_back(arg->getValue()); |
| } |
| |
| if (context.Args.hasArg(options::OPT_enable_app_extension)) { |
| // Keep this string fixed in case the option used by the |
| // compiler itself changes. |
| Arguments.push_back("-application_extension"); |
| } |
| |
| // Linking sanitizers will add rpaths, which might negatively interact when |
| // other rpaths are involved, so we should make sure we add the rpaths after |
| // all user-specified rpaths. |
| if (context.OI.SelectedSanitizers & SanitizerKind::Address) |
| addLinkSanitizerLibArgsForDarwin(context.Args, Arguments, "asan", *this); |
| |
| if (context.OI.SelectedSanitizers & SanitizerKind::Thread) |
| addLinkSanitizerLibArgsForDarwin(context.Args, Arguments, "tsan", *this); |
| |
| if (context.OI.SelectedSanitizers & SanitizerKind::Undefined) |
| addLinkSanitizerLibArgsForDarwin(context.Args, Arguments, "ubsan", *this); |
| |
| // Only link in libFuzzer for executables. |
| if (job.getKind() == LinkKind::Executable && |
| (context.OI.SelectedSanitizers & SanitizerKind::Fuzzer)) |
| addLinkSanitizerLibArgsForDarwin(context.Args, Arguments, "fuzzer", *this, |
| /*shared=*/false); |
| |
| if (context.Args.hasArg(options::OPT_embed_bitcode, |
| options::OPT_embed_bitcode_marker)) { |
| Arguments.push_back("-bitcode_bundle"); |
| } |
| |
| if (!context.OI.SDKPath.empty()) { |
| Arguments.push_back("-syslibroot"); |
| Arguments.push_back(context.Args.MakeArgString(context.OI.SDKPath)); |
| } |
| |
| Arguments.push_back("-lobjc"); |
| Arguments.push_back("-lSystem"); |
| |
| Arguments.push_back("-arch"); |
| Arguments.push_back(context.Args.MakeArgString(getTriple().getArchName())); |
| |
| // Add the runtime library link path, which is platform-specific and found |
| // relative to the compiler. |
| SmallString<128> RuntimeLibPath; |
| getRuntimeLibraryPath(RuntimeLibPath, context.Args, /*Shared=*/true); |
| |
| // Link the standard library. |
| Arguments.push_back("-L"); |
| if (context.Args.hasFlag(options::OPT_static_stdlib, |
| options::OPT_no_static_stdlib, false)) { |
| SmallString<128> StaticRuntimeLibPath; |
| getRuntimeLibraryPath(StaticRuntimeLibPath, context.Args, /*Shared=*/false); |
| Arguments.push_back(context.Args.MakeArgString(StaticRuntimeLibPath)); |
| Arguments.push_back("-lc++"); |
| Arguments.push_back("-framework"); |
| Arguments.push_back("Foundation"); |
| Arguments.push_back("-force_load_swift_libs"); |
| } else { |
| Arguments.push_back(context.Args.MakeArgString(RuntimeLibPath)); |
| // FIXME: We probably shouldn't be adding an rpath here unless we know ahead |
| // of time the standard library won't be copied. SR-1967 |
| Arguments.push_back("-rpath"); |
| Arguments.push_back(context.Args.MakeArgString(RuntimeLibPath)); |
| } |
| |
| if (context.Args.hasArg(options::OPT_profile_generate)) { |
| SmallString<128> LibProfile; |
| getClangLibraryPath(context.Args, LibProfile); |
| |
| StringRef RT; |
| if (Triple.isiOS()) { |
| if (Triple.isTvOS()) |
| RT = "tvos"; |
| else |
| RT = "ios"; |
| } else if (Triple.isWatchOS()) { |
| RT = "watchos"; |
| } else { |
| assert(Triple.isMacOSX()); |
| RT = "osx"; |
| } |
| |
| StringRef Sim; |
| if (tripleIsAnySimulator(Triple)) { |
| Sim = "sim"; |
| } |
| |
| llvm::sys::path::append(LibProfile, |
| "libclang_rt.profile_" + RT + Sim + ".a"); |
| |
| // FIXME: Continue accepting the old path for simulator libraries for now. |
| if (!Sim.empty() && !llvm::sys::fs::exists(LibProfile)) { |
| llvm::sys::path::remove_filename(LibProfile); |
| llvm::sys::path::append(LibProfile, "libclang_rt.profile_" + RT + ".a"); |
| } |
| |
| Arguments.push_back(context.Args.MakeArgString(LibProfile)); |
| } |
| |
| // FIXME: Properly handle deployment targets. |
| assert(Triple.isiOS() || Triple.isWatchOS() || Triple.isMacOSX()); |
| if (Triple.isiOS()) { |
| bool isiOSSimulator = tripleIsiOSSimulator(Triple); |
| if (Triple.isTvOS()) { |
| if (isiOSSimulator) |
| Arguments.push_back("-tvos_simulator_version_min"); |
| else |
| Arguments.push_back("-tvos_version_min"); |
| } else { |
| if (isiOSSimulator) |
| Arguments.push_back("-ios_simulator_version_min"); |
| else |
| Arguments.push_back("-iphoneos_version_min"); |
| } |
| unsigned major, minor, micro; |
| Triple.getiOSVersion(major, minor, micro); |
| addVersionString(context.Args, Arguments, major, minor, micro); |
| } else if (Triple.isWatchOS()) { |
| if (tripleIsWatchSimulator(Triple)) |
| Arguments.push_back("-watchos_simulator_version_min"); |
| else |
| Arguments.push_back("-watchos_version_min"); |
| unsigned major, minor, micro; |
| Triple.getOSVersion(major, minor, micro); |
| addVersionString(context.Args, Arguments, major, minor, micro); |
| } else { |
| Arguments.push_back("-macosx_version_min"); |
| unsigned major, minor, micro; |
| Triple.getMacOSXVersion(major, minor, micro); |
| addVersionString(context.Args, Arguments, major, minor, micro); |
| } |
| |
| Arguments.push_back("-no_objc_category_merging"); |
| |
| // These custom arguments should be right before the object file at the end. |
| context.Args.AddAllArgs(Arguments, options::OPT_linker_option_Group); |
| context.Args.AddAllArgValues(Arguments, options::OPT_Xlinker); |
| |
| // This should be the last option, for convenience in checking output. |
| Arguments.push_back("-o"); |
| Arguments.push_back( |
| context.Args.MakeArgString(context.Output.getPrimaryOutputFilename())); |
| |
| return II; |
| } |
| |
| bool toolchains::Darwin::shouldStoreInvocationInDebugInfo() const { |
| // This matches the behavior in Clang (see |
| // clang/lib/driver/ToolChains/Darwin.cpp). |
| if (const char *S = ::getenv("RC_DEBUG_OPTIONS")) |
| return S[0] != '\0'; |
| return false; |
| } |