Set up MSVC environment for C preprocessing on Windows

This commit is contained in:
Martin Kinkelin 2023-06-07 11:58:03 +02:00
parent d94b12500b
commit 04bcdc4d06
6 changed files with 103 additions and 26 deletions

View file

@ -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

View file

@ -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):

View file

@ -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

View file

@ -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());

View file

@ -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<std::pair<std::wstring, wchar_t *>> *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<const char *, 3> 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<const char *, 2> 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<const char *, 3> includePaths;
llvm::SmallVector<const char *, 3> 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<const char *> &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() {

View file

@ -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();