//===-- tool.cpp ----------------------------------------------------------===// // // LDC – the LLVM D compiler // // This file is distributed under the BSD-style LDC license. See the LICENSE // file for details. // //===----------------------------------------------------------------------===// #include "driver/tool.h" #include "dmd/errors.h" #include "dmd/vsoptions.h" #include "driver/args.h" #include "driver/cl_options.h" #include "driver/exe_path.h" #include "driver/targetmachine.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Target/TargetMachine.h" #ifdef _WIN32 #include #endif ////////////////////////////////////////////////////////////////////////////// namespace opts { llvm::cl::opt linker( "linker", llvm::cl::ZeroOrMore, llvm::cl::value_desc("lld-link|lld|gold|bfd|..."), llvm::cl::desc("Set the linker to use. When explicitly set to '' " "(nothing), prevents LDC from passing `-fuse-ld` to `cc`."), llvm::cl::cat(opts::linkingCategory)); } static llvm::cl::opt gcc("gcc", llvm::cl::ZeroOrMore, llvm::cl::cat(opts::linkingCategory), llvm::cl::value_desc("gcc|clang|..."), llvm::cl::desc( "C compiler to use for linking (and external assembling). Defaults " "to the CC environment variable if set, otherwise to `cc`.")); ////////////////////////////////////////////////////////////////////////////// static std::string findProgramByName(llvm::StringRef name) { llvm::ErrorOr res = llvm::sys::findProgramByName(name); return res ? res.get() : std::string(); } ////////////////////////////////////////////////////////////////////////////// std::string getProgram(const char *fallbackName, const llvm::cl::opt *opt, const char *envVar) { std::string name; if (opt && !opt->empty()) { name = *opt; } else { if (envVar) name = env::get(envVar); if (name.empty()) // no or empty env var name = fallbackName; } const std::string path = findProgramByName(name); if (path.empty()) { error(Loc(), "cannot find program `%s`", name.c_str()); fatal(); } return path; } //////////////////////////////////////////////////////////////////////////////// std::string getGcc(std::vector &additional_args, const char *fallback) { #ifdef _WIN32 // spaces in $CC are to be expected on Windows // (e.g., `C:\Program Files\LLVM\bin\clang-cl.exe`) return getProgram(fallback, &gcc, "CC"); #else // Posix: in case $CC contains spaces split it into a command and arguments std::string cc = env::get("CC"); if (cc.empty()) return getProgram(fallback, &gcc); // $CC is set so fallback doesn't matter anymore. if (cc.find(' ') == cc.npos) return getProgram(cc.c_str(), &gcc); llvm::StringRef sr(cc); llvm::SmallVector args; sr.split(args, ' ', /*MaxSplit=*/-1, /*keepEmpty=*/false); // args[0] == CC command, args[1..] = CLI options additional_args.reserve(additional_args.size() + args.size() - 1); for (size_t i = 1; i < args.size(); i ++) additional_args.emplace_back(args[i].str()); return getProgram(args[0].str().c_str(), &gcc); #endif } //////////////////////////////////////////////////////////////////////////////// void appendTargetArgsForGcc(std::vector &args) { using llvm::Triple; const auto &triple = *global.params.targetTriple; const auto arch64 = triple.get64BitArchVariant().getArch(); switch (arch64) { // Specify -m32/-m64 for architectures where gcc supports those flags. case Triple::x86_64: case Triple::ppc64: case Triple::ppc64le: case Triple::sparcv9: case Triple::nvptx64: args.push_back(triple.isArch64Bit() ? "-m64" : "-m32"); return; #if LDC_LLVM_VER >= 1600 // LoongArch does not use -m32/-m64 and uses -mabi=. case Triple::loongarch64: args.emplace_back(triple.isArch64Bit() ? "-mabi=lp64d" : "-mabi=ilp32d"); return; #endif // LDC_LLVM_VER >= 1600 // MIPS does not have -m32/-m64 but requires -mabi=. case Triple::mips64: case Triple::mips64el: switch (getMipsABI()) { case MipsABI::EABI: args.push_back("-mabi=eabi"); args.push_back("-march=mips32r2"); break; case MipsABI::O32: args.push_back("-mabi=32"); args.push_back("-march=mips32r2"); break; case MipsABI::N32: args.push_back("-mabi=n32"); args.push_back("-march=mips64r2"); break; case MipsABI::N64: args.push_back("-mabi=64"); args.push_back("-march=mips64r2"); break; case MipsABI::Unknown: break; } return; case Triple::riscv64: { extern llvm::TargetMachine* gTargetMachine; const auto featuresStr = gTargetMachine->getTargetFeatureString(); llvm::SmallVector features; featuresStr.split(features, ",", -1, false); const std::string mabi = getABI(triple, features); args.push_back("-mabi=" + mabi); std::string march = triple.isArch64Bit() ? "rv64" : "rv32"; const bool m = isFeatureEnabled(features, "m"); const bool a = isFeatureEnabled(features, "a"); const bool f = isFeatureEnabled(features, "f"); const bool d = isFeatureEnabled(features, "d"); const bool c = isFeatureEnabled(features, "c"); bool g = false; if (m && a && f && d) { march += "g"; g = true; } else { march += "i"; if (m) march += "m"; if (a) march += "a"; if (f) march += "f"; if (d) march += "d"; } if (c) march += "c"; if (!g) march += "_zicsr_zifencei"; args.push_back("-march=" + march); return; } default: break; } } ////////////////////////////////////////////////////////////////////////////// void createDirectoryForFileOrFail(llvm::StringRef fileName) { auto dir = llvm::sys::path::parent_path(fileName); if (!dir.empty() && !llvm::sys::fs::exists(dir)) { if (auto ec = llvm::sys::fs::create_directories(dir)) { error(Loc(), "failed to create path to file: %s\n%s", dir.data(), ec.message().c_str()); fatal(); } } } //////////////////////////////////////////////////////////////////////////////// std::vector getFullArgs(const char *tool, const std::vector &args, bool printVerbose) { std::vector fullArgs; fullArgs.reserve(args.size() + 2); // args::executeAndWait() may append an additional null fullArgs.push_back(tool); for (const auto &arg : args) fullArgs.push_back(arg.c_str()); // Print command line if requested if (printVerbose) { llvm::SmallString<256> singleString; for (auto arg : fullArgs) { singleString += arg; singleString += ' '; } message("%s", singleString.c_str()); } return fullArgs; } //////////////////////////////////////////////////////////////////////////////// int executeToolAndWait(const Loc &loc, const std::string &tool_, const std::vector &args, bool verbose) { const auto tool = findProgramByName(tool_); if (tool.empty()) { error(loc, "cannot find program `%s`", tool_.c_str()); return -1; } // Construct real argument list; first entry is the tool itself. auto fullArgs = getFullArgs(tool.c_str(), args, verbose); // We may need a response file to overcome cmdline limits, especially on Windows. auto rspEncoding = llvm::sys::WEM_UTF8; #ifdef _WIN32 // MSVC tools (link.exe etc.) apparently require UTF-16 encoded response files auto triple = global.params.targetTriple; if (triple && triple->isWindowsMSVCEnvironment()) rspEncoding = llvm::sys::WEM_UTF16; #endif // Execute tool. std::string errorMsg; const int status = args::executeAndWait(std::move(fullArgs), rspEncoding, &errorMsg); if (status) { error(loc, "%s failed with status: %d", tool.c_str(), status); if (!errorMsg.empty()) { errorSupplemental(loc, "message: %s", errorMsg.c_str()); } } return status; } //////////////////////////////////////////////////////////////////////////////// #ifdef _WIN32 namespace windows { namespace { bool setupMsvcEnvironmentImpl( bool forPreprocessingOnly, std::vector> *rollback) { const bool x64 = global.params.targetTriple->isArch64Bit(); if (env::has(L"VSINSTALLDIR") && !env::has(L"LDC_VSDIR_FORCE")) { // Assume a fully set up environment (e.g., VS native tools command prompt). // Skip the MSVC setup unless the environment is set up for a different // target architecture. const auto tgtArch = env::get("VSCMD_ARG_TGT_ARCH"); // VS 2017+ if (tgtArch.empty() || tgtArch == (x64 ? "x64" : "x86")) return true; } const auto begin = std::chrono::steady_clock::now(); static VSOptions vsOptions; // cache, as this can be expensive if (!vsOptions.VSInstallDir) { vsOptions.initialize(); if (!vsOptions.VSInstallDir) return false; } // cache the environment variable prefixes too static llvm::SmallVector binPaths; static llvm::SmallVector includePaths; static llvm::SmallVector libPaths; if (binPaths.empty()) { // PATH const char *secondaryBindir = nullptr; if (auto bindir = vsOptions.getVCBinDir(x64, secondaryBindir)) { binPaths.push_back(bindir); if (secondaryBindir) binPaths.push_back(secondaryBindir); } else { return false; } } if (forPreprocessingOnly && includePaths.empty()) { // INCLUDE if (auto vcincludedir = vsOptions.getVCIncludeDir()) { includePaths.push_back(vcincludedir); } else { return false; } if (auto sdkincludedir = vsOptions.getSDKIncludePath()) { includePaths.push_back(FileName::combine(sdkincludedir, "ucrt")); includePaths.push_back(FileName::combine(sdkincludedir, "shared")); includePaths.push_back(FileName::combine(sdkincludedir, "um")); includePaths.push_back(FileName::combine(sdkincludedir, "winrt")); includePaths.push_back(FileName::combine(sdkincludedir, "cppwinrt")); } else { includePaths.clear(); return false; } } if (!forPreprocessingOnly && libPaths.empty()) { // LIB if (auto vclibdir = vsOptions.getVCLibDir(x64)) libPaths.push_back(vclibdir); if (auto ucrtlibdir = vsOptions.getUCRTLibPath(x64)) libPaths.push_back(ucrtlibdir); if (auto sdklibdir = vsOptions.getSDKLibPath(x64)) libPaths.push_back(sdklibdir); if (libPaths.size() != 3) { libPaths.clear(); return false; } } if (!rollback) // check for availability only return true; if (global.params.v.verbose) message("Prepending to environment variables:"); const auto prependToEnvVar = [rollback](const char *key, const wchar_t *wkey, const llvm::SmallVectorImpl &entries) { if (entries.empty()) return; wchar_t *originalValue = _wgetenv(wkey); llvm::SmallString<256> head; for (const char *entry : entries) { if (!head.empty()) head += ';'; head += entry; } if (global.params.v.verbose) message(" %s += %.*s", key, (int)head.size(), head.data()); llvm::SmallVector wvalue; llvm::sys::windows::UTF8ToUTF16(head, wvalue); if (originalValue) { wvalue.push_back(L';'); wvalue.append(originalValue, originalValue + wcslen(originalValue)); } wvalue.push_back(0); // copy the original value, if set if (originalValue) originalValue = wcsdup(originalValue); rollback->emplace_back(wkey, originalValue); SetEnvironmentVariableW(wkey, wvalue.data()); }; rollback->reserve(2); prependToEnvVar("INCLUDE", L"INCLUDE", includePaths); prependToEnvVar("LIB", L"LIB", libPaths); prependToEnvVar("PATH", L"PATH", binPaths); if (global.params.v.verbose) { const auto end = std::chrono::steady_clock::now(); message("MSVC setup took %lld microseconds", std::chrono::duration_cast(end - begin) .count()); } return true; } } // anonymous namespace bool isMsvcAvailable() { return setupMsvcEnvironmentImpl(false, nullptr); } bool MsvcEnvironmentScope::setup(bool forPreprocessingOnly) { rollback.clear(); return setupMsvcEnvironmentImpl(forPreprocessingOnly, &rollback); } MsvcEnvironmentScope::~MsvcEnvironmentScope() { for (const auto &pair : rollback) { SetEnvironmentVariableW(pair.first.c_str(), pair.second); free(pair.second); } } } // namespace windows #endif // _WIN32