diff --git a/CHANGELOG.md b/CHANGELOG.md index 7939691fe4..286d441e05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ - Frontend, druntime and Phobos are at version [2.103.1](https://dlang.org/changelog/2.103.0.html), incl. new command-line option `-verror-supplements`. (#4345) - New commandline option `-femit-local-var-lifetime` that enables variable lifetime (scope) annotation to LLVM IR codegen. Lifetime annotation enables stack memory reuse for local variables with non-overlapping scope. (#4395) - C files are now automatically preprocessed using the external C compiler (configurable via `-gcc` or the `CC` environment variable, and `-Xcc` for extra flags). Extra preprocessor flags (e.g., include dirs and manual defines) can be added via new command-line option `-P`. (#4417) - - Windows: Requires an MSVC environment set up for the target (`cl.exe` on `PATH`, `INCLUDE` environment variable). If `clang-cl.exe` is on `PATH`, it is preferred over Microsoft's `cl.exe` by default (e.g., to avoid printing the C source file path to stderr during preprocessing). + - Windows: If `clang-cl.exe` is on `PATH`, it is preferred over Microsoft's `cl.exe` by default (e.g., to avoid printing the C source file path to stderr during preprocessing). #### Platform support diff --git a/dmd/vsoptions.d b/dmd/vsoptions.d index 2a363ceeed..afb235ffa4 100644 --- a/dmd/vsoptions.d +++ b/dmd/vsoptions.d @@ -486,6 +486,21 @@ public: return FileName.exists(proposed) ? proposed : null; } +version (IN_LLVM) +{ + const(char)* getVCIncludeDir() const + { + const(char)* proposed; + + if (VCToolsInstallDir !is null) + proposed = FileName.combine(VCToolsInstallDir, "include"); + else if (VCInstallDir !is null) + proposed = FileName.combine(VCInstallDir, "include"); + + return FileName.exists(proposed) ? proposed : null; + } +} + /** * get the path to the universal CRT libraries * Params: @@ -546,6 +561,33 @@ version (IN_LLVM) {} else return null; } +version (IN_LLVM) +{ + const(char)* getSDKIncludePath() const + { + if (WindowsSdkDir) + { + alias umExists = returnDirIfContainsFile!"um"; // subdir in this case + + const sdk = FileName.combine(WindowsSdkDir.toDString, "include"); + if (WindowsSdkVersion) + { + if (auto p = umExists(sdk, WindowsSdkVersion.toDString)) // SDK 10.0 + return p; + } + // purely speculative + if (auto p = umExists(sdk, "win8")) // SDK 8.0 + return p; + if (auto p = umExists(sdk, "winv6.3")) // SDK 8.1 + return p; + if (auto p = umExists(sdk)) // SDK 7.1 or earlier + return p; + } + + return null; + } +} + private: extern(D): diff --git a/dmd/vsoptions.h b/dmd/vsoptions.h index fc63e1c1b9..b39601bf92 100644 --- a/dmd/vsoptions.h +++ b/dmd/vsoptions.h @@ -25,8 +25,14 @@ struct VSOptions void initialize(); const char *getVCBinDir(bool x64, const char *&addpath) const; const char *getVCLibDir(bool x64) const; +#if IN_LLVM + const char *getVCIncludeDir() const; +#endif const char *getUCRTLibPath(bool x64) const; const char *getSDKLibPath(bool x64) const; +#if IN_LLVM + const char *getSDKIncludePath() const; +#endif }; #endif // _WIN32 diff --git a/driver/cpreprocessor.cpp b/driver/cpreprocessor.cpp index 0e9ce48e24..b603a4b22c 100644 --- a/driver/cpreprocessor.cpp +++ b/driver/cpreprocessor.cpp @@ -73,11 +73,10 @@ FileName runCPreprocessor(FileName csrcfile, const Loc &loc, bool &ifile, const auto &triple = *global.params.targetTriple; const bool isMSVC = triple.isWindowsMSVCEnvironment(); -#if 0 //ifdef _WIN32 - // TODO: INCLUDE env var etc.? +#ifdef _WIN32 windows::MsvcEnvironmentScope msvcEnv; if (isMSVC) - msvcEnv.setup(); + msvcEnv.setup(/*forPreprocessingOnly=*/true); #endif FileName ipath = getOutputPath(loc, csrcfile.toChars()); diff --git a/driver/tool.cpp b/driver/tool.cpp index d3f4cd150d..c6e43b13ab 100644 --- a/driver/tool.cpp +++ b/driver/tool.cpp @@ -265,7 +265,10 @@ int executeToolAndWait(const Loc &loc, const std::string &tool_, namespace windows { namespace { +VSOptions vsOptions; // cache, as this can be expensive + bool setupMsvcEnvironmentImpl( + bool forPreprocessingOnly, std::vector> *rollback) { const bool x64 = global.params.targetTriple->isArch64Bit(); @@ -280,19 +283,13 @@ bool setupMsvcEnvironmentImpl( const auto begin = std::chrono::steady_clock::now(); - VSOptions vsOptions; - vsOptions.initialize(); - if (!vsOptions.VSInstallDir) - return false; - - llvm::SmallVector libPaths; - 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 (!vsOptions.VSInstallDir) { + vsOptions.initialize(); + if (!vsOptions.VSInstallDir) + return false; + } + // PATH llvm::SmallVector binPaths; const char *secondaryBindir = nullptr; if (auto bindir = vsOptions.getVCBinDir(x64, secondaryBindir)) { @@ -300,20 +297,52 @@ bool setupMsvcEnvironmentImpl( if (secondaryBindir) binPaths.push_back(secondaryBindir); } - - const bool success = libPaths.size() == 3 && !binPaths.empty(); - if (!success) + if (binPaths.empty()) return false; + llvm::SmallVector includePaths; + llvm::SmallVector libPaths; + if (forPreprocessingOnly) { + // 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 { + return false; + } + } else { + // 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) + return false; + } + if (!rollback) // check for availability only return true; if (global.params.verbose) message("Prepending to environment variables:"); - const auto preprendToEnvVar = + 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; @@ -343,8 +372,9 @@ bool setupMsvcEnvironmentImpl( }; rollback->reserve(2); - preprendToEnvVar("LIB", L"LIB", libPaths); - preprendToEnvVar("PATH", L"PATH", binPaths); + prependToEnvVar("INCLUDE", L"INCLUDE", includePaths); + prependToEnvVar("LIB", L"LIB", libPaths); + prependToEnvVar("PATH", L"PATH", binPaths); if (global.params.verbose) { const auto end = std::chrono::steady_clock::now(); @@ -357,11 +387,11 @@ bool setupMsvcEnvironmentImpl( } } // anonymous namespace -bool isMsvcAvailable() { return setupMsvcEnvironmentImpl(nullptr); } +bool isMsvcAvailable() { return setupMsvcEnvironmentImpl(false, nullptr); } -bool MsvcEnvironmentScope::setup() { +bool MsvcEnvironmentScope::setup(bool forPreprocessingOnly) { rollback.clear(); - return setupMsvcEnvironmentImpl(&rollback); + return setupMsvcEnvironmentImpl(forPreprocessingOnly, &rollback); } MsvcEnvironmentScope::~MsvcEnvironmentScope() { diff --git a/driver/tool.h b/driver/tool.h index 21457dd064..c6f0cc35a1 100644 --- a/driver/tool.h +++ b/driver/tool.h @@ -53,7 +53,7 @@ struct MsvcEnvironmentScope { // Tries to set up the MSVC environment variables for the current process and // returns true if successful. The original environment is restored on // destruction. - bool setup(); + bool setup(bool forPreprocessingOnly = false); ~MsvcEnvironmentScope();