Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 99cf321

Browse files
committedApr 29, 2020
Adopt DMD's MSVC toolchain detection
This reduces the overhead for auto-detecting and setting up an MSVC toolchain from a very rough 1 second to about 8 milliseconds on my box. Enabling the auto-detection by default (and so preferring MSVC over the 'internal' toolchain if there's a Visual C++ installation) is now possible, fixing issues like #3402. The MSVC setup now consists of the bare minimum - prepending 3 directories to the LIB env var and 1-2 directories to PATH.
1 parent 734d849 commit 99cf321

12 files changed

+243
-256
lines changed
 

‎CMakeLists.txt

-5
Original file line numberDiff line numberDiff line change
@@ -932,11 +932,6 @@ if(${BUILD_SHARED})
932932
endif()
933933
install(FILES ${PROJECT_BINARY_DIR}/bin/${LDC_EXE}_install.conf DESTINATION ${CONF_INST_DIR} RENAME ${LDC_EXE}.conf)
934934

935-
if(MSVC)
936-
file(COPY vcbuild/ DESTINATION ${PROJECT_BINARY_DIR}/bin FILES_MATCHING PATTERN "*.bat")
937-
install(DIRECTORY vcbuild/ DESTINATION ${CMAKE_INSTALL_PREFIX}/bin FILES_MATCHING PATTERN "*.bat")
938-
endif()
939-
940935
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
941936
if(NOT DEFINED BASH_COMPLETION_COMPLETIONSDIR)
942937
find_package(bash-completion QUIET)

‎azure-pipelines.yml

+2-3
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,11 @@ jobs:
9090
displayName: Generate hello.d
9191
- script: |
9292
echo on
93-
%ARTIFACT_NAME%\bin\ldc2 -v -run hello.d || exit /b
94-
%ARTIFACT_NAME%\bin\ldc2 -v -m32 -run hello.d
93+
%ARTIFACT_NAME%\bin\ldc2 -v -mscrtlib=vcruntime140 -run hello.d || exit /b
94+
%ARTIFACT_NAME%\bin\ldc2 -v -mscrtlib=vcruntime140 -m32 -run hello.d
9595
displayName: Run 32/64-bit hello-world smoke test with internal toolchain
9696
- script: |
9797
echo on
98-
set LDC_VSDIR_FORCE=1
9998
%ARTIFACT_NAME%\bin\ldc2 -v -run hello.d || exit /b
10099
%ARTIFACT_NAME%\bin\ldc2 -v -m32 -run hello.d
101100
displayName: Run 32/64-bit hello-world smoke test with MSVC auto-detection

‎dmd/vsoptions.d

+78-15
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ version (Windows):
1515
import core.stdc.ctype;
1616
import core.stdc.stdlib;
1717
import core.stdc.string;
18+
import core.stdc.wchar_;
1819
import core.sys.windows.winbase;
1920
import core.sys.windows.windef;
2021
import core.sys.windows.winreg;
@@ -25,7 +26,16 @@ import dmd.root.filename;
2526
import dmd.root.outbuffer;
2627
import dmd.root.rmem;
2728

28-
struct VSOptions
29+
version (IN_LLVM)
30+
{
31+
enum supportedPre2017Versions = ["14.0".ptr];
32+
}
33+
else
34+
{
35+
enum supportedPre2017Versions = ["14.0".ptr, "12.0", "11.0", "10.0", "9.0"];
36+
}
37+
38+
extern(C++) struct VSOptions
2939
{
3040
// evaluated once at startup, reflecting the result of vcvarsall.bat
3141
// from the current environment or the latest Visual Studio installation
@@ -238,14 +248,20 @@ private:
238248
if (VSInstallDir is null)
239249
VSInstallDir = getenv("VSINSTALLDIR");
240250

251+
version (IN_LLVM)
252+
{
253+
if (VSInstallDir is null)
254+
VSInstallDir = getenv("LDC_VSDIR");
255+
}
256+
241257
if (VSInstallDir is null)
242258
VSInstallDir = detectVSInstallDirViaCOM();
243259

244260
if (VSInstallDir is null)
245261
VSInstallDir = GetRegistryString(r"Microsoft\VisualStudio\SxS\VS7", "15.0"); // VS2017
246262

247263
if (VSInstallDir is null)
248-
foreach (const(char)* ver; ["14.0".ptr, "12.0", "11.0", "10.0", "9.0"])
264+
foreach (const(char)* ver; supportedPre2017Versions)
249265
{
250266
VSInstallDir = GetRegistryString(FileName.combine(r"Microsoft\VisualStudio", ver), "InstallDir");
251267
if (VSInstallDir)
@@ -267,7 +283,7 @@ private:
267283

268284
// detect from registry (build tools?)
269285
if (VCInstallDir is null)
270-
foreach (const(char)* ver; ["14.0".ptr, "12.0", "11.0", "10.0", "9.0"])
286+
foreach (const(char)* ver; supportedPre2017Versions)
271287
{
272288
auto regPath = FileName.buildPath(r"Microsoft\VisualStudio", ver, r"Setup\VC");
273289
VCInstallDir = GetRegistryString(regPath, "ProductDir");
@@ -311,6 +327,7 @@ private:
311327
}
312328
}
313329

330+
public:
314331
/**
315332
* get Visual C bin folder
316333
* Params:
@@ -325,7 +342,7 @@ private:
325342
* Note: differences for the linker binaries are small, they all
326343
* allow cross compilation
327344
*/
328-
const(char)* getVCBinDir(bool x64, out const(char)* addpath)
345+
const(char)* getVCBinDir(bool x64, out const(char)* addpath) const
329346
{
330347
static const(char)* linkExists(const(char)* p)
331348
{
@@ -406,7 +423,7 @@ private:
406423
* Returns:
407424
* folder containing the the VC runtime libraries
408425
*/
409-
const(char)* getVCLibDir(bool x64)
426+
const(char)* getVCLibDir(bool x64) const
410427
{
411428
if (VCToolsInstallDir !is null)
412429
return FileName.combine(VCToolsInstallDir, x64 ? r"lib\x64" : r"lib\x86");
@@ -422,7 +439,7 @@ private:
422439
* Returns:
423440
* folder containing the universal CRT libraries
424441
*/
425-
const(char)* getUCRTLibPath(bool x64)
442+
const(char)* getUCRTLibPath(bool x64) const
426443
{
427444
if (UCRTSdkDir && UCRTVersion)
428445
return FileName.buildPath(UCRTSdkDir, "Lib", UCRTVersion, x64 ? r"ucrt\x64" : r"ucrt\x86");
@@ -436,7 +453,7 @@ private:
436453
* Returns:
437454
* folder containing the Windows SDK libraries
438455
*/
439-
const(char)* getSDKLibPath(bool x64)
456+
const(char)* getSDKLibPath(bool x64) const
440457
{
441458
if (WindowsSdkDir)
442459
{
@@ -455,13 +472,20 @@ private:
455472
return sdk;
456473
}
457474

475+
version (IN_LLVM) {}
476+
else
477+
{
458478
// try mingw fallback relative to phobos library folder that's part of LIB
459479
if (auto p = FileName.searchPath(getenv("LIB"), r"mingw\kernel32.lib"[], false))
460480
return FileName.path(p).ptr;
481+
}
461482

462483
return null;
463484
}
464485

486+
private:
487+
extern(D):
488+
465489
// iterate through subdirectories named by SDK version in baseDir and return the
466490
// one with the largest version that also contains the test file
467491
static const(char)* findLatestSDKDir(const(char)* baseDir, const(char)* testfile)
@@ -500,7 +524,7 @@ private:
500524
* Returns:
501525
* the registry value if it exists and has string type
502526
*/
503-
const(char)* GetRegistryString(const(char)* softwareKeyPath, const(char)* valueName)
527+
const(char)* GetRegistryString(const(char)* softwareKeyPath, const(char)* valueName) const
504528
{
505529
enum x64hive = false; // VS registry entries always in 32-bit hive
506530

@@ -527,6 +551,7 @@ private:
527551
char[260] buf = void;
528552
DWORD cnt = buf.length * char.sizeof;
529553
DWORD type;
554+
// TODO: wide API
530555
int hr = RegQueryValueExA(key, valueName, null, &type, cast(ubyte*) buf.ptr, &cnt);
531556
if (hr == 0 && cnt > 0)
532557
return buf.dup.ptr;
@@ -579,6 +604,8 @@ import core.sys.windows.oleauto : SysFreeString;
579604
pragma(lib, "ole32.lib");
580605
pragma(lib, "oleaut32.lib");
581606

607+
extern (C) int _waccess(const(wchar)* _FileName, int _AccessMode);
608+
582609
interface ISetupInstance : IUnknown
583610
{
584611
// static const GUID iid = uuid("B41463C3-8866-43B5-BC33-2B0676F7F42E");
@@ -635,18 +662,54 @@ const(char)* detectVSInstallDirViaCOM()
635662
return null;
636663
scope(exit) instances.Release();
637664

665+
BSTR versionString;
666+
BSTR installDir;
667+
scope(exit) SysFreeString(versionString);
668+
scope(exit) SysFreeString(installDir);
669+
638670
while (instances.Next(1, &instance, &fetched) == S_OK && fetched)
639671
{
640-
BSTR bstrInstallDir;
641-
if (instance.GetInstallationPath(&bstrInstallDir) != S_OK)
672+
BSTR thisVersionString;
673+
if (instance.GetInstallationVersion(&thisVersionString) != S_OK)
674+
continue;
675+
scope(exit) SysFreeString(thisVersionString);
676+
677+
BSTR thisInstallDir;
678+
if (instance.GetInstallationPath(&thisInstallDir) != S_OK)
642679
continue;
680+
scope(exit) SysFreeString(thisInstallDir);
681+
682+
if (versionString && wcscmp(thisVersionString, versionString) <= 0)
683+
continue; // not a newer version, skip
684+
685+
const installDirLength = wcslen(thisInstallDir);
686+
const vcInstallDirLength = installDirLength + 4;
687+
auto vcInstallDir = (cast(wchar*) mem.xmalloc_noscan(vcInstallDirLength * wchar.sizeof))[0 .. vcInstallDirLength];
688+
scope(exit) mem.xfree(vcInstallDir.ptr);
689+
vcInstallDir[0 .. installDirLength] = thisInstallDir[0 .. installDirLength];
690+
vcInstallDir[installDirLength .. $] = "\\VC\0"w;
691+
if (_waccess(vcInstallDir.ptr, 0) != 0)
692+
continue; // Visual C++ not included, skip
643693

644-
char[260] path;
645-
int len = WideCharToMultiByte(CP_UTF8, 0, bstrInstallDir, -1, path.ptr, 260, null, null);
646-
SysFreeString(bstrInstallDir);
694+
if (versionString)
695+
{
696+
SysFreeString(versionString);
697+
SysFreeString(installDir);
698+
}
699+
versionString = thisVersionString;
700+
installDir = thisInstallDir;
701+
thisVersionString = null;
702+
thisInstallDir = null;
703+
}
647704

648-
if (len > 0)
649-
return path[0..len].idup.ptr;
705+
if (installDir)
706+
{
707+
char[260] path = void;
708+
int len = WideCharToMultiByte(CP_UTF8, 0, installDir, -1, path.ptr, path.length, null, null);
709+
assert(len);
710+
711+
return path[0 .. len].idup.ptr;
650712
}
713+
651714
return null;
652715
}

‎dmd/vsoptions.h

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
2+
/* Compiler implementation of the D programming language
3+
* Copyright (C) 2009-2020 by The D Language Foundation, All Rights Reserved
4+
* written by Walter Bright
5+
* http://www.digitalmars.com
6+
* Distributed under the Boost Software License, Version 1.0.
7+
* http://www.boost.org/LICENSE_1_0.txt
8+
* https://github.com/dlang/dmd/blob/master/src/dmd/vsoptions.h
9+
*/
10+
11+
#pragma once
12+
13+
#ifdef _WIN32
14+
15+
struct VSOptions
16+
{
17+
const char *WindowsSdkDir = nullptr;
18+
const char *WindowsSdkVersion = nullptr;
19+
const char *UCRTSdkDir = nullptr;
20+
const char *UCRTVersion = nullptr;
21+
const char *VSInstallDir = nullptr;
22+
const char *VCInstallDir = nullptr;
23+
const char *VCToolsInstallDir = nullptr; // used by VS 2017+
24+
25+
void initialize();
26+
const char *getVCBinDir(bool x64, const char *&addpath) const;
27+
const char *getVCLibDir(bool x64) const;
28+
const char *getUCRTLibPath(bool x64) const;
29+
const char *getSDKLibPath(bool x64) const;
30+
};
31+
32+
#endif // _WIN32

‎driver/linker-msvc.cpp

+33-9
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
//===----------------------------------------------------------------------===//
99

1010
#include "dmd/errors.h"
11+
#include "driver/args.h"
1112
#include "driver/cl_options.h"
1213
#include "driver/cl_options_instrumentation.h"
1314
#include "driver/cl_options_sanitizers.h"
@@ -31,21 +32,27 @@
3132

3233
namespace {
3334

34-
void addMscrtLibs(std::vector<std::string> &args) {
35-
const auto mscrtlibName = getMscrtLibName();
35+
void addMscrtLibs(bool useInternalToolchain, std::vector<std::string> &args) {
36+
const auto mscrtlibName = getMscrtLibName(&useInternalToolchain);
3637

3738
args.push_back(("/DEFAULTLIB:" + mscrtlibName).str());
3839

3940
// We need the vcruntime lib for druntime's exception handling (ldc.eh_msvc).
4041
// Pick one of the 4 variants matching the selected main UCRT lib.
4142

43+
if (useInternalToolchain) {
4244
#if LDC_LLVM_VER >= 400
43-
if (mscrtlibName.contains_lower("vcruntime")) {
45+
assert(mscrtlibName.contains_lower("vcruntime"));
46+
#endif
4447
return;
4548
}
46-
#endif
4749

50+
#if LDC_LLVM_VER >= 400
51+
const bool isStatic = mscrtlibName.contains_lower("libcmt");
52+
#else // LLVM 3.9: no llvm::StringRef::{contains,find}_lower
4853
const bool isStatic = mscrtlibName.startswith_lower("libcmt");
54+
#endif
55+
4956
const bool isDebug =
5057
mscrtlibName.endswith_lower("d") || mscrtlibName.endswith_lower("d.lib");
5158

@@ -85,12 +92,29 @@ int linkObjToBinaryMSVC(llvm::StringRef outputPath,
8592
fatal();
8693
}
8794

88-
const bool useInternalToolchain = useInternalToolchainForMSVC();
89-
9095
#ifdef _WIN32
9196
windows::MsvcEnvironmentScope msvcEnv;
92-
if (!useInternalToolchain)
93-
msvcEnv.setup();
97+
98+
const auto begin = std::chrono::steady_clock::now();
99+
100+
const bool forceMSVC = env::has(L"LDC_VSDIR_FORCE");
101+
const bool useInternalToolchain =
102+
(!forceMSVC && getExplicitMscrtLibName().contains_lower("vcruntime")) ||
103+
!msvcEnv.setup();
104+
105+
if (!useInternalToolchain && global.params.verbose) {
106+
const auto end = std::chrono::steady_clock::now();
107+
message("MSVC setup took %lld microseconds",
108+
std::chrono::duration_cast<std::chrono::microseconds>(end - begin)
109+
.count());
110+
}
111+
112+
if (forceMSVC && useInternalToolchain) {
113+
warning(Loc(), "no Visual C++ installation found for linking, falling back "
114+
"to MinGW-based libraries");
115+
}
116+
#else
117+
const bool useInternalToolchain = true;
94118
#endif
95119

96120
// build arguments
@@ -121,7 +145,7 @@ int linkObjToBinaryMSVC(llvm::StringRef outputPath,
121145
}
122146

123147
// add C runtime libs
124-
addMscrtLibs(args);
148+
addMscrtLibs(useInternalToolchain, args);
125149

126150
// specify creation of DLL
127151
if (global.params.dll) {

‎driver/linker.cpp

+21-19
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
#include "driver/linker.h"
1111

1212
#include "dmd/errors.h"
13-
#include "driver/args.h"
1413
#include "driver/cl_options.h"
1514
#include "driver/tool.h"
1615
#include "gen/llvm.h"
@@ -184,28 +183,31 @@ bool linkAgainstSharedDefaultLibs() {
184183

185184
//////////////////////////////////////////////////////////////////////////////
186185

187-
bool useInternalToolchainForMSVC() {
188-
#ifndef _WIN32
189-
return true;
186+
llvm::StringRef getExplicitMscrtLibName() { return mscrtlib; }
187+
188+
llvm::StringRef getMscrtLibName(const bool *useInternalToolchain) {
189+
llvm::StringRef name = getExplicitMscrtLibName();
190+
if (!name.empty())
191+
return name;
192+
193+
bool useInternal = false;
194+
if (useInternalToolchain) {
195+
useInternal = *useInternalToolchain;
196+
} else {
197+
#ifdef _WIN32
198+
static bool haveMSVC = windows::MsvcEnvironmentScope().setup();
199+
useInternal = !haveMSVC;
190200
#else
191-
return !env::has(L"VSINSTALLDIR") && !env::has(L"LDC_VSDIR") &&
192-
// LDC_VSDIR_FORCE alone can be used to prefer MSVC toolchain
193-
// auto-detection over the internal toolchain.
194-
!env::has(L"LDC_VSDIR_FORCE");
201+
useInternal = true;
195202
#endif
196-
}
203+
}
197204

198-
llvm::StringRef getMscrtLibName() {
199-
llvm::StringRef name = mscrtlib;
200-
if (name.empty()) {
201-
if (useInternalToolchainForMSVC()) {
202-
name = "vcruntime140";
203-
} else {
204-
// default to static release variant
205-
name = linkFullyStatic() != llvm::cl::BOU_FALSE ? "libcmt" : "msvcrt";
206-
}
205+
if (useInternal) {
206+
return "vcruntime140";
207+
} else {
208+
// default to static release variant
209+
return linkFullyStatic() != llvm::cl::BOU_FALSE ? "libcmt" : "msvcrt";
207210
}
208-
return name;
209211
}
210212

211213
//////////////////////////////////////////////////////////////////////////////

‎driver/linker.h

+3-4
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,14 @@ llvm::cl::boolOrDefault linkFullyStatic();
4040
bool linkAgainstSharedDefaultLibs();
4141

4242
/**
43-
* Indicates whether the internal 'toolchain' (-link-internally and MinGW-w64
44-
* libs) is to be used for MSVC targets.
43+
* Returns the value of -mscrtlib.
4544
*/
46-
bool useInternalToolchainForMSVC();
45+
llvm::StringRef getExplicitMscrtLibName();
4746

4847
/**
4948
* Returns the name of the MS C runtime library to link with.
5049
*/
51-
llvm::StringRef getMscrtLibName();
50+
llvm::StringRef getMscrtLibName(const bool *useInternalToolchain = nullptr);
5251

5352
/**
5453
* Inserts bitcode files passed on the commandline into a module.

‎driver/tool.cpp

+58-109
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "driver/tool.h"
1111

1212
#include "dmd/errors.h"
13+
#include "dmd/vsoptions.h"
1314
#include "driver/args.h"
1415
#include "driver/cl_options.h"
1516
#include "driver/exe_path.h"
@@ -296,134 +297,82 @@ int executeAndWait(const char *commandLine, DWORD creationFlags = 0) {
296297

297298
bool setupMsvcEnvironmentImpl(
298299
std::vector<std::pair<std::wstring, wchar_t *>> &rollback) {
299-
if (env::has(L"VSINSTALLDIR") && !env::has(L"LDC_VSDIR_FORCE"))
300+
if (env::has(L"VSINSTALLDIR") && !env::has(L"LDC_VSDIR_FORCE")) {
301+
// assume a fully set up environment (e.g., VS native tools command prompt)
300302
return true;
303+
}
301304

302-
llvm::SmallString<128> tmpFilePath;
303-
if (llvm::sys::fs::createTemporaryFile("ldc_dumpEnv", "", tmpFilePath))
305+
VSOptions vsOptions;
306+
vsOptions.initialize();
307+
if (!vsOptions.VSInstallDir)
304308
return false;
305309

306-
/* Run `%ComSpec% /s /c "...\dumpEnv.bat <x86|amd64> > <tmpFilePath>"` to
307-
* dump the MSVC environment to the temporary file.
308-
*
309-
* cmd.exe /c treats the following string argument (the command)
310-
* in a very peculiar way if it starts with a double-quote.
311-
* By adding /s and enclosing the command in extra double-quotes
312-
* (WITHOUT additionally escaping the command), the command will
313-
* be parsed properly.
314-
*/
315-
316-
std::string cmdExecutable = env::get("ComSpec");
317-
if (cmdExecutable.empty()) {
318-
warning(Loc(),
319-
"'ComSpec' environment variable is not set, assuming 'cmd.exe'.");
320-
cmdExecutable = "cmd.exe";
321-
}
322-
std::string batchFile = exe_path::prependBinDir("dumpEnv.bat");
323-
std::string arch =
324-
global.params.targetTriple->isArch64Bit() ? "amd64" : "x86";
325-
326-
llvm::SmallString<512> commandLine;
327-
commandLine += quoteArg(cmdExecutable);
328-
commandLine += " /s /c \"";
329-
commandLine += "chcp 65001 && "; // => UTF-8 output encoding
330-
commandLine += quoteArg(batchFile);
331-
commandLine += ' ';
332-
commandLine += arch;
333-
commandLine += " > ";
334-
commandLine += quoteArg(tmpFilePath);
335-
commandLine += '"';
336-
337-
// do NOT pass down our console
338-
const DWORD procCreationFlags = CREATE_NO_WINDOW;
339-
const int exitCode = executeAndWait(commandLine.c_str(), procCreationFlags);
340-
if (exitCode != 0) {
341-
error(Loc(), "'%s' failed with status: %d", commandLine.c_str(), exitCode);
342-
llvm::sys::fs::remove(tmpFilePath);
343-
return false;
310+
const bool x64 = global.params.targetTriple->isArch64Bit();
311+
312+
llvm::SmallVector<const char *, 3> libPaths;
313+
if (auto vclibdir = vsOptions.getVCLibDir(x64))
314+
libPaths.push_back(vclibdir);
315+
if (auto ucrtlibdir = vsOptions.getUCRTLibPath(x64))
316+
libPaths.push_back(ucrtlibdir);
317+
if (auto sdklibdir = vsOptions.getSDKLibPath(x64))
318+
libPaths.push_back(sdklibdir);
319+
320+
llvm::SmallVector<const char *, 2> binPaths;
321+
const char *secondaryBindir = nullptr;
322+
if (auto bindir = vsOptions.getVCBinDir(x64, secondaryBindir)) {
323+
binPaths.push_back(bindir);
324+
if (secondaryBindir)
325+
binPaths.push_back(secondaryBindir);
344326
}
345327

346-
auto fileBuffer = llvm::MemoryBuffer::getFile(tmpFilePath);
347-
llvm::sys::fs::remove(tmpFilePath);
348-
if (fileBuffer.getError())
328+
const bool success = libPaths.size() == 3 && !binPaths.empty();
329+
if (!success)
349330
return false;
350331

351-
const auto contents = (*fileBuffer)->getBuffer();
352-
const auto size = contents.size();
353-
354-
// Parse the file.
355-
std::vector<std::pair<llvm::StringRef, llvm::StringRef>> env;
356-
357-
size_t i = 0;
358-
// for each line
359-
while (i < size) {
360-
llvm::StringRef key, value;
361-
362-
for (size_t j = i; j < size; ++j) {
363-
const char c = contents[j];
364-
if (c == '=' && key.empty()) {
365-
key = contents.slice(i, j);
366-
i = j + 1;
367-
} else if (c == '\n' || c == '\r' || c == '\0') {
368-
if (!key.empty()) {
369-
value = contents.slice(i, j);
370-
}
371-
// break and continue with next line
372-
i = j + 1;
373-
break;
374-
}
375-
}
376-
377-
if (!key.empty() && !value.empty())
378-
env.emplace_back(key, value);
379-
}
380-
381332
if (global.params.verbose)
382-
message("Applying environment variables:");
383-
384-
rollback.reserve(env.size());
385-
bool haveVsInstallDir = false;
386-
387-
for (const auto &pair : env) {
388-
const llvm::StringRef key = pair.first;
389-
const llvm::StringRef value = pair.second;
390-
391-
if (key == "VSINSTALLDIR")
392-
haveVsInstallDir = true;
333+
message("Prepending to environment variables:");
334+
335+
const auto preprendToEnvVar =
336+
[&rollback](const char *key, const wchar_t *wkey,
337+
const llvm::SmallVectorImpl<const char *> &entries) {
338+
wchar_t *originalValue = _wgetenv(wkey);
339+
340+
llvm::SmallString<256> head;
341+
for (const char *entry : entries) {
342+
if (!head.empty())
343+
head += ';';
344+
head += entry;
345+
}
393346

394-
llvm::SmallVector<wchar_t, 32> wkey;
395-
llvm::SmallVector<wchar_t, 512> wvalue;
396-
llvm::sys::windows::UTF8ToUTF16(key, wkey);
397-
llvm::sys::windows::UTF8ToUTF16(value, wvalue);
398-
wkey.push_back(0);
399-
wvalue.push_back(0);
347+
if (global.params.verbose)
348+
message(" %s += %.*s", key, (int)head.size(), head.data());
400349

401-
wchar_t *originalValue = _wgetenv(wkey.data());
402-
if (originalValue && wcscmp(originalValue, wvalue.data()) == 0)
403-
continue; // no change
350+
llvm::SmallVector<wchar_t, 1024> wvalue;
351+
llvm::sys::windows::UTF8ToUTF16(head, wvalue);
352+
if (originalValue) {
353+
wvalue.push_back(L';');
354+
wvalue.append(originalValue, originalValue + wcslen(originalValue));
355+
}
356+
wvalue.push_back(0);
404357

405-
if (global.params.verbose) {
406-
message(" %.*s=%.*s", (int)key.size(), key.data(), (int)value.size(),
407-
value.data());
408-
}
358+
// copy the original value, if set
359+
if (originalValue)
360+
originalValue = wcsdup(originalValue);
361+
rollback.emplace_back(wkey, originalValue);
409362

410-
// copy the original value, if set
411-
if (originalValue)
412-
originalValue = wcsdup(originalValue);
413-
rollback.emplace_back(wkey.data(), originalValue);
363+
SetEnvironmentVariableW(wkey, wvalue.data());
364+
};
414365

415-
SetEnvironmentVariableW(wkey.data(), wvalue.data());
416-
}
366+
rollback.reserve(2);
367+
preprendToEnvVar("LIB", L"LIB", libPaths);
368+
preprendToEnvVar("PATH", L"PATH", binPaths);
417369

418-
return haveVsInstallDir;
370+
return true;
419371
}
420372

421373
bool MsvcEnvironmentScope::setup() {
422374
rollback.clear();
423-
const bool success = setupMsvcEnvironmentImpl(rollback);
424-
if (!success)
425-
warning(Loc(), "no Visual C++ installation detected");
426-
return success;
375+
return setupMsvcEnvironmentImpl(rollback);
427376
}
428377

429378
MsvcEnvironmentScope::~MsvcEnvironmentScope() {

‎gen/target.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,8 @@ Expression *Target::getTargetInfo(const char *name_, const Loc &loc) {
250250
if (name == "cppRuntimeLibrary") {
251251
const char *cppRuntimeLibrary = "";
252252
if (triple.isWindowsMSVCEnvironment()) {
253-
cppRuntimeLibrary = mem.xstrdup(getMscrtLibName().str().c_str());
253+
auto mscrtlib = getMscrtLibName().str();
254+
cppRuntimeLibrary = mem.xstrdup(mscrtlib.c_str());
254255
}
255256
return createStringExp(cppRuntimeLibrary);
256257
}

‎packaging/README.txt

+14-27
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,21 @@ The compiler configuration file is etc\ldc2.conf and can be easily customized
77
to your liking, e.g., adding implicit command-line options and setting up cross-
88
compilation.
99

10-
The LDC package is portable and ships with LLD, the LLVM linker, as well as
11-
WinSDK & Visual C++ runtime (import) libraries based on MinGW-w64. In order to
12-
run the generated binaries, a Visual C++ 2015 runtime installation is required
13-
(vcruntime140.dll, ucrtbase.dll etc.).
10+
If you have an installed Visual C++ toolchain (Visual Studio/Build Tools 2015 or
11+
newer), LDC defaults to using linker and libraries of the latest Visual C++
12+
installation it can find.
13+
You can set the LDC_VSDIR environment variable to select a specific version,
14+
e.g., 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community'.
15+
The MSVC toolchain setup is skipped if LDC is run inside a 'VS Native/Cross
16+
Tools Command Prompt' (more precisely, if the VSINSTALLDIR environment variable
17+
is set). You can set the LDC_VSDIR_FORCE environment variable (to some
18+
non-empty value) to enforce setting up the MSVC toolchain.
1419

15-
In case you prefer an official Microsoft toolchain for linking (Visual C++ 2015
16-
or newer), e.g., to link with the static Microsoft libraries (and thus avoid the
17-
dependency on the Visual C++ runtime installation for your users), you have the
18-
following options:
19-
20-
* Run LDC in a 'VS Native/Cross Tools Command Prompt' (LDC checks whether the
21-
VSINSTALLDIR environment variable is set).
22-
LDC assumes the environment variables are all set up appropriately.
23-
* Or set the LDC_VSDIR environment variable to some Visual Studio/Visual C++
24-
Build Tools installation directory, e.g.,
25-
'C:\Program Files (x86)\Microsoft Visual Studio\2017\Community'.
26-
LDC will invoke a batch file provided by VS to set up the environment
27-
variables for the selected 32/64-bit target platform, which adds an overhead
28-
of about 1 second for each linking operation.
29-
You can also set LDC_VSDIR_FORCE (to some non-empty value); LDC will then try
30-
to auto-detect your latest Visual C++ installation if you haven't set
31-
LDC_VSDIR, and won't skip the environment setup if VSINSTALLDIR is pre-set.
32-
* Or set up the etc\ldc2.conf config file and specify the directories containing
33-
the MS libs (appending them to the 'lib-dirs' array; check out the LIB
34-
environment variable in a VS tools command prompt) as well as the C runtime
35-
flavor (e.g., appending '-mscrtlib=libcmt' to the 'switches' array).
36-
In case you prefer the MS linker over LLD, add the switch
37-
'-linker=<path\to\link.exe>'.
20+
If you don't have a Visual C++ installation, LDC falls back to LLD (the LLVM
21+
linker) and the bundled WinSDK & Visual C++ runtime (import) libraries based on
22+
MinGW-w64. In that case, the generated executables and DLLs depend on an
23+
installed (redistributable) Visual C++ 2015+ runtime (vcruntime140.dll,
24+
ucrtbase.dll etc.).
3825

3926
For further information, including on how to report bugs, please refer to the
4027
LDC wiki: http://wiki.dlang.org/LDC.

‎vcbuild/dumpEnv.bat

-7
This file was deleted.

‎vcbuild/msvcEnv.bat

-57
This file was deleted.

0 commit comments

Comments
 (0)
Please sign in to comment.