//===------ 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/TaskQueue.h" #include "swift/Config.h" #include "swift/Driver/Compilation.h" #include "swift/Driver/Driver.h" #include "swift/Driver/Job.h" #include "swift/Frontend/Frontend.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) { switch (getDarwinPlatformKind(triple)) { 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 xcrun -f clang 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 &path) { assert(path.empty()); auto xcrunPath = llvm::sys::findProgramByName("xcrun"); if (!xcrunPath.getError()) { const char *args[] = {"-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, 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())); } 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 Clang's logic here. bool wantsObjCRuntime = false; if (Triple.isiOS()) wantsObjCRuntime = Triple.isOSVersionLT(9); else if (Triple.isMacOSX()) wantsObjCRuntime = Triple.isMacOSXVersionLT(10, 11); if (context.Args.hasFlag(options::OPT_link_objc_runtime, options::OPT_no_link_objc_runtime, /*Default=*/wantsObjCRuntime)) { 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. } } context.Args.AddAllArgValues(Arguments, options::OPT_Xlinker); context.Args.AddAllArgs(Arguments, options::OPT_linker_option_Group); 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); // 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(RuntimeLibPath); llvm::sys::path::remove_filename(LibProfile); // remove platform name llvm::sys::path::append(LibProfile, "clang", "lib", "darwin"); 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"); // 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; }