//===-- ldmd.cpp - Drop-in DMD replacement wrapper for LDC ----------------===// // // LDC – the LLVM D compiler // // This file is distributed under the BSD-style LDC license, except for the // command line handling code, which originated from DMD. See the LICENSE // file for details. // //===----------------------------------------------------------------------===// // // Wrapper allowing use of LDC as drop-in replacement for DMD. // // The reason why full command line parsing is required instead of just // rewriting the names of a few switches is an annoying impedance mismatch // between the way how DMD handles arguments and the LLVM command line library: // DMD allows all switches to be specified multiple times – in case of // conflicts, the last one takes precedence. There is no easy way to replicate // this behavior with LLVM, save parsing the switches and re-emitting a cleaned // up string. // // DMD also reads switches from the DFLAGS enviroment variable, if present. This // is contrary to what C compilers do, where CFLAGS is usually handled by the // build system. Thus, conflicts like mentioned above occur quite frequently in // practice in makefiles written for DMD, as DFLAGS is also a natural name for // handling flags there. // // In order to maintain backwards compatibility with earlier versions of LDMD, // unknown switches are passed through verbatim to LDC. Finding a better // solution for this is tricky, as some of the LLVM arguments can be // intentionally specified multiple times to get a certain effect (e.g. pass, // linker options). // // Just as with the old LDMD script, arguments can be passed through unmodified // to LDC by using -Csomearg. // // If maintaining this wrapper is deemed too messy at some point, an alternative // would be to either extend the LLVM command line library to support the DMD // semantics (unlikely to happen), or to abandon it altogether (except for // passing the LLVM-defined flags to the various passes). // // Note: This program inherited ugly C-style string handling and memory leaks // from DMD, but this should not be a problem due to the short-livedness of // the process. // //===----------------------------------------------------------------------===// #ifndef LDC_EXE_NAME #error "Please define LDC_EXE_NAME to the name of the LDC executable to use." #endif #include "driver/exe_path.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/Program.h" #include "llvm/Support/SystemUtils.h" #include "llvm/Support/raw_ostream.h" #if _WIN32 #include "Windows.h" #else #include #endif #include #include #include #include #include #include #include #include #ifdef HAVE_SC_ARG_MAX #include #endif namespace ls = llvm::sys; // We reuse DMD's response file parsing routine for maximum compatibilty - it // handles quotes in a very peculiar way. int response_expand(size_t *pargc, char ***pargv); void browse(const char *url); /** * Prints a formatted error message to stderr and exits the program. */ void error(const char *fmt, ...) { va_list argp; va_start(argp, fmt); fprintf(stderr, "Error: "); vfprintf(stderr, fmt, argp); fprintf(stderr, "\n"); exit(EXIT_FAILURE); va_end(argp); } /** * Prints a formatted warning message to stderr. */ void warning(const char *fmt, ...) { va_list argp; va_start(argp, fmt); fprintf(stderr, "Warning: "); vfprintf(stderr, fmt, argp); fprintf(stderr, "\n"); va_end(argp); } char *concat(const char *a, const char *b) { size_t na = strlen(a); size_t nb = strlen(b); char *result = static_cast(malloc(na + nb + 1)); assert(result); memcpy(result, a, na); memcpy(result + na, b, nb + 1); return result; } char *concat(const char *a, int b) { char bStr[14]; #if defined(_MSC_VER) _snprintf_s(bStr, _countof(bStr), sizeof(bStr), "%d", b); #else snprintf(bStr, sizeof(bStr), "%d", b); #endif return concat(a, bStr); } /** * Runs the given executable, returning its error code. */ int execute(const std::string &exePath, const char **args) { std::string errorMsg; int rc = ls::ExecuteAndWait(exePath, args, nullptr, nullptr, 0, 0, &errorMsg); if (!errorMsg.empty()) { error("Error executing %s: %s", exePath.c_str(), errorMsg.c_str()); } return rc; } /** * Prints usage information to stdout. */ void printUsage(const char *argv0, const std::string &ldcPath) { // Print version information by actually invoking ldc -version. const char *args[] = {ldcPath.c_str(), "-version", nullptr}; execute(ldcPath, args); printf( "\n\ Usage:\n\ %s files.d ... { -switch }\n\ \n\ files.d D source files\n\ @cmdfile read arguments from cmdfile\n\ -allinst generate code for all template instantiations\n\ -c do not link\n\ -color[=on|off] force colored console output on or off\n\ -conf=path use config file at path\n\ -cov do code coverage analysis\n\ -cov=nnn require at least nnn%% code coverage\n\ -D generate documentation\n\ -Dddocdir write documentation file to docdir directory\n\ -Dffilename write documentation file to filename\n\ -d silently allow deprecated features\n\ -dw show use of deprecated features as warnings (default)\n\ -de show use of deprecated features as errors (halt compilation)\n\ -debug compile in debug code\n\ -debug=level compile in debug code <= level\n\ -debug=ident compile in debug code identified by ident\n\ -debuglib=name set symbolic debug library to name\n\ -defaultlib=name set default library to name\n\ -deps print module dependencies (imports/file/version/debug/lib)\n\ -deps=filename write module dependencies to filename (only imports)\n\ -dip25 implement http://wiki.dlang.org/DIP25 (experimental)\n\ -fPIC generate position independent code\n\ -g add symbolic debug info\n\ -gc add symbolic debug info, pretend to be C\n\ -gs always emit stack frame\n\ -H generate 'header' file\n\ -Hddirectory write 'header' file to directory\n\ -Hffilename write 'header' file to filename\n\ --help print help\n\ -Ipath where to look for imports\n\ -ignore ignore unsupported pragmas\n\ -inline do function inlining\n\ -Jpath where to look for string imports\n\ -Llinkerflag pass linkerflag to link\n\ -lib generate library rather than object files\n\ -m32 generate 32 bit code\n\ -m64 generate 64 bit code\n\ -man open web browser on manual page\n" #if 0 " -map generate linker .map file\n" #endif " -boundscheck=[on|safeonly|off] bounds checks on, in @safe only, or off\n\ -noboundscheck no array bounds checking (deprecated, use -boundscheck=off)\n\ -O optimize\n\ -o- do not write object file\n\ -odobjdir write object & library files to directory objdir\n\ -offilename name output file to filename\n\ -op do not strip paths from source file\n" #if 0 " -profile profile runtime performance of generated code\n" #endif " -property enforce property syntax (deprecated, no effect)\n\ -quiet suppress unnecessary messages\n\ -release compile release version\n\ -run srcfile args... run resulting program, passing args\n\ -shared generate shared library\n\ -transition=id show additional info about language change identified by 'id'\n\ -transition=? list all language changes\n\ -unittest compile in unit tests\n\ -v verbose\n\ -vcolumns print character (column) numbers in diagnostics\n\ -vdmd print the command used to invoke the underlying compiler\n\ --version print compiler version and exit\n\ -version=level compile in version code >= level\n\ -version=ident compile in version code identified by ident\n\ -vtls list all variables going into thread local storage\n\ -vgc list all gc allocations including hidden ones\n\ -verrors=num limit the number of error messages (0 means unlimited)\n\ -w enable warnings\n\ -wi enable informational warnings\n\ -X generate JSON file\n\ -Xffilename write JSON file to filename\n\n", argv0); } /** * Parses an enviroment variable for flags and appends them to given list of * arguments. * * This corresponds to getenv_setargv() in DMD, but we need to duplicate it * here since it is defined in mars.c. */ void appendEnvVar(const char *envVarName, std::vector &args) { char *env = getenv(envVarName); if (!env) { return; } env = strdup(env); // create our own writable copy size_t j = 1; // leave argv[0] alone while (1) { switch (*env) { case ' ': case '\t': env++; break; case 0: return; default: args.push_back(env); // append j++; char *p = env; int slash = 0; int instring = 0; char c = 0; while (1) { c = *env++; switch (c) { case '"': p -= (slash >> 1); if (slash & 1) { p--; goto Laddc; } instring ^= 1; slash = 0; continue; case ' ': case '\t': if (instring) { goto Laddc; } *p = 0; break; case '\\': slash++; *p++ = c; continue; case 0: *p = 0; return; default: Laddc: slash = 0; *p++ = c; continue; } break; } } } } struct BoundsCheck { enum Type { defaultVal, off, safeOnly, on }; }; struct Color { enum Type { automatic, on, off }; }; struct Debug { enum Type { none, normal, pretendC }; }; struct Deprecated { enum Type { allow, warn, error }; }; struct Model { enum Type { automatic, m32, m64 }; }; struct Warnings { enum Type { none, asErrors, informational }; }; struct Params { bool allinst = false; Deprecated::Type useDeprecated = Deprecated::warn; bool compileOnly = false; bool coverage = false; bool emitSharedLib = false; bool pic = false; bool emitMap = false; bool multiObj = false; Debug::Type debugInfo = Debug::none; bool alwaysStackFrame = false; Model::Type targetModel = Model::automatic; bool profile = false; bool verbose = false; bool vcolumns = false; bool vdmd = false; bool vgc = false; bool logTlsUse = false; unsigned errorLimit = 0; bool errorLimitSet = false; Warnings::Type warnings = Warnings::none; bool optimize = false; bool noObj = false; char *objDir = nullptr; char *objName = nullptr; bool preservePaths = false; bool generateDocs = false; char *docDir = nullptr; char *docName = nullptr; bool generateHeaders = false; char *headerDir = nullptr; char *headerName = nullptr; bool generateJson = false; char *jsonName = nullptr; bool ignoreUnsupportedPragmas = false; bool enforcePropertySyntax = false; bool enableInline = false; bool emitStaticLib = false; bool quiet = false; bool release = false; BoundsCheck::Type boundsChecks = BoundsCheck::defaultVal; bool emitUnitTests = false; std::vector modulePaths; std::vector importPaths; bool debugFlag = false; unsigned debugLevel = 0; std::vector debugIdentifiers; unsigned versionLevel = 0; std::vector versionIdentifiers; std::vector linkerSwitches; std::vector transitions; char *defaultLibName = nullptr; char *debugLibName = nullptr; char *moduleDepsFile = nullptr; Color::Type color = Color::automatic; bool useDIP25 = false; char *conf = nullptr; bool hiddenDebugB = false; bool hiddenDebugC = false; bool hiddenDebugF = false; bool hiddenDebugR = false; bool hiddenDebugX = false; bool hiddenDebugY = false; std::vector unknownSwitches; bool run = false; std::vector files; std::vector runArgs; }; /** * Parses the flags from the given command line and the DFLAGS environment * variable into a Params struct. */ Params parseArgs(size_t originalArgc, char **originalArgv, const std::string &ldcPath) { // Expand any response files present into the list of arguments. size_t argc = originalArgc; char **argv = originalArgv; if (response_expand(&argc, &argv)) { error("Could not read response file."); } std::vector args(argv, argv + argc); appendEnvVar("DFLAGS", args); Params result = Params(); for (size_t i = 1; i < args.size(); i++) { char *p = args[i]; if (*p == '-') { if (strcmp(p + 1, "allinst") == 0) { result.allinst = true; } else if (strcmp(p + 1, "de") == 0) { result.useDeprecated = Deprecated::error; } else if (strcmp(p + 1, "d") == 0) { result.useDeprecated = Deprecated::allow; } else if (strcmp(p + 1, "dw") == 0) { result.useDeprecated = Deprecated::warn; } else if (strcmp(p + 1, "c") == 0) { result.compileOnly = true; } else if (strncmp(p + 1, "color", 5) == 0) { result.color = Color::on; // Parse: // -color // -color=on|off if (p[6] == '=') { if (strcmp(p + 7, "off") == 0) { result.color = Color::off; } else if (strcmp(p + 7, "on") != 0) { goto Lerror; } } else if (p[6]) { goto Lerror; } } else if (strncmp(p + 1, "conf=", 5) == 0) { result.conf = p + 1 + 5; } else if (strcmp(p + 1, "cov") == 0) { // For "-cov=...", the whole cmdline switch is forwarded to LDC. // For plain "-cov", the cmdline switch must be explicitly forwarded // and result.coverage must be set to true to that effect. result.coverage = (p[4] != '='); } else if (strcmp(p + 1, "dip25") == 0) { result.useDIP25 = true; } else if (strcmp(p + 1, "shared") == 0 // backwards compatibility with old switch || strcmp(p + 1, "dylib") == 0) { result.emitSharedLib = true; } else if (strcmp(p + 1, "fPIC") == 0) { result.pic = true; } else if (strcmp(p + 1, "map") == 0) { result.emitMap = true; } else if (strcmp(p + 1, "multiobj") == 0) { result.multiObj = true; } else if (strcmp(p + 1, "g") == 0) { result.debugInfo = Debug::normal; } else if (strcmp(p + 1, "gc") == 0) { result.debugInfo = Debug::pretendC; } else if (strcmp(p + 1, "gs") == 0) { result.alwaysStackFrame = true; } else if (strcmp(p + 1, "gt") == 0) { error("use -profile instead of -gt\n"); } else if (strcmp(p + 1, "m32") == 0) { result.targetModel = Model::m32; } else if (strcmp(p + 1, "m64") == 0) { result.targetModel = Model::m64; } else if (strcmp(p + 1, "profile") == 0) { result.profile = true; } else if (memcmp(p + 1, "transition=", 11) == 0) { result.transitions.push_back(p + 1 + 11); } else if (strcmp(p + 1, "v") == 0) { result.verbose = true; } else if (strcmp(p + 1, "vcolumns") == 0) { result.vcolumns = true; } else if (strcmp(p + 1, "vdmd") == 0) { result.vdmd = true; } else if (strcmp(p + 1, "vgc") == 0) { result.vgc = true; } else if (strcmp(p + 1, "vtls") == 0) { result.logTlsUse = true; } else if (strcmp(p + 1, "v1") == 0) { error("use DMD 1.0 series compilers for -v1 switch"); break; } else if (memcmp(p + 1, "verrors", 7) == 0) { if (p[8] == '=' && isdigit(static_cast(p[9]))) { long num; char *endp; errno = 0; num = strtol(p + 9, &endp, 10); if (*endp || errno || num > INT_MAX) { goto Lerror; } // Bugzilla issue number result.errorLimit = static_cast(num); result.errorLimitSet = true; } else { goto Lerror; } } else if (strcmp(p + 1, "w") == 0) { result.warnings = Warnings::asErrors; } else if (strcmp(p + 1, "wi") == 0) { result.warnings = Warnings::informational; } else if (strcmp(p + 1, "O") == 0) { result.optimize = true; } else if (p[1] == 'o') { switch (p[2]) { case '-': result.noObj = true; break; case 'd': if (!p[3]) { goto Lnoarg; } result.objDir = p + 3; break; case 'f': if (!p[3]) { goto Lnoarg; } result.objName = p + 3; break; case 'p': if (p[3]) { goto Lerror; } result.preservePaths = 1; break; case 0: error("-o no longer supported, use -of or -od"); break; default: goto Lerror; } } else if (p[1] == 'D') { result.generateDocs = true; switch (p[2]) { case 'd': if (!p[3]) { goto Lnoarg; } result.docDir = p + 3; break; case 'f': if (!p[3]) { goto Lnoarg; } result.docName = p + 3; break; case 0: break; default: goto Lerror; } } else if (p[1] == 'H') { result.generateHeaders = true; switch (p[2]) { case 'd': if (!p[3]) { goto Lnoarg; } result.headerDir = p + 3; break; case 'f': if (!p[3]) { goto Lnoarg; } result.headerName = p + 3; break; case 0: break; default: goto Lerror; } } else if (p[1] == 'X') { result.generateJson = true; switch (p[2]) { case 'f': if (!p[3]) { goto Lnoarg; } result.jsonName = p + 3; break; case 0: break; default: goto Lerror; } } else if (strcmp(p + 1, "ignore") == 0) { result.ignoreUnsupportedPragmas = true; } else if (strcmp(p + 1, "property") == 0) { result.enforcePropertySyntax = true; } else if (strcmp(p + 1, "inline") == 0) { result.enableInline = true; } else if (strcmp(p + 1, "lib") == 0) { result.emitStaticLib = true; } else if (strcmp(p + 1, "quiet") == 0) { result.quiet = 1; } else if (strcmp(p + 1, "release") == 0) { result.release = 1; } else if (strcmp(p + 1, "noboundscheck") == 0) { warning("The -noboundscheck switch is deprecated, use -boundscheck=off " "instead."); result.boundsChecks = BoundsCheck::off; } else if (memcmp(p + 1, "boundscheck", 11) == 0) { if (p[12] == '=') { if (strcmp(p + 13, "on") == 0) { result.boundsChecks = BoundsCheck::on; } else if (strcmp(p + 13, "safeonly") == 0) { result.boundsChecks = BoundsCheck::safeOnly; } else if (strcmp(p + 13, "off") == 0) { result.boundsChecks = BoundsCheck::off; } else { goto Lerror; } } } else if (strcmp(p + 1, "unittest") == 0) { result.emitUnitTests = 1; } else if (p[1] == 'I') { result.modulePaths.push_back(p + 2); } else if (p[1] == 'J') { result.importPaths.push_back(p + 2); } else if (memcmp(p + 1, "debug", 5) == 0 && p[6] != 'l') { // Parse: // -debug // -debug=number // -debug=identifier if (p[6] == '=') { if (isdigit(static_cast(p[7]))) { long level; errno = 0; level = strtol(p + 7, &p, 10); if (*p || errno || level > INT_MAX) { goto Lerror; } result.debugLevel = static_cast(level); } else { result.debugIdentifiers.push_back(p + 7); } } else if (p[6]) { goto Lerror; } else { result.debugFlag = true; } } else if (memcmp(p + 1, "version", 5) == 0) { // Parse: // -version=number // -version=identifier if (p[8] == '=') { if (isdigit(static_cast(p[9]))) { long level; errno = 0; level = strtol(p + 9, &p, 10); if (*p || errno || level > INT_MAX) { goto Lerror; } result.versionLevel = static_cast(level); } else { result.versionIdentifiers.push_back(p + 9); } } else { goto Lerror; } } else if (strcmp(p + 1, "-b") == 0) { result.hiddenDebugB = 1; } else if (strcmp(p + 1, "-c") == 0) { result.hiddenDebugC = 1; } else if (strcmp(p + 1, "-f") == 0) { result.hiddenDebugF = 1; } else if (strcmp(p + 1, "-help") == 0) { printUsage(originalArgv[0], ldcPath); exit(EXIT_SUCCESS); } else if (strcmp(p + 1, "-version") == 0) { // Print version information by actually invoking ldc -version. const char *versionargs[] = {ldcPath.c_str(), "-version", nullptr}; execute(ldcPath, versionargs); exit(EXIT_SUCCESS); } else if (strcmp(p + 1, "-r") == 0) { result.hiddenDebugR = 1; } else if (strcmp(p + 1, "-x") == 0) { result.hiddenDebugX = 1; } else if (strcmp(p + 1, "-y") == 0) { result.hiddenDebugY = 1; } else if (p[1] == 'L') { result.linkerSwitches.push_back(p + 2); } else if (memcmp(p + 1, "defaultlib=", 11) == 0) { result.defaultLibName = p + 1 + 11; } else if (memcmp(p + 1, "debuglib=", 9) == 0) { result.debugLibName = p + 1 + 9; } else if (memcmp(p + 1, "deps", 4) == 0) { result.moduleDepsFile = p + 1 + (p[1 + 4] == '=' ? 5 : 4); } else if (memcmp(p + 1, "man", 3) == 0) { browse("http://wiki.dlang.org/LDC"); exit(EXIT_SUCCESS); } else if (strcmp(p + 1, "run") == 0) { result.run = true; int runargCount = ((i >= originalArgc) ? argc : originalArgc) - i - 1; if (runargCount) { result.files.push_back(argv[i + 1]); result.runArgs = std::vector(argv + i + 2, argv + i + runargCount + 1); i += runargCount; } else { result.run = false; goto Lnoarg; } } else if (p[1] == 'C') { result.unknownSwitches.push_back(concat("-", p + 2)); } else { Lerror: result.unknownSwitches.push_back(p); continue; Lnoarg: error("argument expected for switch '%s'", p); continue; } } else { // FIXME: #if TARGET_WINDOS llvm::StringRef ext = ls::path::extension(p); if (ext.equals_lower("exe")) { result.objName = p; continue; } // #endif result.files.push_back(p); } } if (result.files.empty()) { printUsage(originalArgv[0], ldcPath); error("No source file specified."); } return result; } void pushSwitches(const char *prefix, const std::vector &vals, std::vector &r) { for (auto v : vals) { r.push_back(concat(prefix, v)); } } /** * Appends the LDC command line parameters corresponding to the given set of * parameters to r. */ void buildCommandLine(std::vector &r, const Params &p) { if (p.allinst) { r.push_back("-allinst"); } if (p.useDeprecated == Deprecated::allow) { r.push_back("-d"); } if (p.useDeprecated == Deprecated::error) { r.push_back("-de"); } if (p.compileOnly) { r.push_back("-c"); } if (p.coverage) { r.push_back("-cov"); } if (p.emitSharedLib) { r.push_back("-shared"); } if (p.pic) { r.push_back("-relocation-model=pic"); } if (p.emitMap) { warning("Map file generation not yet supported by LDC."); } if (!p.emitStaticLib && ((!p.multiObj && !p.compileOnly) || p.objName)) { r.push_back("-singleobj"); } if (p.debugInfo == Debug::normal) { r.push_back("-g"); } else if (p.debugInfo == Debug::pretendC) { r.push_back("-gc"); } if (p.alwaysStackFrame) { r.push_back("-disable-fp-elim"); } if (p.targetModel == Model::m32) { r.push_back("-m32"); } else if (p.targetModel == Model::m64) { r.push_back("-m64"); } if (p.profile) { warning("CPU profile generation not yet supported by LDC."); } if (p.verbose) { r.push_back("-v"); } if (p.vcolumns) { r.push_back("-vcolumns"); } if (p.vgc) { r.push_back("-vgc"); } if (p.logTlsUse) { r.push_back("-transition=tls"); } if (p.errorLimitSet) { r.push_back(concat("-verrors=", p.errorLimit)); } if (p.warnings == Warnings::asErrors) { r.push_back("-w"); } else if (p.warnings == Warnings::informational) { r.push_back("-wi"); } if (p.optimize) { r.push_back("-O3"); } if (p.noObj) { r.push_back("-o-"); } if (p.objDir) { r.push_back(concat("-od=", p.objDir)); } if (p.objName) { r.push_back(concat("-of=", p.objName)); } if (p.preservePaths) { r.push_back("-op"); } if (p.generateDocs) { r.push_back("-D"); } if (p.docDir) { r.push_back(concat("-Dd=", p.docDir)); } if (p.docName) { r.push_back(concat("-Df=", p.docName)); } if (p.generateHeaders) { r.push_back("-H"); } if (p.headerDir) { r.push_back(concat("-Hd=", p.headerDir)); } if (p.headerName) { r.push_back(concat("-Hf=", p.headerName)); } if (p.generateJson) { r.push_back("-X"); } if (p.jsonName) { r.push_back(concat("-Xf=", p.jsonName)); } if (p.ignoreUnsupportedPragmas) { r.push_back("-ignore"); } if (p.enforcePropertySyntax) { r.push_back("-property"); } if (p.enableInline) { // -inline also influences .di generation with DMD. r.push_back("-enable-inlining"); r.push_back("-Hkeep-all-bodies"); } if (p.emitStaticLib) { r.push_back("-lib"); } // -quiet is the default in (newer?) frontend versions, just ignore it. if (p.release) { r.push_back("-release"); // Also disables boundscheck. } if (p.boundsChecks == BoundsCheck::on) { r.push_back("-boundscheck=on"); } if (p.boundsChecks == BoundsCheck::safeOnly) { r.push_back("-boundscheck=safeonly"); } if (p.boundsChecks == BoundsCheck::off) { r.push_back("-boundscheck=off"); } if (p.emitUnitTests) { r.push_back("-unittest"); } pushSwitches("-I=", p.modulePaths, r); pushSwitches("-J=", p.importPaths, r); if (p.debugFlag) { r.push_back("-d-debug"); } if (p.debugLevel) { r.push_back(concat("-d-debug=", p.debugLevel)); } pushSwitches("-d-debug=", p.debugIdentifiers, r); if (p.versionLevel) { r.push_back(concat("-d-version=", p.versionLevel)); } pushSwitches("-d-version=", p.versionIdentifiers, r); pushSwitches("-L=", p.linkerSwitches, r); pushSwitches("-transition=", p.transitions, r); if (p.defaultLibName) { r.push_back(concat("-defaultlib=", p.defaultLibName)); } if (p.debugLibName) { r.push_back(concat("-debuglib=", p.debugLibName)); } if (p.moduleDepsFile) { r.push_back(p.moduleDepsFile[0] == 0 ? "-deps" : concat("-deps=", p.moduleDepsFile)); } if (p.color == Color::on) { r.push_back("-enable-color"); } if (p.color == Color::off) { r.push_back("-disable-color"); } if (p.useDIP25) { r.push_back("-dip25"); } if (p.conf) { r.push_back(concat("-conf=", p.conf)); } if (p.hiddenDebugB) { r.push_back("-hidden-debug-b"); } if (p.hiddenDebugC) { r.push_back("-hidden-debug-c"); } if (p.hiddenDebugF) { r.push_back("-hidden-debug-f"); } if (p.hiddenDebugR) { r.push_back("-hidden-debug-r"); } if (p.hiddenDebugX) { r.push_back("-hidden-debug-x"); } if (p.hiddenDebugY) { r.push_back("-hidden-debug-y"); } r.insert(r.end(), p.unknownSwitches.begin(), p.unknownSwitches.end()); if (p.run) { r.push_back("-run"); } r.insert(r.end(), p.files.begin(), p.files.end()); r.insert(r.end(), p.runArgs.begin(), p.runArgs.end()); } /** * Returns the OS-dependent length limit for the command line when invoking * subprocesses. */ size_t maxCommandLineLen() { #if defined(HAVE_SC_ARG_MAX) // http://www.in-ulm.de/~mascheck/various/argmax – the factor 2 is just // a wild guess to account for the enviroment. return sysconf(_SC_ARG_MAX) / 2; #elif defined(_WIN32) // http://blogs.msdn.com/b/oldnewthing/archive/2003/12/10/56028.aspx return 32767; #else #error "Do not know how to determine maximum command line length." #endif } /** * Tries to locate an executable with the given name, or an invalid path if * nothing was found. Search paths: 1. Directory where this binary resides. * 2. System PATH. */ std::string locateBinary(std::string exeName) { std::string path = exe_path::prependBinDir(exeName.c_str()); if (ls::fs::can_execute(path)) { return path; } #if LDC_LLVM_VER >= 306 llvm::ErrorOr res = ls::findProgramByName(exeName); path = res ? res.get() : std::string(); #else path = ls::FindProgramByName(exeName); #endif if (ls::fs::can_execute(path)) { return path; } return ""; } /** * Makes sure the given directory (absolute or relative) exists on disk. */ static void createOutputDir(const char *dir) { if (ls::fs::create_directories(dir)) { error("Could not create output directory '%s'.", dir); } } static size_t addStrlen(size_t acc, const char *str) { if (!str) { return acc; } return acc + strlen(str); } // In driver/main.d int main(int argc, char **argv); int cppmain(int argc, char **argv) { exe_path::initialize(argv[0]); std::string ldcExeName = LDC_EXE_NAME; #ifdef _WIN32 ldcExeName += ".exe"; #endif std::string ldcPath = locateBinary(ldcExeName); if (ldcPath.empty()) { error("Could not locate " LDC_EXE_NAME " executable."); } // We need to manually set up argv[0] and the terminating NULL. std::vector args; args.push_back(ldcPath.c_str()); Params p = parseArgs(argc, argv, ldcPath); buildCommandLine(args, p); if (p.vdmd) { printf(" -- Invoking:"); for (auto &arg : args) { printf(" %s", arg); } puts(""); } args.push_back(nullptr); // On Linux, DMD creates output directores that don't already exist, while // LDC does not (and neither does GDC). Do this here for rdmd compatibility. if (p.objName) { llvm::SmallString<256> outputPath(p.objName); ls::path::remove_filename(outputPath); if (!outputPath.empty()) { createOutputDir(outputPath.c_str()); } } if (p.objDir) { createOutputDir(p.objDir); } // Check if we need to write out a response file. size_t totalLen = std::accumulate(args.begin(), args.end(), 0, addStrlen); if (totalLen > maxCommandLineLen()) { int rspFd; llvm::SmallString<128> rspPath; if (ls::fs::createUniqueFile("ldmd-%%-%%-%%-%%.rsp", rspFd, rspPath)) { error("Could not open temporary response file."); } { llvm::raw_fd_ostream rspOut(rspFd, /*shouldClose=*/true); for (auto arg : args) { rspOut << arg << '\n'; } } std::string rspArg = "@"; rspArg += rspPath.str(); std::vector newArgs; newArgs.push_back(argv[0]); newArgs.push_back(rspArg.c_str()); newArgs.push_back(nullptr); int rc = execute(ldcPath, &newArgs[0]); if (ls::fs::remove(rspPath.str())) { warning("Could not remove response file."); } return rc; } return execute(ldcPath, &args[0]); }