diff --git a/src/ddmd/mars.d b/src/ddmd/mars.d index 1e42a5e71665..26482517af63 100644 --- a/src/ddmd/mars.d +++ b/src/ddmd/mars.d @@ -370,1734 +370,1754 @@ private int tryMain(size_t argc, const(char)** argv) getenv_setargv(readFromEnv(&environment, "DFLAGS"), &arguments); updateRealEnvironment(&environment); environment.reset(1); // don't need environment cache any more - version (none) + + parseCommandLine(arguments, argc, global.params, files); + + if (global.params.color) + global.console = Console.create(core.stdc.stdio.stderr); + + global.params.cpu = setTargetCPU(global.params.cpu); + if (global.params.is64bit != is64bit) + error(Loc(), "the architecture must not be changed in the %s section of %s", envsection.ptr, global.inifilename); + if (global.params.enforcePropertySyntax) { - for (size_t i = 0; i < arguments.dim; i++) + /*NOTE: -property used to disallow calling non-properties + without parentheses. This behaviour has fallen from grace. + Phobos dropped support for it while dmd still recognized it, so + that the switch has effectively not been supported. Time to + remove it from dmd. + Step 1 (2.069): Deprecate -property and ignore it. */ + Loc loc; + deprecation(loc, "The -property switch is deprecated and has no " ~ + "effect anymore."); + /* Step 2: Remove -property. Throw an error when it's set. + Do this by removing global.params.enforcePropertySyntax and the code + above that sets it. Let it be handled as an unrecognized switch. + Step 3: Possibly reintroduce -property with different semantics. + Any new semantics need to be decided on first. */ + } + // Target uses 64bit pointers. + global.params.isLP64 = global.params.is64bit; + if (global.errors) + { + fatal(); + } + if (files.dim == 0) + { + usage(); + return EXIT_FAILURE; + } + static if (TARGET_OSX) + { + global.params.pic = 1; + } + static if (TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS) + { + if (global.params.lib && global.params.dll) + error(Loc(), "cannot mix -lib and -shared"); + } + static if (TARGET_WINDOS) + { + if (!global.params.mscrtlib) + global.params.mscrtlib = "libcmt"; + } + if (global.params.useArrayBounds == BOUNDSCHECKdefault) + { + // Set the real default value + global.params.useArrayBounds = global.params.release ? BOUNDSCHECKsafeonly : BOUNDSCHECKon; + } + if (global.params.release) + { + global.params.useInvariants = false; + global.params.useIn = false; + global.params.useOut = false; + global.params.useAssert = false; + global.params.useSwitchError = false; + } + if (global.params.useUnitTests) + global.params.useAssert = true; + if (!global.params.obj || global.params.lib) + global.params.link = false; + if (global.params.link) + { + global.params.exefile = global.params.objname; + global.params.oneobj = true; + if (global.params.objname) { - printf("arguments[%d] = '%s'\n", i, arguments[i]); + /* Use this to name the one object file with the same + * name as the exe file. + */ + global.params.objname = cast(char*)FileName.forceExt(global.params.objname, global.obj_ext); + /* If output directory is given, use that path rather than + * the exe file path. + */ + if (global.params.objdir) + { + const(char)* name = FileName.name(global.params.objname); + global.params.objname = cast(char*)FileName.combine(global.params.objdir, name); + } } } - for (size_t i = 1; i < arguments.dim; i++) + else if (global.params.run) { - const(char)* p = arguments[i]; - if (*p == '-') + error(Loc(), "flags conflict with -run"); + fatal(); + } + else if (global.params.lib) + { + global.params.libname = global.params.objname; + global.params.objname = null; + // Haven't investigated handling these options with multiobj + if (!global.params.cov && !global.params.trace) + global.params.multiobj = true; + } + else + { + if (global.params.objname && files.dim > 1) { - if (strcmp(p + 1, "allinst") == 0) - global.params.allInst = true; - else if (strcmp(p + 1, "de") == 0) - global.params.useDeprecated = 0; - else if (strcmp(p + 1, "d") == 0) - global.params.useDeprecated = 1; - else if (strcmp(p + 1, "dw") == 0) - global.params.useDeprecated = 2; - else if (strcmp(p + 1, "c") == 0) - global.params.link = false; - else if (memcmp(p + 1, cast(char*)"color", 5) == 0) + global.params.oneobj = true; + //error("multiple source files, but only one .obj name"); + //fatal(); + } + } + + // Predefined version identifiers + addDefaultVersionIdentifiers(); + + setDefaultLibrary(); + + // Initialization + Type._init(); + Id.initialize(); + Module._init(); + Target._init(); + Expression._init(); + Objc._init(); + builtin_init(); + + if (global.params.verbose) + { + fprintf(global.stdmsg, "binary %s\n", global.params.argv0); + fprintf(global.stdmsg, "version %s\n", global._version); + fprintf(global.stdmsg, "config %s\n", global.inifilename ? global.inifilename : "(none)"); + } + //printf("%d source files\n",files.dim); + + // Build import search path + + static Strings* buildPath(Strings* imppath) + { + Strings* result = null; + if (imppath) + { + foreach (const path; *imppath) { - global.params.color = true; - // Parse: - // -color - // -color=on|off - if (p[6] == '=') + Strings* a = FileName.splitPath(path); + if (a) { - if (strcmp(p + 7, "off") == 0) - global.params.color = false; - else if (strcmp(p + 7, "on") != 0) - goto Lerror; + if (!result) + result = new Strings(); + result.append(a); } - else if (p[6]) - goto Lerror; } - else if (memcmp(p + 1, cast(char*)"conf=", 5) == 0) + } + return result; + } + + global.path = buildPath(global.params.imppath); + global.filePath = buildPath(global.params.fileImppath); + + if (global.params.addMain) + { + files.push(cast(char*)global.main_d); // a dummy name, we never actually look up this file + } + // Create Modules + Modules modules; + modules.reserve(files.dim); + bool firstmodule = true; + for (size_t i = 0; i < files.dim; i++) + { + const(char)* name; + version (Windows) + { + files[i] = toWinPath(files[i]); + } + const(char)* p = files[i]; + p = FileName.name(p); // strip path + const(char)* ext = FileName.ext(p); + char* newname; + if (ext) + { + /* Deduce what to do with a file based on its extension + */ + if (FileName.equals(ext, global.obj_ext)) { - // ignore, already handled above + global.params.objfiles.push(files[i]); + libmodules.push(files[i]); + continue; } - else if (memcmp(p + 1, cast(char*)"cov", 3) == 0) + if (FileName.equals(ext, global.lib_ext)) { - global.params.cov = true; - // Parse: - // -cov - // -cov=nnn - if (p[4] == '=') - { - if (isdigit(cast(char)p[5])) - { - long percent; - errno = 0; - percent = strtol(p + 5, &p, 10); - if (*p || errno || percent > 100) - goto Lerror; - global.params.covPercent = cast(ubyte)percent; - } - else - goto Lerror; - } - else if (p[4]) - goto Lerror; + global.params.libfiles.push(files[i]); + libmodules.push(files[i]); + continue; } - else if (strcmp(p + 1, "shared") == 0) - global.params.dll = true; - else if (strcmp(p + 1, "dylib") == 0) + static if (TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS) { - static if (TARGET_OSX) - { - Loc loc; - deprecation(loc, "use -shared instead of -dylib"); - global.params.dll = true; - } - else + if (FileName.equals(ext, global.dll_ext)) { - goto Lerror; + global.params.dllfiles.push(files[i]); + libmodules.push(files[i]); + continue; } } - else if (strcmp(p + 1, "fPIC") == 0) + if (strcmp(ext, global.ddoc_ext) == 0) { - static if (TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS) - { - global.params.pic = 1; - } - else - { - goto Lerror; - } + global.params.ddocfiles.push(files[i]); + continue; } - else if (strcmp(p + 1, "map") == 0) - global.params.map = true; - else if (strcmp(p + 1, "multiobj") == 0) - global.params.multiobj = true; - else if (strcmp(p + 1, "g") == 0) - global.params.symdebug = 1; - else if (strcmp(p + 1, "gc") == 0) + if (FileName.equals(ext, global.json_ext)) { - Loc loc; - deprecation(loc, "use -g instead of -gc"); - global.params.symdebug = 2; + global.params.doJsonGeneration = true; + global.params.jsonfilename = files[i]; + continue; } - else if (strcmp(p + 1, "gf") == 0) - { - if (!global.params.symdebug) - global.params.symdebug = 1; - global.params.symdebugref = true; - } - else if (strcmp(p + 1, "gs") == 0) - global.params.alwaysframe = true; - else if (strcmp(p + 1, "gx") == 0) - global.params.stackstomp = true; - else if (strcmp(p + 1, "gt") == 0) - { - error(Loc(), "use -profile instead of -gt"); - global.params.trace = true; - } - else if (strcmp(p + 1, "m32") == 0) + if (FileName.equals(ext, global.map_ext)) { - global.params.is64bit = false; - global.params.mscoff = false; + global.params.mapfile = files[i]; + continue; } - else if (strcmp(p + 1, "m64") == 0) + static if (TARGET_WINDOS) { - global.params.is64bit = true; - static if (TARGET_WINDOS) + if (FileName.equals(ext, "res")) { - global.params.mscoff = true; + global.params.resfile = files[i]; + continue; } - } - else if (strcmp(p + 1, "m32mscoff") == 0) - { - static if (TARGET_WINDOS) + if (FileName.equals(ext, "def")) { - global.params.is64bit = 0; - global.params.mscoff = true; + global.params.deffile = files[i]; + continue; } - else + if (FileName.equals(ext, "exe")) { - error(Loc(), "-m32mscoff can only be used on windows"); + assert(0); // should have already been handled } } - else if (strncmp(p + 1, "mscrtlib=", 9) == 0) + /* Examine extension to see if it is a valid + * D source file extension + */ + if (FileName.equals(ext, global.mars_ext) || FileName.equals(ext, global.hdr_ext) || FileName.equals(ext, "dd")) { - static if (TARGET_WINDOS) - { - global.params.mscrtlib = p + 10; - } - else + ext--; // skip onto '.' + assert(*ext == '.'); + newname = cast(char*)mem.xmalloc((ext - p) + 1); + memcpy(newname, p, ext - p); + newname[ext - p] = 0; // strip extension + name = newname; + if (name[0] == 0 || strcmp(name, "..") == 0 || strcmp(name, ".") == 0) { - error(Loc(), "-mscrtlib"); + Linvalid: + error(Loc(), "invalid file name '%s'", files[i]); + fatal(); } } - else if (memcmp(p + 1, cast(char*)"profile", 7) == 0) + else { - // Parse: - // -profile - // -profile=gc - if (p[8] == '=') - { - if (strcmp(p + 9, "gc") == 0) - global.params.tracegc = true; - else - goto Lerror; - } - else if (p[8]) - goto Lerror; - else - global.params.trace = true; + error(Loc(), "unrecognized file extension %s", ext); + fatal(); } - else if (strcmp(p + 1, "v") == 0) - global.params.verbose = true; - else if (strcmp(p + 1, "vcg-ast") == 0) - global.params.vcg_ast = true; - else if (strcmp(p + 1, "vtls") == 0) - global.params.vtls = true; - else if (strcmp(p + 1, "vcolumns") == 0) - global.params.showColumns = true; - else if (strcmp(p + 1, "vgc") == 0) - global.params.vgc = true; - else if (memcmp(p + 1, cast(char*)"verrors", 7) == 0) + } + else + { + name = p; + if (!*name) + goto Linvalid; + } + /* At this point, name is the D source file name stripped of + * its path and extension. + */ + auto id = Identifier.idPool(name, strlen(name)); + auto m = new Module(files[i], id, global.params.doDocComments, global.params.doHdrGeneration); + modules.push(m); + if (firstmodule) + { + global.params.objfiles.push(m.objfile.name.str); + firstmodule = false; + } + } + // Read files + /* Start by "reading" the dummy main.d file + */ + if (global.params.addMain) + { + for (size_t i = 0; 1; i++) + { + assert(i != modules.dim); + Module m = modules[i]; + if (strcmp(m.srcfile.name.str, global.main_d) == 0) { - if (p[8] == '=' && isdigit(cast(char)p[9])) - { - long num; - errno = 0; - num = strtol(p + 9, &p, 10); - if (*p || errno || num > INT_MAX) - goto Lerror; - global.errorLimit = cast(uint)num; - } - else if (memcmp(p + 9, cast(char*)"spec", 4) == 0) - { - global.params.showGaggedErrors = true; - } - else - goto Lerror; + string buf = "int main(){return 0;}"; + m.srcfile.setbuffer(cast(void*)buf.ptr, buf.length); + m.srcfile._ref = 1; + break; } - else if (memcmp(p + 1, "mcpu".ptr, 4) == 0) + } + } + enum ASYNCREAD = false; + static if (ASYNCREAD) + { + // Multi threaded + AsyncRead* aw = AsyncRead.create(modules.dim); + for (size_t i = 0; i < modules.dim; i++) + { + Module m = modules[i]; + aw.addFile(m.srcfile); + } + aw.start(); + } + else + { + // Single threaded + for (size_t i = 0; i < modules.dim; i++) + { + Module m = modules[i]; + m.read(Loc()); + } + } + // Parse files + bool anydocfiles = false; + size_t filecount = modules.dim; + for (size_t filei = 0, modi = 0; filei < filecount; filei++, modi++) + { + Module m = modules[modi]; + if (global.params.verbose) + fprintf(global.stdmsg, "parse %s\n", m.toChars()); + if (!Module.rootModule) + Module.rootModule = m; + m.importedFrom = m; // m.isRoot() == true + if (!global.params.oneobj || modi == 0 || m.isDocFile) + m.deleteObjFile(); + static if (ASYNCREAD) + { + if (aw.read(filei)) { - // Parse: - // -mcpu=identifier - if (p[5] == '=') - { - if (strcmp(p + 6, "?") == 0) - { - printf(" -CPU architectures supported by -mcpu=id: - =? list information on all architecture choices - =baseline use default architecture as determined by target - =avx use AVX 1 instructions - =avx2 use AVX 2 instructions - =native use CPU architecture that this compiler is running on -"); - exit(EXIT_SUCCESS); - } - else if (Identifier.isValidIdentifier(p + 6)) - { - const ident = p + 6; - switch (ident[0 .. strlen(ident)]) - { - case "baseline": - global.params.cpu = CPU.baseline; - break; - case "avx": - global.params.cpu = CPU.avx; - break; - case "avx2": - global.params.cpu = CPU.avx2; - break; - case "native": - global.params.cpu = CPU.native; - break; - default: - goto Lerror; - } - } - else - goto Lerror; - } - else - goto Lerror; + error(Loc(), "cannot read file %s", m.srcfile.name.toChars()); + fatal(); } - else if (memcmp(p + 1, cast(char*)"transition", 10) == 0) + } + m.parse(); + if (m.isDocFile) + { + anydocfiles = true; + gendocfile(m); + // Remove m from list of modules + modules.remove(modi); + modi--; + // Remove m's object file from list of object files + for (size_t j = 0; j < global.params.objfiles.dim; j++) { - // Parse: - // -transition=number - if (p[11] == '=') + if (m.objfile.name.str == (*global.params.objfiles)[j]) { - if (strcmp(p + 12, "?") == 0) - { - printf(" -Language changes listed by -transition=id: - =all list information on all language changes - =checkimports give deprecation messages about 10378 anomalies - =complex,14488 list all usages of complex or imaginary types - =field,3449 list all non-mutable fields which occupy an object instance - =import,10378 revert to single phase name lookup - =tls list all variables going into thread local storage -"); - exit(EXIT_SUCCESS); - } - if (isdigit(cast(char)p[12])) - { - long num; - errno = 0; - num = strtol(p + 12, &p, 10); - if (*p || errno || num > INT_MAX) - goto Lerror; - // Bugzilla issue number - switch (num) - { - case 3449: - global.params.vfield = true; - break; - case 10378: - global.params.bug10378 = true; - break; - case 14488: - global.params.vcomplex = true; - break; - default: - goto Lerror; - } - } - else if (Identifier.isValidIdentifier(p + 12)) - { - const ident = p + 12; - switch (ident[0 .. strlen(ident)]) - { - case "all": - global.params.vtls = true; - global.params.vfield = true; - global.params.vcomplex = true; - break; - case "checkimports": - global.params.check10378 = true; - break; - case "complex": - global.params.vcomplex = true; - break; - case "field": - global.params.vfield = true; - break; - case "import": - global.params.bug10378 = true; - break; - case "tls": - global.params.vtls = true; - break; - default: - goto Lerror; - } - } - else - goto Lerror; - } - else - goto Lerror; - } - else if (strcmp(p + 1, "w") == 0) - global.params.warnings = 1; - else if (strcmp(p + 1, "wi") == 0) - global.params.warnings = 2; - else if (strcmp(p + 1, "O") == 0) - global.params.optimize = true; - else if (p[1] == 'o') - { - const(char)* path; - switch (p[2]) - { - case '-': - global.params.obj = false; - break; - case 'd': - if (!p[3]) - goto Lnoarg; - path = p + 3 + (p[3] == '='); - version (Windows) - { - path = toWinPath(path); - } - global.params.objdir = path; - break; - case 'f': - if (!p[3]) - goto Lnoarg; - path = p + 3 + (p[3] == '='); - version (Windows) - { - path = toWinPath(path); - } - global.params.objname = path; - break; - case 'p': - if (p[3]) - goto Lerror; - global.params.preservePaths = true; - break; - case 0: - error(Loc(), "-o no longer supported, use -of or -od"); + global.params.objfiles.remove(j); break; - default: - goto Lerror; } } - else if (p[1] == 'D') + if (global.params.objfiles.dim == 0) + global.params.link = false; + } + } + static if (ASYNCREAD) + { + AsyncRead.dispose(aw); + } + if (anydocfiles && modules.dim && (global.params.oneobj || global.params.objname)) + { + error(Loc(), "conflicting Ddoc and obj generation options"); + fatal(); + } + if (global.errors) + fatal(); + + if (global.params.doHdrGeneration) + { + /* Generate 'header' import files. + * Since 'header' import files must be independent of command + * line switches and what else is imported, they are generated + * before any semantic analysis. + */ + for (size_t i = 0; i < modules.dim; i++) + { + Module m = modules[i]; + if (global.params.verbose) + fprintf(global.stdmsg, "import %s\n", m.toChars()); + genhdrfile(m); + } + } + if (global.errors) + fatal(); + + // load all unconditional imports for better symbol resolving + for (size_t i = 0; i < modules.dim; i++) + { + Module m = modules[i]; + if (global.params.verbose) + fprintf(global.stdmsg, "importall %s\n", m.toChars()); + m.importAll(null); + } + if (global.errors) + fatal(); + + backend_init(); + + // Do semantic analysis + for (size_t i = 0; i < modules.dim; i++) + { + Module m = modules[i]; + if (global.params.verbose) + fprintf(global.stdmsg, "semantic %s\n", m.toChars()); + m.semantic(null); + } + //if (global.errors) + // fatal(); + Module.dprogress = 1; + Module.runDeferredSemantic(); + if (Module.deferred.dim) + { + for (size_t i = 0; i < Module.deferred.dim; i++) + { + Dsymbol sd = Module.deferred[i]; + sd.error("unable to resolve forward reference in definition"); + } + //fatal(); + } + + // Do pass 2 semantic analysis + for (size_t i = 0; i < modules.dim; i++) + { + Module m = modules[i]; + if (global.params.verbose) + fprintf(global.stdmsg, "semantic2 %s\n", m.toChars()); + m.semantic2(null); + } + Module.runDeferredSemantic2(); + if (global.errors) + fatal(); + + // Do pass 3 semantic analysis + for (size_t i = 0; i < modules.dim; i++) + { + Module m = modules[i]; + if (global.params.verbose) + fprintf(global.stdmsg, "semantic3 %s\n", m.toChars()); + m.semantic3(null); + } + Module.runDeferredSemantic3(); + if (global.errors) + fatal(); + + // Scan for functions to inline + if (global.params.useInline) + { + for (size_t i = 0; i < modules.dim; i++) + { + Module m = modules[i]; + if (global.params.verbose) + fprintf(global.stdmsg, "inline scan %s\n", m.toChars()); + inlineScanModule(m); + } + } + // Do not attempt to generate output files if errors or warnings occurred + if (global.errors || global.warnings) + fatal(); + + // inlineScan incrementally run semantic3 of each expanded functions. + // So deps file generation should be moved after the inlinig stage. + if (global.params.moduleDeps) + { + foreach (i; 1 .. modules[0].aimports.dim) + semantic3OnDependencies(modules[0].aimports[i]); + + OutBuffer* ob = global.params.moduleDeps; + if (global.params.moduleDepsFile) + { + auto deps = File(global.params.moduleDepsFile); + deps.setbuffer(cast(void*)ob.data, ob.offset); + writeFile(Loc(), &deps); + } + else + printf("%.*s", cast(int)ob.offset, ob.data); + } + + printCtfePerformanceStats(); + + Library library = null; + if (global.params.lib) + { + library = Library.factory(); + library.setFilename(global.params.objdir, global.params.libname); + // Add input object and input library files to output library + for (size_t i = 0; i < libmodules.dim; i++) + { + const(char)* p = libmodules[i]; + library.addObject(p, null); + } + } + // Generate output files + if (global.params.doJsonGeneration) + { + OutBuffer buf; + json_generate(&buf, &modules); + // Write buf to file + const(char)* name = global.params.jsonfilename; + if (name && name[0] == '-' && name[1] == 0) + { + // Write to stdout; assume it succeeds + size_t n = fwrite(buf.data, 1, buf.offset, stdout); + assert(n == buf.offset); // keep gcc happy about return values + } + else + { + /* The filename generation code here should be harmonized with Module::setOutfile() + */ + const(char)* jsonfilename; + if (name && *name) { - global.params.doDocComments = true; - switch (p[2]) - { - case 'd': - if (!p[3]) - goto Lnoarg; - global.params.docdir = p + 3 + (p[3] == '='); - break; - case 'f': - if (!p[3]) - goto Lnoarg; - global.params.docname = p + 3 + (p[3] == '='); - break; - case 0: - break; - default: - goto Lerror; - } + jsonfilename = FileName.defaultExt(name, global.json_ext); } - else if (p[1] == 'H') + else { - global.params.doHdrGeneration = true; - switch (p[2]) - { - case 'd': - if (!p[3]) - goto Lnoarg; - global.params.hdrdir = p + 3 + (p[3] == '='); - break; - case 'f': - if (!p[3]) - goto Lnoarg; - global.params.hdrname = p + 3 + (p[3] == '='); - break; - case 0: - break; - default: - goto Lerror; - } + // Generate json file name from first obj name + const(char)* n = (*global.params.objfiles)[0]; + n = FileName.name(n); + //if (!FileName::absolute(name)) + // name = FileName::combine(dir, name); + jsonfilename = FileName.forceExt(n, global.json_ext); } - else if (p[1] == 'X') - { - global.params.doJsonGeneration = true; - switch (p[2]) + ensurePathToNameExists(Loc(), jsonfilename); + auto jsonfile = new File(jsonfilename); + jsonfile.setbuffer(buf.data, buf.offset); + jsonfile._ref = 1; + writeFile(Loc(), jsonfile); + } + } + if (!global.errors && global.params.doDocComments) + { + for (size_t i = 0; i < modules.dim; i++) + { + Module m = modules[i]; + gendocfile(m); + } + } + if (global.params.vcg_ast) + { + import ddmd.hdrgen; + foreach (mod; modules) + { + auto buf = OutBuffer(); + buf.doindent = 1; + scope HdrGenState hgs; + hgs.fullDump = 1; + scope PrettyPrintVisitor ppv = new PrettyPrintVisitor(&buf, &hgs); + mod.accept(ppv); + + // write the output to $(filename).cg + auto modFilename = mod.srcfile.toChars(); + auto modFilenameLength = strlen(modFilename); + auto cgFilename = cast(char*)allocmemory(modFilenameLength + 4); + memcpy(cgFilename, modFilename, modFilenameLength); + cgFilename[modFilenameLength .. modFilenameLength + 4] = ".cg\0"; + auto cgFile = File(cgFilename); + cgFile.setbuffer(buf.data, buf.offset); + cgFile._ref = 1; + cgFile.write(); + } + } + if (!global.params.obj) + { + } + else if (global.params.oneobj) + { + if (modules.dim) + obj_start(cast(char*)modules[0].srcfile.toChars()); + for (size_t i = 0; i < modules.dim; i++) + { + Module m = modules[i]; + if (global.params.verbose) + fprintf(global.stdmsg, "code %s\n", m.toChars()); + genObjFile(m, false); + if (entrypoint && m == rootHasMain) + genObjFile(entrypoint, false); + } + if (!global.errors && modules.dim) + { + obj_end(library, modules[0].objfile); + } + } + else + { + for (size_t i = 0; i < modules.dim; i++) + { + Module m = modules[i]; + if (global.params.verbose) + fprintf(global.stdmsg, "code %s\n", m.toChars()); + obj_start(cast(char*)m.srcfile.toChars()); + genObjFile(m, global.params.multiobj); + if (entrypoint && m == rootHasMain) + genObjFile(entrypoint, global.params.multiobj); + obj_end(library, m.objfile); + obj_write_deferred(library); + if (global.errors && !global.params.lib) + m.deleteObjFile(); + } + } + if (global.params.lib && !global.errors) + library.write(); + backend_term(); + if (global.errors) + fatal(); + int status = EXIT_SUCCESS; + if (!global.params.objfiles.dim) + { + if (global.params.link) + error(Loc(), "no object files to link"); + } + else + { + if (global.params.link) + status = runLINK(); + if (global.params.run) + { + if (!status) + { + status = runProgram(); + /* Delete .obj files and .exe file + */ + for (size_t i = 0; i < modules.dim; i++) { - case 'f': - if (!p[3]) - goto Lnoarg; - global.params.jsonfilename = p + 3 + (p[3] == '='); - break; - case 0: - break; - default: - goto Lerror; + modules[i].deleteObjFile(); + if (global.params.oneobj) + break; } + remove(global.params.exefile); } - else if (strcmp(p + 1, "ignore") == 0) - global.params.ignoreUnsupportedPragmas = true; - else if (strcmp(p + 1, "property") == 0) - global.params.enforcePropertySyntax = true; - else if (strcmp(p + 1, "inline") == 0) - { - global.params.useInline = true; - global.params.hdrStripPlainFunctions = false; - } - else if (strcmp(p + 1, "dip25") == 0) - global.params.useDIP25 = true; - else if (strcmp(p + 1, "dip1000") == 0) - { - global.params.useDIP25 = true; - global.params.vsafe = true; - } - else if (strcmp(p + 1, "lib") == 0) - global.params.lib = true; - else if (strcmp(p + 1, "nofloat") == 0) - global.params.nofloat = true; - else if (strcmp(p + 1, "quiet") == 0) - { - // Ignore - } - else if (strcmp(p + 1, "release") == 0) - global.params.release = true; - else if (strcmp(p + 1, "betterC") == 0) - global.params.betterC = true; - else if (strcmp(p + 1, "noboundscheck") == 0) + } + } + return status; +} + + +/** + * Entry point which forwards to `tryMain`. + * + * Returns: + * Return code of the application + */ +int main() +{ + import core.memory; + import core.runtime; + + version (GC) + { + } + else + { + GC.disable(); + } + version(D_Coverage) + { + // for now we need to manually set the source path + string dirName(string path, char separator) + { + for (size_t i = path.length - 1; i > 0; i--) { - global.params.useArrayBounds = BOUNDSCHECKoff; + if (path[i] == separator) + return path[0..i]; } - else if (memcmp(p + 1, cast(char*)"boundscheck", 11) == 0) + return path; + } + version (Windows) + enum sourcePath = dirName(dirName(__FILE_FULL_PATH__, `\`), `\`); + else + enum sourcePath = dirName(dirName(__FILE_FULL_PATH__, '/'), '/'); + + dmd_coverSourcePath(sourcePath); + dmd_coverDestPath(sourcePath); + dmd_coverSetMerge(true); + } + + auto args = Runtime.cArgs(); + return tryMain(args.argc, cast(const(char)**)args.argv); +} + + +/** + * Parses an environment variable containing command-line flags + * and append them to `args`. + * + * This function is used to read the content of DFLAGS. + * Flags are separated based on spaces and tabs. + * + * Params: + * envvalue = The content of an environment variable + * args = Array to append the flags to, if any. + */ +private void getenv_setargv(const(char)* envvalue, Strings* args) +{ + if (!envvalue) + return; + char* p; + int instring; + int slash; + char c; + char* env = mem.xstrdup(envvalue); // create our own writable copy + //printf("env = '%s'\n", env); + while (1) + { + switch (*env) + { + case ' ': + case '\t': + env++; + break; + case 0: + return; + default: + args.push(env); // append + p = env; + slash = 0; + instring = 0; + c = 0; + while (1) { - // Parse: - // -boundscheck=[on|safeonly|off] - if (p[12] == '=') + c = *env++; + switch (c) { - if (strcmp(p + 13, "on") == 0) - { - global.params.useArrayBounds = BOUNDSCHECKon; - } - else if (strcmp(p + 13, "safeonly") == 0) - { - global.params.useArrayBounds = BOUNDSCHECKsafeonly; - } - else if (strcmp(p + 13, "off") == 0) + case '"': + p -= (slash >> 1); + if (slash & 1) { - global.params.useArrayBounds = BOUNDSCHECKoff; + p--; + goto Laddc; } - else - goto Lerror; - } - else - goto Lerror; - } - else if (strcmp(p + 1, "unittest") == 0) - global.params.useUnitTests = true; - else if (p[1] == 'I') - { - if (!global.params.imppath) - global.params.imppath = new Strings(); - global.params.imppath.push(p + 2 + (p[2] == '=')); - } - else if (p[1] == 'm' && p[2] == 'v' && p[3] == '=') - { - if (p[4] && strchr(p + 5, '=')) - { - if (!global.params.modFileAliasStrings) - global.params.modFileAliasStrings = new Strings(); - global.params.modFileAliasStrings.push(p + 4); - } - else - goto Lerror; - } - else if (p[1] == 'J') - { - if (!global.params.fileImppath) - global.params.fileImppath = new Strings(); - global.params.fileImppath.push(p + 2 + (p[2] == '=')); - } - else if (memcmp(p + 1, cast(char*)"debug", 5) == 0 && p[6] != 'l') - { - // Parse: - // -debug - // -debug=number - // -debug=identifier - if (p[6] == '=') - { - if (isdigit(cast(char)p[7])) - { - long level; - errno = 0; - level = strtol(p + 7, &p, 10); - if (*p || errno || level > INT_MAX) - goto Lerror; - DebugCondition.setGlobalLevel(cast(int)level); - } - else if (Identifier.isValidIdentifier(p + 7)) - DebugCondition.addGlobalIdent(p[7 .. p.strlen]); - else - goto Lerror; - } - else if (p[6]) - goto Lerror; - else - DebugCondition.setGlobalLevel(1); - } - else if (memcmp(p + 1, cast(char*)"version", 7) == 0) - { - // Parse: - // -version=number - // -version=identifier - if (p[8] == '=') - { - if (isdigit(cast(char)p[9])) - { - long level; - errno = 0; - level = strtol(p + 9, &p, 10); - if (*p || errno || level > INT_MAX) - goto Lerror; - VersionCondition.setGlobalLevel(cast(int)level); - } - else if (Identifier.isValidIdentifier(p + 9)) - VersionCondition.addGlobalIdent(p[9 .. p.strlen]); - else - goto Lerror; - } - else - goto Lerror; - } - else if (strcmp(p + 1, "-b") == 0) - global.params.debugb = true; - else if (strcmp(p + 1, "-c") == 0) - global.params.debugc = true; - else if (strcmp(p + 1, "-f") == 0) - global.params.debugf = true; - else if (strcmp(p + 1, "-help") == 0 || strcmp(p + 1, "h") == 0) - { - usage(); - exit(EXIT_SUCCESS); - } - else if (strcmp(p + 1, "-r") == 0) - global.params.debugr = true; - else if (strcmp(p + 1, "-version") == 0) - { - logo(); - exit(EXIT_SUCCESS); - } - else if (strcmp(p + 1, "-x") == 0) - global.params.debugx = true; - else if (strcmp(p + 1, "-y") == 0) - global.params.debugy = true; - else if (p[1] == 'L') - { - global.params.linkswitches.push(p + 2 + (p[2] == '=')); - } - else if (memcmp(p + 1, cast(char*)"defaultlib=", 11) == 0) - { - global.params.defaultlibname = p + 1 + 11; - } - else if (memcmp(p + 1, cast(char*)"debuglib=", 9) == 0) - { - global.params.debuglibname = p + 1 + 9; - } - else if (memcmp(p + 1, cast(char*)"deps", 4) == 0) - { - if (global.params.moduleDeps) - { - error(Loc(), "-deps[=file] can only be provided once!"); + instring ^= 1; + slash = 0; + continue; + case ' ': + case '\t': + if (instring) + goto Laddc; + *p = 0; + //if (wildcard) + // wildcardexpand(); // not implemented break; + case '\\': + slash++; + *p++ = c; + continue; + case 0: + *p = 0; + //if (wildcard) + // wildcardexpand(); // not implemented + return; + default: + Laddc: + slash = 0; + *p++ = c; + continue; } - if (p[5] == '=') - { - global.params.moduleDepsFile = p + 1 + 5; - if (!global.params.moduleDepsFile[0]) - goto Lnoarg; - } - else if (p[5] != '\0') - { - // Else output to stdout. - goto Lerror; - } - global.params.moduleDeps = new OutBuffer(); - } - else if (strcmp(p + 1, "main") == 0) - { - global.params.addMain = true; - } - else if (memcmp(p + 1, cast(char*)"man", 3) == 0) - { - version (Windows) - { - browse("http://dlang.org/dmd-windows.html"); - } - version (linux) - { - browse("http://dlang.org/dmd-linux.html"); - } - version (OSX) - { - browse("http://dlang.org/dmd-osx.html"); - } - version (FreeBSD) - { - browse("http://dlang.org/dmd-freebsd.html"); - } - version (OpenBSD) - { - browse("http://dlang.org/dmd-openbsd.html"); - } - exit(EXIT_SUCCESS); - } - else if (strcmp(p + 1, "run") == 0) - { - global.params.run = true; - size_t length = argc - i - 1; - if (length) - { - const(char)* ext = FileName.ext(arguments[i + 1]); - if (ext && FileName.equals(ext, "d") == 0 && FileName.equals(ext, "di") == 0) - { - error(Loc(), "-run must be followed by a source file, not '%s'", arguments[i + 1]); - break; - } - files.push(arguments[i + 1]); - global.params.runargs.setDim(length - 1); - for (size_t j = 0; j < length - 1; ++j) - { - global.params.runargs[j] = arguments[i + 2 + j]; - } - i += length; - } - else - { - global.params.run = false; - goto Lnoarg; - } - } - else if (p[1] == '\0') - files.push("__stdin.d"); - else - { - Lerror: - error(Loc(), "unrecognized switch '%s'", arguments[i]); - continue; - Lnoarg: - error(Loc(), "argument expected for switch '%s'", arguments[i]); - continue; + break; } } - else + } +} + +/** + * Parse command line arguments for -m32 or -m64 + * to detect the desired architecture. + * + * Params: + * args = Command line arguments + * arch = Default value to use for architecture. + * Should be "32" or "64" + * + * Returns: + * "32", "64" or "32mscoff" if the "-m32", "-m64", "-m32mscoff" flags were passed, + * respectively. If they weren't, return `arch`. + */ +private const(char)* parse_arch_arg(Strings* args, const(char)* arch) +{ + for (size_t i = 0; i < args.dim; ++i) + { + const(char)* p = (*args)[i]; + if (p[0] == '-') { - static if (TARGET_WINDOS) - { - const(char)* ext = FileName.ext(p); - if (ext && FileName.compare(ext, "exe") == 0) - { - global.params.objname = p; - continue; - } - if (strcmp(p, `/?`) == 0) - { - usage(); - exit(EXIT_SUCCESS); - } - } - files.push(p); + if (strcmp(p + 1, "m32") == 0 || strcmp(p + 1, "m32mscoff") == 0 || strcmp(p + 1, "m64") == 0) + arch = p + 2; + else if (strcmp(p + 1, "run") == 0) + break; } } + return arch; +} - if (global.params.color) - global.console = Console.create(core.stdc.stdio.stderr); - global.params.cpu = setTargetCPU(global.params.cpu); - if (global.params.is64bit != is64bit) - error(Loc(), "the architecture must not be changed in the %s section of %s", envsection.ptr, global.inifilename); - if (global.params.enforcePropertySyntax) +/** + * Parse command line arguments for -conf=path. + * + * Params: + * args = Command line arguments + * + * Returns: + * Path to the config file to use + */ +private const(char)* parse_conf_arg(Strings* args) +{ + const(char)* conf = null; + for (size_t i = 0; i < args.dim; ++i) { - /*NOTE: -property used to disallow calling non-properties - without parentheses. This behaviour has fallen from grace. - Phobos dropped support for it while dmd still recognized it, so - that the switch has effectively not been supported. Time to - remove it from dmd. - Step 1 (2.069): Deprecate -property and ignore it. */ - Loc loc; - deprecation(loc, "The -property switch is deprecated and has no " ~ - "effect anymore."); - /* Step 2: Remove -property. Throw an error when it's set. - Do this by removing global.params.enforcePropertySyntax and the code - above that sets it. Let it be handled as an unrecognized switch. - Step 3: Possibly reintroduce -property with different semantics. - Any new semantics need to be decided on first. */ - } - // Target uses 64bit pointers. - global.params.isLP64 = global.params.is64bit; - if (global.errors) + const(char)* p = (*args)[i]; + if (p[0] == '-') + { + if (strncmp(p + 1, "conf=", 5) == 0) + conf = p + 6; + else if (strcmp(p + 1, "run") == 0) + break; + } + } + return conf; +} + + +/** + * Set the default and debug libraries to link against, if not already set + * + * Must be called after argument parsing is done, as it won't + * override any value. + * Note that if `-defaultlib=` or `-debuglib=` was used, + * we don't override that either. + */ +private void setDefaultLibrary() +{ + if (global.params.defaultlibname is null) { - fatal(); + static if (TARGET_WINDOS) + { + if (global.params.is64bit) + global.params.defaultlibname = "phobos64"; + else if (global.params.mscoff) + global.params.defaultlibname = "phobos32mscoff"; + else + global.params.defaultlibname = "phobos"; + } + else static if (TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS) + { + global.params.defaultlibname = "libphobos2.a"; + } + else static if (TARGET_OSX) + { + global.params.defaultlibname = "phobos2"; + } + else + { + static assert(0, "fix this"); + } } - if (files.dim == 0) + if (global.params.debuglibname is null) + global.params.debuglibname = global.params.defaultlibname; +} + + +/** + * Add default `version` identifier for ddmd, and set the + * target platform in `global`. + * + * Needs to be run after all arguments parsing (command line, DFLAGS environment + * variable and config file) in order to add final flags (such as `X86_64` or + * the `CRuntime` used). + */ +private void addDefaultVersionIdentifiers() +{ + VersionCondition.addPredefinedGlobalIdent("DigitalMars"); + static if (TARGET_WINDOS) { - usage(); - return EXIT_FAILURE; + VersionCondition.addPredefinedGlobalIdent("Windows"); + global.params.isWindows = true; } - static if (TARGET_OSX) + else static if (TARGET_LINUX) { - global.params.pic = 1; + VersionCondition.addPredefinedGlobalIdent("Posix"); + VersionCondition.addPredefinedGlobalIdent("linux"); + VersionCondition.addPredefinedGlobalIdent("ELFv1"); + global.params.isLinux = true; } - static if (TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS) + else static if (TARGET_OSX) { - if (global.params.lib && global.params.dll) - error(Loc(), "cannot mix -lib and -shared"); + VersionCondition.addPredefinedGlobalIdent("Posix"); + VersionCondition.addPredefinedGlobalIdent("OSX"); + global.params.isOSX = true; + // For legacy compatibility + VersionCondition.addPredefinedGlobalIdent("darwin"); } - static if (TARGET_WINDOS) + else static if (TARGET_FREEBSD) { - if (!global.params.mscrtlib) - global.params.mscrtlib = "libcmt"; + VersionCondition.addPredefinedGlobalIdent("Posix"); + VersionCondition.addPredefinedGlobalIdent("FreeBSD"); + VersionCondition.addPredefinedGlobalIdent("ELFv1"); + global.params.isFreeBSD = true; } - if (global.params.useArrayBounds == BOUNDSCHECKdefault) + else static if (TARGET_OPENBSD) { - // Set the real default value - global.params.useArrayBounds = global.params.release ? BOUNDSCHECKsafeonly : BOUNDSCHECKon; + VersionCondition.addPredefinedGlobalIdent("Posix"); + VersionCondition.addPredefinedGlobalIdent("OpenBSD"); + VersionCondition.addPredefinedGlobalIdent("ELFv1"); + global.params.isOpenBSD = true; } - if (global.params.release) + else static if (TARGET_SOLARIS) { - global.params.useInvariants = false; - global.params.useIn = false; - global.params.useOut = false; - global.params.useAssert = false; - global.params.useSwitchError = false; + VersionCondition.addPredefinedGlobalIdent("Posix"); + VersionCondition.addPredefinedGlobalIdent("Solaris"); + VersionCondition.addPredefinedGlobalIdent("ELFv1"); + global.params.isSolaris = true; } - if (global.params.useUnitTests) - global.params.useAssert = true; - if (!global.params.obj || global.params.lib) - global.params.link = false; - if (global.params.link) + else { - global.params.exefile = global.params.objname; - global.params.oneobj = true; - if (global.params.objname) - { - /* Use this to name the one object file with the same - * name as the exe file. - */ - global.params.objname = cast(char*)FileName.forceExt(global.params.objname, global.obj_ext); - /* If output directory is given, use that path rather than - * the exe file path. - */ - if (global.params.objdir) - { - const(char)* name = FileName.name(global.params.objname); - global.params.objname = cast(char*)FileName.combine(global.params.objdir, name); - } - } + static assert(0, "fix this"); } - else if (global.params.run) + VersionCondition.addPredefinedGlobalIdent("LittleEndian"); + VersionCondition.addPredefinedGlobalIdent("D_Version2"); + VersionCondition.addPredefinedGlobalIdent("all"); + + if (global.params.cpu >= CPU.sse2) { - error(Loc(), "flags conflict with -run"); - fatal(); + VersionCondition.addPredefinedGlobalIdent("D_SIMD"); + if (global.params.cpu >= CPU.avx) + VersionCondition.addPredefinedGlobalIdent("D_AVX"); + if (global.params.cpu >= CPU.avx2) + VersionCondition.addPredefinedGlobalIdent("D_AVX2"); } - else if (global.params.lib) + + if (global.params.is64bit) { - global.params.libname = global.params.objname; - global.params.objname = null; - // Haven't investigated handling these options with multiobj - if (!global.params.cov && !global.params.trace) - global.params.multiobj = true; + VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86_64"); + VersionCondition.addPredefinedGlobalIdent("X86_64"); + static if (TARGET_WINDOS) + { + VersionCondition.addPredefinedGlobalIdent("Win64"); + } } else { - if (global.params.objname && files.dim > 1) + VersionCondition.addPredefinedGlobalIdent("D_InlineAsm"); //legacy + VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86"); + VersionCondition.addPredefinedGlobalIdent("X86"); + static if (TARGET_WINDOS) { - global.params.oneobj = true; - //error("multiple source files, but only one .obj name"); - //fatal(); + VersionCondition.addPredefinedGlobalIdent("Win32"); } } + static if (TARGET_WINDOS) + { + if (global.params.mscoff) + VersionCondition.addPredefinedGlobalIdent("CRuntime_Microsoft"); + else + VersionCondition.addPredefinedGlobalIdent("CRuntime_DigitalMars"); + } + else static if (TARGET_LINUX) + { + VersionCondition.addPredefinedGlobalIdent("CRuntime_Glibc"); + } - // Predefined version identifiers - addDefaultVersionIdentifiers(); - - setDefaultLibrary(); + if (global.params.isLP64) + VersionCondition.addPredefinedGlobalIdent("D_LP64"); + if (global.params.doDocComments) + VersionCondition.addPredefinedGlobalIdent("D_Ddoc"); + if (global.params.cov) + VersionCondition.addPredefinedGlobalIdent("D_Coverage"); + if (global.params.pic) + VersionCondition.addPredefinedGlobalIdent("D_PIC"); + if (global.params.useUnitTests) + VersionCondition.addPredefinedGlobalIdent("unittest"); + if (global.params.useAssert) + VersionCondition.addPredefinedGlobalIdent("assert"); + if (global.params.useArrayBounds == BOUNDSCHECKoff) + VersionCondition.addPredefinedGlobalIdent("D_NoBoundsChecks"); + VersionCondition.addPredefinedGlobalIdent("D_HardFloat"); - // Initialization - Type._init(); - Id.initialize(); - Module._init(); - Target._init(); - Expression._init(); - Objc._init(); - builtin_init(); + printPredefinedVersions(); +} - if (global.params.verbose) +private void printPredefinedVersions() +{ + if (global.params.verbose && global.params.versionids) { - fprintf(global.stdmsg, "binary %s\n", global.params.argv0); - fprintf(global.stdmsg, "version %s\n", global._version); - fprintf(global.stdmsg, "config %s\n", global.inifilename ? global.inifilename : "(none)"); + fprintf(global.stdmsg, "predefs "); + foreach (const s; *global.params.versionids) + fprintf(global.stdmsg, " %s", s); + fprintf(global.stdmsg, "\n"); } - //printf("%d source files\n",files.dim); +} - // Build import search path - static Strings* buildPath(Strings* imppath) +/**************************************** + * Determine the instruction set to be used. + * Params: + * cpu = value set by command line switch + * Returns: + * value to generate code for + */ + +private CPU setTargetCPU(CPU cpu) +{ + // Determine base line for target + CPU baseline = CPU.x87; + if (global.params.is64bit) + baseline = CPU.sse2; + else { - Strings* result = null; - if (imppath) + static if (TARGET_OSX) { - foreach (const path; *imppath) - { - Strings* a = FileName.splitPath(path); - if (a) - { - if (!result) - result = new Strings(); - result.append(a); - } - } + baseline = CPU.sse2; } - return result; } - global.path = buildPath(global.params.imppath); - global.filePath = buildPath(global.params.fileImppath); + if (baseline < CPU.sse2) + return baseline; // can't support other instruction sets - if (global.params.addMain) + switch (cpu) { - files.push(cast(char*)global.main_d); // a dummy name, we never actually look up this file + case CPU.baseline: + cpu = baseline; + break; + + case CPU.native: + { + import core.cpuid; + cpu = baseline; + if (core.cpuid.avx2) + cpu = CPU.avx2; + else if (core.cpuid.avx) + cpu = CPU.avx; + break; + } + + default: + break; } - // Create Modules - Modules modules; - modules.reserve(files.dim); - bool firstmodule = true; - for (size_t i = 0; i < files.dim; i++) + return cpu; +} + + +/**************************************************** + * Parse command line arguments. + * + * Prints message and exits if there are errors. + * + * Params: + * arguments = command line arguments + * argc = argument count + * params = set to result of parsing `arguments` + * files = set to files pulled from `arguments` + */ + +private void parseCommandLine(const ref Strings arguments, const size_t argc, ref Param params, ref Strings files) +{ + version (none) { - const(char)* name; - version (Windows) + for (size_t i = 0; i < arguments.dim; i++) { - files[i] = toWinPath(files[i]); + printf("arguments[%d] = '%s'\n", i, arguments[i]); } - const(char)* p = files[i]; - p = FileName.name(p); // strip path - const(char)* ext = FileName.ext(p); - char* newname; - if (ext) + } + for (size_t i = 1; i < arguments.dim; i++) + { + const(char)* p = arguments[i]; + if (*p == '-') { - /* Deduce what to do with a file based on its extension - */ - if (FileName.equals(ext, global.obj_ext)) + if (strcmp(p + 1, "allinst") == 0) + params.allInst = true; + else if (strcmp(p + 1, "de") == 0) + params.useDeprecated = 0; + else if (strcmp(p + 1, "d") == 0) + params.useDeprecated = 1; + else if (strcmp(p + 1, "dw") == 0) + params.useDeprecated = 2; + else if (strcmp(p + 1, "c") == 0) + params.link = false; + else if (memcmp(p + 1, cast(char*)"color", 5) == 0) { - global.params.objfiles.push(files[i]); - libmodules.push(files[i]); - continue; + params.color = true; + // Parse: + // -color + // -color=on|off + if (p[6] == '=') + { + if (strcmp(p + 7, "off") == 0) + params.color = false; + else if (strcmp(p + 7, "on") != 0) + goto Lerror; + } + else if (p[6]) + goto Lerror; } - if (FileName.equals(ext, global.lib_ext)) + else if (memcmp(p + 1, cast(char*)"conf=", 5) == 0) { - global.params.libfiles.push(files[i]); - libmodules.push(files[i]); - continue; + // ignore, already handled above } - static if (TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS) + else if (memcmp(p + 1, cast(char*)"cov", 3) == 0) { - if (FileName.equals(ext, global.dll_ext)) + params.cov = true; + // Parse: + // -cov + // -cov=nnn + if (p[4] == '=') { - global.params.dllfiles.push(files[i]); - libmodules.push(files[i]); - continue; + if (isdigit(cast(char)p[5])) + { + long percent; + errno = 0; + percent = strtol(p + 5, &p, 10); + if (*p || errno || percent > 100) + goto Lerror; + params.covPercent = cast(ubyte)percent; + } + else + goto Lerror; } + else if (p[4]) + goto Lerror; } - if (strcmp(ext, global.ddoc_ext) == 0) + else if (strcmp(p + 1, "shared") == 0) + params.dll = true; + else if (strcmp(p + 1, "dylib") == 0) { - global.params.ddocfiles.push(files[i]); - continue; + static if (TARGET_OSX) + { + Loc loc; + deprecation(loc, "use -shared instead of -dylib"); + params.dll = true; + } + else + { + goto Lerror; + } } - if (FileName.equals(ext, global.json_ext)) + else if (strcmp(p + 1, "fPIC") == 0) { - global.params.doJsonGeneration = true; - global.params.jsonfilename = files[i]; - continue; + static if (TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS) + { + params.pic = 1; + } + else + { + goto Lerror; + } } - if (FileName.equals(ext, global.map_ext)) + else if (strcmp(p + 1, "map") == 0) + params.map = true; + else if (strcmp(p + 1, "multiobj") == 0) + params.multiobj = true; + else if (strcmp(p + 1, "g") == 0) + params.symdebug = 1; + else if (strcmp(p + 1, "gc") == 0) { - global.params.mapfile = files[i]; - continue; + Loc loc; + deprecation(loc, "use -g instead of -gc"); + params.symdebug = 2; } - static if (TARGET_WINDOS) + else if (strcmp(p + 1, "gf") == 0) { - if (FileName.equals(ext, "res")) + if (!params.symdebug) + params.symdebug = 1; + params.symdebugref = true; + } + else if (strcmp(p + 1, "gs") == 0) + params.alwaysframe = true; + else if (strcmp(p + 1, "gx") == 0) + params.stackstomp = true; + else if (strcmp(p + 1, "gt") == 0) + { + error(Loc(), "use -profile instead of -gt"); + params.trace = true; + } + else if (strcmp(p + 1, "m32") == 0) + { + params.is64bit = false; + params.mscoff = false; + } + else if (strcmp(p + 1, "m64") == 0) + { + params.is64bit = true; + static if (TARGET_WINDOS) { - global.params.resfile = files[i]; - continue; + params.mscoff = true; } - if (FileName.equals(ext, "def")) + } + else if (strcmp(p + 1, "m32mscoff") == 0) + { + static if (TARGET_WINDOS) { - global.params.deffile = files[i]; - continue; + params.is64bit = 0; + params.mscoff = true; } - if (FileName.equals(ext, "exe")) + else { - assert(0); // should have already been handled + error(Loc(), "-m32mscoff can only be used on windows"); } } - /* Examine extension to see if it is a valid - * D source file extension - */ - if (FileName.equals(ext, global.mars_ext) || FileName.equals(ext, global.hdr_ext) || FileName.equals(ext, "dd")) + else if (strncmp(p + 1, "mscrtlib=", 9) == 0) { - ext--; // skip onto '.' - assert(*ext == '.'); - newname = cast(char*)mem.xmalloc((ext - p) + 1); - memcpy(newname, p, ext - p); - newname[ext - p] = 0; // strip extension - name = newname; - if (name[0] == 0 || strcmp(name, "..") == 0 || strcmp(name, ".") == 0) + static if (TARGET_WINDOS) { - Linvalid: - error(Loc(), "invalid file name '%s'", files[i]); - fatal(); + params.mscrtlib = p + 10; + } + else + { + error(Loc(), "-mscrtlib"); } } - else + else if (memcmp(p + 1, cast(char*)"profile", 7) == 0) { - error(Loc(), "unrecognized file extension %s", ext); - fatal(); + // Parse: + // -profile + // -profile=gc + if (p[8] == '=') + { + if (strcmp(p + 9, "gc") == 0) + params.tracegc = true; + else + goto Lerror; + } + else if (p[8]) + goto Lerror; + else + params.trace = true; } - } - else - { - name = p; - if (!*name) - goto Linvalid; - } - /* At this point, name is the D source file name stripped of - * its path and extension. - */ - auto id = Identifier.idPool(name, strlen(name)); - auto m = new Module(files[i], id, global.params.doDocComments, global.params.doHdrGeneration); - modules.push(m); - if (firstmodule) - { - global.params.objfiles.push(m.objfile.name.str); - firstmodule = false; - } - } - // Read files - /* Start by "reading" the dummy main.d file - */ - if (global.params.addMain) - { - for (size_t i = 0; 1; i++) - { - assert(i != modules.dim); - Module m = modules[i]; - if (strcmp(m.srcfile.name.str, global.main_d) == 0) + else if (strcmp(p + 1, "v") == 0) + params.verbose = true; + else if (strcmp(p + 1, "vcg-ast") == 0) + params.vcg_ast = true; + else if (strcmp(p + 1, "vtls") == 0) + params.vtls = true; + else if (strcmp(p + 1, "vcolumns") == 0) + params.showColumns = true; + else if (strcmp(p + 1, "vgc") == 0) + params.vgc = true; + else if (memcmp(p + 1, cast(char*)"verrors", 7) == 0) { - string buf = "int main(){return 0;}"; - m.srcfile.setbuffer(cast(void*)buf.ptr, buf.length); - m.srcfile._ref = 1; - break; - } - } - } - enum ASYNCREAD = false; - static if (ASYNCREAD) - { - // Multi threaded - AsyncRead* aw = AsyncRead.create(modules.dim); - for (size_t i = 0; i < modules.dim; i++) - { - Module m = modules[i]; - aw.addFile(m.srcfile); - } - aw.start(); - } - else - { - // Single threaded - for (size_t i = 0; i < modules.dim; i++) - { - Module m = modules[i]; - m.read(Loc()); - } - } - // Parse files - bool anydocfiles = false; - size_t filecount = modules.dim; - for (size_t filei = 0, modi = 0; filei < filecount; filei++, modi++) - { - Module m = modules[modi]; - if (global.params.verbose) - fprintf(global.stdmsg, "parse %s\n", m.toChars()); - if (!Module.rootModule) - Module.rootModule = m; - m.importedFrom = m; // m.isRoot() == true - if (!global.params.oneobj || modi == 0 || m.isDocFile) - m.deleteObjFile(); - static if (ASYNCREAD) - { - if (aw.read(filei)) + if (p[8] == '=' && isdigit(cast(char)p[9])) + { + long num; + errno = 0; + num = strtol(p + 9, &p, 10); + if (*p || errno || num > INT_MAX) + goto Lerror; + global.errorLimit = cast(uint)num; + } + else if (memcmp(p + 9, cast(char*)"spec", 4) == 0) + { + params.showGaggedErrors = true; + } + else + goto Lerror; + } + else if (memcmp(p + 1, "mcpu".ptr, 4) == 0) { - error(Loc(), "cannot read file %s", m.srcfile.name.toChars()); - fatal(); + // Parse: + // -mcpu=identifier + if (p[5] == '=') + { + if (strcmp(p + 6, "?") == 0) + { + printf(" +CPU architectures supported by -mcpu=id: + =? list information on all architecture choices + =baseline use default architecture as determined by target + =avx use AVX 1 instructions + =avx2 use AVX 2 instructions + =native use CPU architecture that this compiler is running on +"); + exit(EXIT_SUCCESS); + } + else if (Identifier.isValidIdentifier(p + 6)) + { + const ident = p + 6; + switch (ident[0 .. strlen(ident)]) + { + case "baseline": + params.cpu = CPU.baseline; + break; + case "avx": + params.cpu = CPU.avx; + break; + case "avx2": + params.cpu = CPU.avx2; + break; + case "native": + params.cpu = CPU.native; + break; + default: + goto Lerror; + } + } + else + goto Lerror; + } + else + goto Lerror; } - } - m.parse(); - if (m.isDocFile) - { - anydocfiles = true; - gendocfile(m); - // Remove m from list of modules - modules.remove(modi); - modi--; - // Remove m's object file from list of object files - for (size_t j = 0; j < global.params.objfiles.dim; j++) + else if (memcmp(p + 1, cast(char*)"transition", 10) == 0) { - if (m.objfile.name.str == (*global.params.objfiles)[j]) + // Parse: + // -transition=number + if (p[11] == '=') { - global.params.objfiles.remove(j); + if (strcmp(p + 12, "?") == 0) + { + printf(" +Language changes listed by -transition=id: + =all list information on all language changes + =checkimports give deprecation messages about 10378 anomalies + =complex,14488 list all usages of complex or imaginary types + =field,3449 list all non-mutable fields which occupy an object instance + =import,10378 revert to single phase name lookup + =tls list all variables going into thread local storage +"); + exit(EXIT_SUCCESS); + } + if (isdigit(cast(char)p[12])) + { + long num; + errno = 0; + num = strtol(p + 12, &p, 10); + if (*p || errno || num > INT_MAX) + goto Lerror; + // Bugzilla issue number + switch (num) + { + case 3449: + params.vfield = true; + break; + case 10378: + params.bug10378 = true; + break; + case 14488: + params.vcomplex = true; + break; + default: + goto Lerror; + } + } + else if (Identifier.isValidIdentifier(p + 12)) + { + const ident = p + 12; + switch (ident[0 .. strlen(ident)]) + { + case "all": + params.vtls = true; + params.vfield = true; + params.vcomplex = true; + break; + case "checkimports": + params.check10378 = true; + break; + case "complex": + params.vcomplex = true; + break; + case "field": + params.vfield = true; + break; + case "import": + params.bug10378 = true; + break; + case "tls": + params.vtls = true; + break; + default: + goto Lerror; + } + } + else + goto Lerror; + } + else + goto Lerror; + } + else if (strcmp(p + 1, "w") == 0) + params.warnings = 1; + else if (strcmp(p + 1, "wi") == 0) + params.warnings = 2; + else if (strcmp(p + 1, "O") == 0) + params.optimize = true; + else if (p[1] == 'o') + { + const(char)* path; + switch (p[2]) + { + case '-': + params.obj = false; + break; + case 'd': + if (!p[3]) + goto Lnoarg; + path = p + 3 + (p[3] == '='); + version (Windows) + { + path = toWinPath(path); + } + params.objdir = path; + break; + case 'f': + if (!p[3]) + goto Lnoarg; + path = p + 3 + (p[3] == '='); + version (Windows) + { + path = toWinPath(path); + } + params.objname = path; + break; + case 'p': + if (p[3]) + goto Lerror; + params.preservePaths = true; + break; + case 0: + error(Loc(), "-o no longer supported, use -of or -od"); break; + default: + goto Lerror; } } - if (global.params.objfiles.dim == 0) - global.params.link = false; - } - } - static if (ASYNCREAD) - { - AsyncRead.dispose(aw); - } - if (anydocfiles && modules.dim && (global.params.oneobj || global.params.objname)) - { - error(Loc(), "conflicting Ddoc and obj generation options"); - fatal(); - } - if (global.errors) - fatal(); - - if (global.params.doHdrGeneration) - { - /* Generate 'header' import files. - * Since 'header' import files must be independent of command - * line switches and what else is imported, they are generated - * before any semantic analysis. - */ - for (size_t i = 0; i < modules.dim; i++) - { - Module m = modules[i]; - if (global.params.verbose) - fprintf(global.stdmsg, "import %s\n", m.toChars()); - genhdrfile(m); - } - } - if (global.errors) - fatal(); - - // load all unconditional imports for better symbol resolving - for (size_t i = 0; i < modules.dim; i++) - { - Module m = modules[i]; - if (global.params.verbose) - fprintf(global.stdmsg, "importall %s\n", m.toChars()); - m.importAll(null); - } - if (global.errors) - fatal(); - - backend_init(); - - // Do semantic analysis - for (size_t i = 0; i < modules.dim; i++) - { - Module m = modules[i]; - if (global.params.verbose) - fprintf(global.stdmsg, "semantic %s\n", m.toChars()); - m.semantic(null); - } - //if (global.errors) - // fatal(); - Module.dprogress = 1; - Module.runDeferredSemantic(); - if (Module.deferred.dim) - { - for (size_t i = 0; i < Module.deferred.dim; i++) - { - Dsymbol sd = Module.deferred[i]; - sd.error("unable to resolve forward reference in definition"); - } - //fatal(); - } - - // Do pass 2 semantic analysis - for (size_t i = 0; i < modules.dim; i++) - { - Module m = modules[i]; - if (global.params.verbose) - fprintf(global.stdmsg, "semantic2 %s\n", m.toChars()); - m.semantic2(null); - } - Module.runDeferredSemantic2(); - if (global.errors) - fatal(); - - // Do pass 3 semantic analysis - for (size_t i = 0; i < modules.dim; i++) - { - Module m = modules[i]; - if (global.params.verbose) - fprintf(global.stdmsg, "semantic3 %s\n", m.toChars()); - m.semantic3(null); - } - Module.runDeferredSemantic3(); - if (global.errors) - fatal(); - - // Scan for functions to inline - if (global.params.useInline) - { - for (size_t i = 0; i < modules.dim; i++) - { - Module m = modules[i]; - if (global.params.verbose) - fprintf(global.stdmsg, "inline scan %s\n", m.toChars()); - inlineScanModule(m); - } - } - // Do not attempt to generate output files if errors or warnings occurred - if (global.errors || global.warnings) - fatal(); - - // inlineScan incrementally run semantic3 of each expanded functions. - // So deps file generation should be moved after the inlinig stage. - if (global.params.moduleDeps) - { - foreach (i; 1 .. modules[0].aimports.dim) - semantic3OnDependencies(modules[0].aimports[i]); - - OutBuffer* ob = global.params.moduleDeps; - if (global.params.moduleDepsFile) - { - auto deps = File(global.params.moduleDepsFile); - deps.setbuffer(cast(void*)ob.data, ob.offset); - writeFile(Loc(), &deps); - } - else - printf("%.*s", cast(int)ob.offset, ob.data); - } - - printCtfePerformanceStats(); - - Library library = null; - if (global.params.lib) - { - library = Library.factory(); - library.setFilename(global.params.objdir, global.params.libname); - // Add input object and input library files to output library - for (size_t i = 0; i < libmodules.dim; i++) - { - const(char)* p = libmodules[i]; - library.addObject(p, null); - } - } - // Generate output files - if (global.params.doJsonGeneration) - { - OutBuffer buf; - json_generate(&buf, &modules); - // Write buf to file - const(char)* name = global.params.jsonfilename; - if (name && name[0] == '-' && name[1] == 0) - { - // Write to stdout; assume it succeeds - size_t n = fwrite(buf.data, 1, buf.offset, stdout); - assert(n == buf.offset); // keep gcc happy about return values - } - else - { - /* The filename generation code here should be harmonized with Module::setOutfile() - */ - const(char)* jsonfilename; - if (name && *name) + else if (p[1] == 'D') { - jsonfilename = FileName.defaultExt(name, global.json_ext); + params.doDocComments = true; + switch (p[2]) + { + case 'd': + if (!p[3]) + goto Lnoarg; + params.docdir = p + 3 + (p[3] == '='); + break; + case 'f': + if (!p[3]) + goto Lnoarg; + params.docname = p + 3 + (p[3] == '='); + break; + case 0: + break; + default: + goto Lerror; + } } - else + else if (p[1] == 'H') { - // Generate json file name from first obj name - const(char)* n = (*global.params.objfiles)[0]; - n = FileName.name(n); - //if (!FileName::absolute(name)) - // name = FileName::combine(dir, name); - jsonfilename = FileName.forceExt(n, global.json_ext); + params.doHdrGeneration = true; + switch (p[2]) + { + case 'd': + if (!p[3]) + goto Lnoarg; + params.hdrdir = p + 3 + (p[3] == '='); + break; + case 'f': + if (!p[3]) + goto Lnoarg; + params.hdrname = p + 3 + (p[3] == '='); + break; + case 0: + break; + default: + goto Lerror; + } } - ensurePathToNameExists(Loc(), jsonfilename); - auto jsonfile = new File(jsonfilename); - jsonfile.setbuffer(buf.data, buf.offset); - jsonfile._ref = 1; - writeFile(Loc(), jsonfile); - } - } - if (!global.errors && global.params.doDocComments) - { - for (size_t i = 0; i < modules.dim; i++) - { - Module m = modules[i]; - gendocfile(m); - } - } - if (global.params.vcg_ast) - { - import ddmd.hdrgen; - foreach (mod; modules) - { - auto buf = OutBuffer(); - buf.doindent = 1; - scope HdrGenState hgs; - hgs.fullDump = 1; - scope PrettyPrintVisitor ppv = new PrettyPrintVisitor(&buf, &hgs); - mod.accept(ppv); - - // write the output to $(filename).cg - auto modFilename = mod.srcfile.toChars(); - auto modFilenameLength = strlen(modFilename); - auto cgFilename = cast(char*)allocmemory(modFilenameLength + 4); - memcpy(cgFilename, modFilename, modFilenameLength); - cgFilename[modFilenameLength .. modFilenameLength + 4] = ".cg\0"; - auto cgFile = File(cgFilename); - cgFile.setbuffer(buf.data, buf.offset); - cgFile._ref = 1; - cgFile.write(); - } - } - if (!global.params.obj) - { - } - else if (global.params.oneobj) - { - if (modules.dim) - obj_start(cast(char*)modules[0].srcfile.toChars()); - for (size_t i = 0; i < modules.dim; i++) - { - Module m = modules[i]; - if (global.params.verbose) - fprintf(global.stdmsg, "code %s\n", m.toChars()); - genObjFile(m, false); - if (entrypoint && m == rootHasMain) - genObjFile(entrypoint, false); - } - if (!global.errors && modules.dim) - { - obj_end(library, modules[0].objfile); - } - } - else - { - for (size_t i = 0; i < modules.dim; i++) - { - Module m = modules[i]; - if (global.params.verbose) - fprintf(global.stdmsg, "code %s\n", m.toChars()); - obj_start(cast(char*)m.srcfile.toChars()); - genObjFile(m, global.params.multiobj); - if (entrypoint && m == rootHasMain) - genObjFile(entrypoint, global.params.multiobj); - obj_end(library, m.objfile); - obj_write_deferred(library); - if (global.errors && !global.params.lib) - m.deleteObjFile(); - } - } - if (global.params.lib && !global.errors) - library.write(); - backend_term(); - if (global.errors) - fatal(); - int status = EXIT_SUCCESS; - if (!global.params.objfiles.dim) - { - if (global.params.link) - error(Loc(), "no object files to link"); - } - else - { - if (global.params.link) - status = runLINK(); - if (global.params.run) - { - if (!status) + else if (p[1] == 'X') { - status = runProgram(); - /* Delete .obj files and .exe file - */ - for (size_t i = 0; i < modules.dim; i++) + params.doJsonGeneration = true; + switch (p[2]) { - modules[i].deleteObjFile(); - if (global.params.oneobj) - break; + case 'f': + if (!p[3]) + goto Lnoarg; + params.jsonfilename = p + 3 + (p[3] == '='); + break; + case 0: + break; + default: + goto Lerror; + } + } + else if (strcmp(p + 1, "ignore") == 0) + params.ignoreUnsupportedPragmas = true; + else if (strcmp(p + 1, "property") == 0) + params.enforcePropertySyntax = true; + else if (strcmp(p + 1, "inline") == 0) + { + params.useInline = true; + params.hdrStripPlainFunctions = false; + } + else if (strcmp(p + 1, "dip25") == 0) + params.useDIP25 = true; + else if (strcmp(p + 1, "dip1000") == 0) + { + params.useDIP25 = true; + params.vsafe = true; + } + else if (strcmp(p + 1, "lib") == 0) + params.lib = true; + else if (strcmp(p + 1, "nofloat") == 0) + params.nofloat = true; + else if (strcmp(p + 1, "quiet") == 0) + { + // Ignore + } + else if (strcmp(p + 1, "release") == 0) + params.release = true; + else if (strcmp(p + 1, "betterC") == 0) + params.betterC = true; + else if (strcmp(p + 1, "noboundscheck") == 0) + { + params.useArrayBounds = BOUNDSCHECKoff; + } + else if (memcmp(p + 1, cast(char*)"boundscheck", 11) == 0) + { + // Parse: + // -boundscheck=[on|safeonly|off] + if (p[12] == '=') + { + if (strcmp(p + 13, "on") == 0) + { + params.useArrayBounds = BOUNDSCHECKon; + } + else if (strcmp(p + 13, "safeonly") == 0) + { + params.useArrayBounds = BOUNDSCHECKsafeonly; + } + else if (strcmp(p + 13, "off") == 0) + { + params.useArrayBounds = BOUNDSCHECKoff; + } + else + goto Lerror; + } + else + goto Lerror; + } + else if (strcmp(p + 1, "unittest") == 0) + params.useUnitTests = true; + else if (p[1] == 'I') + { + if (!params.imppath) + params.imppath = new Strings(); + params.imppath.push(p + 2 + (p[2] == '=')); + } + else if (p[1] == 'm' && p[2] == 'v' && p[3] == '=') + { + if (p[4] && strchr(p + 5, '=')) + { + if (!params.modFileAliasStrings) + params.modFileAliasStrings = new Strings(); + params.modFileAliasStrings.push(p + 4); + } + else + goto Lerror; + } + else if (p[1] == 'J') + { + if (!params.fileImppath) + params.fileImppath = new Strings(); + params.fileImppath.push(p + 2 + (p[2] == '=')); + } + else if (memcmp(p + 1, cast(char*)"debug", 5) == 0 && p[6] != 'l') + { + // Parse: + // -debug + // -debug=number + // -debug=identifier + if (p[6] == '=') + { + if (isdigit(cast(char)p[7])) + { + long level; + errno = 0; + level = strtol(p + 7, &p, 10); + if (*p || errno || level > INT_MAX) + goto Lerror; + DebugCondition.setGlobalLevel(cast(int)level); + } + else if (Identifier.isValidIdentifier(p + 7)) + DebugCondition.addGlobalIdent(p[7 .. p.strlen]); + else + goto Lerror; + } + else if (p[6]) + goto Lerror; + else + DebugCondition.setGlobalLevel(1); + } + else if (memcmp(p + 1, cast(char*)"version", 7) == 0) + { + // Parse: + // -version=number + // -version=identifier + if (p[8] == '=') + { + if (isdigit(cast(char)p[9])) + { + long level; + errno = 0; + level = strtol(p + 9, &p, 10); + if (*p || errno || level > INT_MAX) + goto Lerror; + VersionCondition.setGlobalLevel(cast(int)level); + } + else if (Identifier.isValidIdentifier(p + 9)) + VersionCondition.addGlobalIdent(p[9 .. p.strlen]); + else + goto Lerror; } - remove(global.params.exefile); + else + goto Lerror; } - } - } - return status; -} - - -/** - * Entry point which forwards to `tryMain`. - * - * Returns: - * Return code of the application - */ -int main() -{ - import core.memory; - import core.runtime; - - version (GC) - { - } - else - { - GC.disable(); - } - version(D_Coverage) - { - // for now we need to manually set the source path - string dirName(string path, char separator) - { - for (size_t i = path.length - 1; i > 0; i--) + else if (strcmp(p + 1, "-b") == 0) + params.debugb = true; + else if (strcmp(p + 1, "-c") == 0) + params.debugc = true; + else if (strcmp(p + 1, "-f") == 0) + params.debugf = true; + else if (strcmp(p + 1, "-help") == 0 || strcmp(p + 1, "h") == 0) { - if (path[i] == separator) - return path[0..i]; + usage(); + exit(EXIT_SUCCESS); } - return path; - } - version (Windows) - enum sourcePath = dirName(dirName(__FILE_FULL_PATH__, `\`), `\`); - else - enum sourcePath = dirName(dirName(__FILE_FULL_PATH__, '/'), '/'); - - dmd_coverSourcePath(sourcePath); - dmd_coverDestPath(sourcePath); - dmd_coverSetMerge(true); - } - - auto args = Runtime.cArgs(); - return tryMain(args.argc, cast(const(char)**)args.argv); -} - - -/** - * Parses an environment variable containing command-line flags - * and append them to `args`. - * - * This function is used to read the content of DFLAGS. - * Flags are separated based on spaces and tabs. - * - * Params: - * envvalue = The content of an environment variable - * args = Array to append the flags to, if any. - */ -private void getenv_setargv(const(char)* envvalue, Strings* args) -{ - if (!envvalue) - return; - char* p; - int instring; - int slash; - char c; - char* env = mem.xstrdup(envvalue); // create our own writable copy - //printf("env = '%s'\n", env); - while (1) - { - switch (*env) - { - case ' ': - case '\t': - env++; - break; - case 0: - return; - default: - args.push(env); // append - p = env; - slash = 0; - instring = 0; - c = 0; - while (1) + else if (strcmp(p + 1, "-r") == 0) + params.debugr = true; + else if (strcmp(p + 1, "-version") == 0) { - c = *env++; - switch (c) + logo(); + exit(EXIT_SUCCESS); + } + else if (strcmp(p + 1, "-x") == 0) + params.debugx = true; + else if (strcmp(p + 1, "-y") == 0) + params.debugy = true; + else if (p[1] == 'L') + { + params.linkswitches.push(p + 2 + (p[2] == '=')); + } + else if (memcmp(p + 1, cast(char*)"defaultlib=", 11) == 0) + { + params.defaultlibname = p + 1 + 11; + } + else if (memcmp(p + 1, cast(char*)"debuglib=", 9) == 0) + { + params.debuglibname = p + 1 + 9; + } + else if (memcmp(p + 1, cast(char*)"deps", 4) == 0) + { + if (params.moduleDeps) { - case '"': - p -= (slash >> 1); - if (slash & 1) - { - p--; - goto Laddc; - } - instring ^= 1; - slash = 0; - continue; - case ' ': - case '\t': - if (instring) - goto Laddc; - *p = 0; - //if (wildcard) - // wildcardexpand(); // not implemented + error(Loc(), "-deps[=file] can only be provided once!"); break; - case '\\': - slash++; - *p++ = c; - continue; - case 0: - *p = 0; - //if (wildcard) - // wildcardexpand(); // not implemented - return; - default: - Laddc: - slash = 0; - *p++ = c; - continue; } - break; + if (p[5] == '=') + { + params.moduleDepsFile = p + 1 + 5; + if (!params.moduleDepsFile[0]) + goto Lnoarg; + } + else if (p[5] != '\0') + { + // Else output to stdout. + goto Lerror; + } + params.moduleDeps = new OutBuffer(); } - } - } -} - -/** - * Parse command line arguments for -m32 or -m64 - * to detect the desired architecture. - * - * Params: - * args = Command line arguments - * arch = Default value to use for architecture. - * Should be "32" or "64" - * - * Returns: - * "32", "64" or "32mscoff" if the "-m32", "-m64", "-m32mscoff" flags were passed, - * respectively. If they weren't, return `arch`. - */ -private const(char)* parse_arch_arg(Strings* args, const(char)* arch) -{ - for (size_t i = 0; i < args.dim; ++i) - { - const(char)* p = (*args)[i]; - if (p[0] == '-') - { - if (strcmp(p + 1, "m32") == 0 || strcmp(p + 1, "m32mscoff") == 0 || strcmp(p + 1, "m64") == 0) - arch = p + 2; - else if (strcmp(p + 1, "run") == 0) - break; - } - } - return arch; -} - - -/** - * Parse command line arguments for -conf=path. - * - * Params: - * args = Command line arguments - * - * Returns: - * Path to the config file to use - */ -private const(char)* parse_conf_arg(Strings* args) -{ - const(char)* conf = null; - for (size_t i = 0; i < args.dim; ++i) - { - const(char)* p = (*args)[i]; - if (p[0] == '-') - { - if (strncmp(p + 1, "conf=", 5) == 0) - conf = p + 6; - else if (strcmp(p + 1, "run") == 0) - break; - } - } - return conf; -} - - -/** - * Set the default and debug libraries to link against, if not already set - * - * Must be called after argument parsing is done, as it won't - * override any value. - * Note that if `-defaultlib=` or `-debuglib=` was used, - * we don't override that either. - */ -private void setDefaultLibrary() -{ - if (global.params.defaultlibname is null) - { - static if (TARGET_WINDOS) - { - if (global.params.is64bit) - global.params.defaultlibname = "phobos64"; - else if (global.params.mscoff) - global.params.defaultlibname = "phobos32mscoff"; + else if (strcmp(p + 1, "main") == 0) + { + params.addMain = true; + } + else if (memcmp(p + 1, cast(char*)"man", 3) == 0) + { + version (Windows) + { + browse("http://dlang.org/dmd-windows.html"); + } + version (linux) + { + browse("http://dlang.org/dmd-linux.html"); + } + version (OSX) + { + browse("http://dlang.org/dmd-osx.html"); + } + version (FreeBSD) + { + browse("http://dlang.org/dmd-freebsd.html"); + } + version (OpenBSD) + { + browse("http://dlang.org/dmd-openbsd.html"); + } + exit(EXIT_SUCCESS); + } + else if (strcmp(p + 1, "run") == 0) + { + params.run = true; + size_t length = argc - i - 1; + if (length) + { + const(char)* ext = FileName.ext(arguments[i + 1]); + if (ext && FileName.equals(ext, "d") == 0 && FileName.equals(ext, "di") == 0) + { + error(Loc(), "-run must be followed by a source file, not '%s'", arguments[i + 1]); + break; + } + files.push(arguments[i + 1]); + params.runargs.setDim(length - 1); + for (size_t j = 0; j < length - 1; ++j) + { + params.runargs[j] = arguments[i + 2 + j]; + } + i += length; + } + else + { + params.run = false; + goto Lnoarg; + } + } + else if (p[1] == '\0') + files.push("__stdin.d"); else - global.params.defaultlibname = "phobos"; - } - else static if (TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS) - { - global.params.defaultlibname = "libphobos2.a"; - } - else static if (TARGET_OSX) - { - global.params.defaultlibname = "phobos2"; + { + Lerror: + error(Loc(), "unrecognized switch '%s'", arguments[i]); + continue; + Lnoarg: + error(Loc(), "argument expected for switch '%s'", arguments[i]); + continue; + } } else { - static assert(0, "fix this"); - } - } - if (global.params.debuglibname is null) - global.params.debuglibname = global.params.defaultlibname; -} - - -/** - * Add default `version` identifier for ddmd, and set the - * target platform in `global`. - * - * Needs to be run after all arguments parsing (command line, DFLAGS environment - * variable and config file) in order to add final flags (such as `X86_64` or - * the `CRuntime` used). - */ -private void addDefaultVersionIdentifiers() -{ - VersionCondition.addPredefinedGlobalIdent("DigitalMars"); - static if (TARGET_WINDOS) - { - VersionCondition.addPredefinedGlobalIdent("Windows"); - global.params.isWindows = true; - } - else static if (TARGET_LINUX) - { - VersionCondition.addPredefinedGlobalIdent("Posix"); - VersionCondition.addPredefinedGlobalIdent("linux"); - VersionCondition.addPredefinedGlobalIdent("ELFv1"); - global.params.isLinux = true; - } - else static if (TARGET_OSX) - { - VersionCondition.addPredefinedGlobalIdent("Posix"); - VersionCondition.addPredefinedGlobalIdent("OSX"); - global.params.isOSX = true; - // For legacy compatibility - VersionCondition.addPredefinedGlobalIdent("darwin"); - } - else static if (TARGET_FREEBSD) - { - VersionCondition.addPredefinedGlobalIdent("Posix"); - VersionCondition.addPredefinedGlobalIdent("FreeBSD"); - VersionCondition.addPredefinedGlobalIdent("ELFv1"); - global.params.isFreeBSD = true; - } - else static if (TARGET_OPENBSD) - { - VersionCondition.addPredefinedGlobalIdent("Posix"); - VersionCondition.addPredefinedGlobalIdent("OpenBSD"); - VersionCondition.addPredefinedGlobalIdent("ELFv1"); - global.params.isOpenBSD = true; - } - else static if (TARGET_SOLARIS) - { - VersionCondition.addPredefinedGlobalIdent("Posix"); - VersionCondition.addPredefinedGlobalIdent("Solaris"); - VersionCondition.addPredefinedGlobalIdent("ELFv1"); - global.params.isSolaris = true; - } - else - { - static assert(0, "fix this"); - } - VersionCondition.addPredefinedGlobalIdent("LittleEndian"); - VersionCondition.addPredefinedGlobalIdent("D_Version2"); - VersionCondition.addPredefinedGlobalIdent("all"); - - if (global.params.cpu >= CPU.sse2) - { - VersionCondition.addPredefinedGlobalIdent("D_SIMD"); - if (global.params.cpu >= CPU.avx) - VersionCondition.addPredefinedGlobalIdent("D_AVX"); - if (global.params.cpu >= CPU.avx2) - VersionCondition.addPredefinedGlobalIdent("D_AVX2"); - } - - if (global.params.is64bit) - { - VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86_64"); - VersionCondition.addPredefinedGlobalIdent("X86_64"); - static if (TARGET_WINDOS) - { - VersionCondition.addPredefinedGlobalIdent("Win64"); - } - } - else - { - VersionCondition.addPredefinedGlobalIdent("D_InlineAsm"); //legacy - VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86"); - VersionCondition.addPredefinedGlobalIdent("X86"); - static if (TARGET_WINDOS) - { - VersionCondition.addPredefinedGlobalIdent("Win32"); + static if (TARGET_WINDOS) + { + const(char)* ext = FileName.ext(p); + if (ext && FileName.compare(ext, "exe") == 0) + { + params.objname = p; + continue; + } + if (strcmp(p, `/?`) == 0) + { + usage(); + exit(EXIT_SUCCESS); + } + } + files.push(p); } } - static if (TARGET_WINDOS) - { - if (global.params.mscoff) - VersionCondition.addPredefinedGlobalIdent("CRuntime_Microsoft"); - else - VersionCondition.addPredefinedGlobalIdent("CRuntime_DigitalMars"); - } - else static if (TARGET_LINUX) - { - VersionCondition.addPredefinedGlobalIdent("CRuntime_Glibc"); - } - - if (global.params.isLP64) - VersionCondition.addPredefinedGlobalIdent("D_LP64"); - if (global.params.doDocComments) - VersionCondition.addPredefinedGlobalIdent("D_Ddoc"); - if (global.params.cov) - VersionCondition.addPredefinedGlobalIdent("D_Coverage"); - if (global.params.pic) - VersionCondition.addPredefinedGlobalIdent("D_PIC"); - if (global.params.useUnitTests) - VersionCondition.addPredefinedGlobalIdent("unittest"); - if (global.params.useAssert) - VersionCondition.addPredefinedGlobalIdent("assert"); - if (global.params.useArrayBounds == BOUNDSCHECKoff) - VersionCondition.addPredefinedGlobalIdent("D_NoBoundsChecks"); - VersionCondition.addPredefinedGlobalIdent("D_HardFloat"); - - printPredefinedVersions(); -} - -private void printPredefinedVersions() -{ - if (global.params.verbose && global.params.versionids) - { - fprintf(global.stdmsg, "predefs "); - foreach (const s; *global.params.versionids) - fprintf(global.stdmsg, " %s", s); - fprintf(global.stdmsg, "\n"); - } } - -/**************************************** - * Determine the instruction set to be used. - * Params: - * cpu = value set by command line switch - * Returns: - * value to generate code for - */ - -private CPU setTargetCPU(CPU cpu) -{ - // Determine base line for target - CPU baseline = CPU.x87; - if (global.params.is64bit) - baseline = CPU.sse2; - else - { - static if (TARGET_OSX) - { - baseline = CPU.sse2; - } - } - - if (baseline < CPU.sse2) - return baseline; // can't support other instruction sets - - switch (cpu) - { - case CPU.baseline: - cpu = baseline; - break; - - case CPU.native: - { - import core.cpuid; - cpu = baseline; - if (core.cpuid.avx2) - cpu = CPU.avx2; - else if (core.cpuid.avx) - cpu = CPU.avx; - break; - } - - default: - break; - } - return cpu; -}