From 64df3401503fbf32547c476f6aff2252345ee350 Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Fri, 13 Sep 2024 15:46:28 +0200 Subject: [PATCH] Fix bugzilla 24748 - DMD can't output object files with fully qualified name, making -od overwirte each other file --- changelog/dmd.oq.dd | 15 +++++++++++++++ compiler/src/dmd/cli.d | 6 ++++++ compiler/src/dmd/dmodule.d | 20 ++++++++++++++++++-- compiler/src/dmd/frontend.h | 9 ++++++--- compiler/src/dmd/globals.d | 2 ++ compiler/src/dmd/globals.h | 4 ++++ compiler/src/dmd/main.d | 11 +++++++++++ compiler/src/dmd/mars.d | 5 +++++ compiler/test/runnable/test_switches.sh | 7 ++++++- 9 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 changelog/dmd.oq.dd diff --git a/changelog/dmd.oq.dd b/changelog/dmd.oq.dd new file mode 100644 index 0000000000..cc22d6137b --- /dev/null +++ b/changelog/dmd.oq.dd @@ -0,0 +1,15 @@ +Add `-oq` switch to DMD + +The switch gives fully qualified names to object files, preventing name conflicts when using the `-od` switch +while compiling multiple modules with the same name, but inside different packages. +The switch already existed in LDC, but is now in dmd as well. + +Example: + +$(CONSOLE +dmd -c -oq -od=. app.d util/app.d misc/app.d +) + +This will output `app.obj`, `util.app.obj`, and `misc.app.obj`, instead of just `app.obj`. + +`-oq` also applies to other outputs, such as DDoc (`-D -Dd=.`) and .di header generation (`-H -Hd=.`). diff --git a/compiler/src/dmd/cli.d b/compiler/src/dmd/cli.d index 810debb41b..2274ab4c1a 100644 --- a/compiler/src/dmd/cli.d +++ b/compiler/src/dmd/cli.d @@ -690,6 +690,12 @@ dmd -cov -unittest myprog.d off when generating an object, interface, or Ddoc file name. $(SWLINK -op) will leave it on.`, ), + Option("oq", + "Write object files with fully qualified file names", + `When compiling pkg/app.d, the resulting object file name will be pkg_app.obj + instead of app.o. This helps to prevent name conflicts when compiling multiple + packages in the same directory with the $(SWLINK -od) flag.`, + ), Option("os=", "sets target operating system to ", `Set the target operating system as other than the host. diff --git a/compiler/src/dmd/dmodule.d b/compiler/src/dmd/dmodule.d index 583b4679f0..eda20a2141 100644 --- a/compiler/src/dmd/dmodule.d +++ b/compiler/src/dmd/dmodule.d @@ -349,8 +349,8 @@ extern (C++) final class Module : Package const(char)[] arg; // original argument name ModuleDeclaration* md; // if !=null, the contents of the ModuleDeclaration declaration const FileName srcfile; // input source file - const FileName objfile; // output .obj file - const FileName hdrfile; // 'header' file + FileName objfile; // output .obj file + FileName hdrfile; // 'header' file FileName docfile; // output documentation file const(ubyte)[] src; /// Raw content of the file uint errors; // if any errors in file @@ -579,6 +579,22 @@ extern (C++) final class Module : Package argdoc = arg; else argdoc = FileName.name(arg); + + if (global.params.fullyQualifiedObjectFiles) + { + const fqn = md ? md.toString() : toString(); + argdoc = FileName.replaceName(argdoc, fqn); + + // add ext, otherwise forceExt will make nested.module into nested. + const bufferLength = argdoc.length + 1 + ext.length + /* null terminator */ 1; + char[] s = new char[bufferLength]; + s[0 .. argdoc.length] = argdoc[]; + s[argdoc.length] = '.'; + s[$-1-ext.length .. $-1] = ext[]; + s[$-1] = 0; + argdoc = s; + } + // If argdoc doesn't have an absolute path, make it relative to dir if (!FileName.absolute(argdoc)) { diff --git a/compiler/src/dmd/frontend.h b/compiler/src/dmd/frontend.h index 8ed0767671..e6d457a180 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -7020,8 +7020,8 @@ public: _d_dynamicArray< const char > arg; ModuleDeclaration* md; const FileName srcfile; - const FileName objfile; - const FileName hdrfile; + FileName objfile; + FileName hdrfile; FileName docfile; _d_dynamicArray< const uint8_t > src; uint32_t errors; @@ -8112,6 +8112,7 @@ struct Param final _d_dynamicArray< const char > resfile; _d_dynamicArray< const char > exefile; _d_dynamicArray< const char > mapfile; + bool fullyQualifiedObjectFiles; bool timeTrace; uint32_t timeTraceGranularityUs; const char* timeTraceFile; @@ -8191,12 +8192,13 @@ struct Param final resfile(), exefile(), mapfile(), + fullyQualifiedObjectFiles(), timeTrace(false), timeTraceGranularityUs(500u), timeTraceFile() { } - Param(bool obj, bool multiobj = false, bool trace = false, bool tracegc = false, bool vcg_ast = false, DiagnosticReporting useDeprecated = (DiagnosticReporting)1u, bool useUnitTests = false, bool useInline = false, bool release = false, bool preservePaths = false, DiagnosticReporting warnings = (DiagnosticReporting)2u, bool cov = false, uint8_t covPercent = 0u, bool ctfe_cov = false, bool ignoreUnsupportedPragmas = true, bool useModuleInfo = true, bool useTypeInfo = true, bool useExceptions = true, bool useGC = true, bool betterC = false, bool addMain = false, bool allInst = false, bool bitfields = false, CppStdRevision cplusplus = (CppStdRevision)201103u, Help help = Help(), Verbose v = Verbose(), FeatureState useDIP25 = (FeatureState)2u, FeatureState useDIP1000 = (FeatureState)0u, bool ehnogc = false, bool useDIP1021 = false, FeatureState fieldwise = (FeatureState)0u, bool fixAliasThis = false, FeatureState rvalueRefParam = (FeatureState)0u, FeatureState noSharedAccess = (FeatureState)0u, bool previewIn = false, bool inclusiveInContracts = false, bool shortenedMethods = true, bool fixImmutableConv = false, bool fix16997 = true, FeatureState dtorFields = (FeatureState)0u, FeatureState systemVariables = (FeatureState)0u, CHECKENABLE useInvariants = (CHECKENABLE)0u, CHECKENABLE useIn = (CHECKENABLE)0u, CHECKENABLE useOut = (CHECKENABLE)0u, CHECKENABLE useArrayBounds = (CHECKENABLE)0u, CHECKENABLE useAssert = (CHECKENABLE)0u, CHECKENABLE useSwitchError = (CHECKENABLE)0u, CHECKENABLE boundscheck = (CHECKENABLE)0u, CHECKACTION checkAction = (CHECKACTION)0u, CLIIdentifierTable dIdentifierTable = (CLIIdentifierTable)0u, CLIIdentifierTable cIdentifierTable = (CLIIdentifierTable)0u, _d_dynamicArray< const char > argv0 = {}, Array modFileAliasStrings = Array(), Array imppath = Array(), Array fileImppath = Array(), _d_dynamicArray< const char > objdir = {}, _d_dynamicArray< const char > objname = {}, _d_dynamicArray< const char > libname = {}, Output ddoc = Output(), Output dihdr = Output(), Output cxxhdr = Output(), Output json = Output(), JsonFieldFlags jsonFieldFlags = (JsonFieldFlags)0u, Output makeDeps = Output(), Output mixinOut = Output(), Output moduleDeps = Output(), uint32_t debuglevel = 0u, uint32_t versionlevel = 0u, bool run = false, Array runargs = Array(), Array cppswitches = Array(), const char* cpp = nullptr, Array objfiles = Array(), Array linkswitches = Array(), Array linkswitchIsForCC = Array(), Array libfiles = Array(), Array dllfiles = Array(), _d_dynamicArray< const char > deffile = {}, _d_dynamicArray< const char > resfile = {}, _d_dynamicArray< const char > exefile = {}, _d_dynamicArray< const char > mapfile = {}, bool timeTrace = false, uint32_t timeTraceGranularityUs = 500u, const char* timeTraceFile = nullptr) : + Param(bool obj, bool multiobj = false, bool trace = false, bool tracegc = false, bool vcg_ast = false, DiagnosticReporting useDeprecated = (DiagnosticReporting)1u, bool useUnitTests = false, bool useInline = false, bool release = false, bool preservePaths = false, DiagnosticReporting warnings = (DiagnosticReporting)2u, bool cov = false, uint8_t covPercent = 0u, bool ctfe_cov = false, bool ignoreUnsupportedPragmas = true, bool useModuleInfo = true, bool useTypeInfo = true, bool useExceptions = true, bool useGC = true, bool betterC = false, bool addMain = false, bool allInst = false, bool bitfields = false, CppStdRevision cplusplus = (CppStdRevision)201103u, Help help = Help(), Verbose v = Verbose(), FeatureState useDIP25 = (FeatureState)2u, FeatureState useDIP1000 = (FeatureState)0u, bool ehnogc = false, bool useDIP1021 = false, FeatureState fieldwise = (FeatureState)0u, bool fixAliasThis = false, FeatureState rvalueRefParam = (FeatureState)0u, FeatureState noSharedAccess = (FeatureState)0u, bool previewIn = false, bool inclusiveInContracts = false, bool shortenedMethods = true, bool fixImmutableConv = false, bool fix16997 = true, FeatureState dtorFields = (FeatureState)0u, FeatureState systemVariables = (FeatureState)0u, CHECKENABLE useInvariants = (CHECKENABLE)0u, CHECKENABLE useIn = (CHECKENABLE)0u, CHECKENABLE useOut = (CHECKENABLE)0u, CHECKENABLE useArrayBounds = (CHECKENABLE)0u, CHECKENABLE useAssert = (CHECKENABLE)0u, CHECKENABLE useSwitchError = (CHECKENABLE)0u, CHECKENABLE boundscheck = (CHECKENABLE)0u, CHECKACTION checkAction = (CHECKACTION)0u, CLIIdentifierTable dIdentifierTable = (CLIIdentifierTable)0u, CLIIdentifierTable cIdentifierTable = (CLIIdentifierTable)0u, _d_dynamicArray< const char > argv0 = {}, Array modFileAliasStrings = Array(), Array imppath = Array(), Array fileImppath = Array(), _d_dynamicArray< const char > objdir = {}, _d_dynamicArray< const char > objname = {}, _d_dynamicArray< const char > libname = {}, Output ddoc = Output(), Output dihdr = Output(), Output cxxhdr = Output(), Output json = Output(), JsonFieldFlags jsonFieldFlags = (JsonFieldFlags)0u, Output makeDeps = Output(), Output mixinOut = Output(), Output moduleDeps = Output(), uint32_t debuglevel = 0u, uint32_t versionlevel = 0u, bool run = false, Array runargs = Array(), Array cppswitches = Array(), const char* cpp = nullptr, Array objfiles = Array(), Array linkswitches = Array(), Array linkswitchIsForCC = Array(), Array libfiles = Array(), Array dllfiles = Array(), _d_dynamicArray< const char > deffile = {}, _d_dynamicArray< const char > resfile = {}, _d_dynamicArray< const char > exefile = {}, _d_dynamicArray< const char > mapfile = {}, bool fullyQualifiedObjectFiles = false, bool timeTrace = false, uint32_t timeTraceGranularityUs = 500u, const char* timeTraceFile = nullptr) : obj(obj), multiobj(multiobj), trace(trace), @@ -8278,6 +8280,7 @@ struct Param final resfile(resfile), exefile(exefile), mapfile(mapfile), + fullyQualifiedObjectFiles(fullyQualifiedObjectFiles), timeTrace(timeTrace), timeTraceGranularityUs(timeTraceGranularityUs), timeTraceFile(timeTraceFile) diff --git a/compiler/src/dmd/globals.d b/compiler/src/dmd/globals.d index 2bdc1856e1..ea88445516 100644 --- a/compiler/src/dmd/globals.d +++ b/compiler/src/dmd/globals.d @@ -248,6 +248,8 @@ extern (C++) struct Param const(char)[] exefile; const(char)[] mapfile; + bool fullyQualifiedObjectFiles; // prepend module names to object files to prevent name conflicts with -od + // Time tracing bool timeTrace = false; /// Whether profiling of compile time is enabled uint timeTraceGranularityUs = 500; /// In microseconds, minimum event size to report diff --git a/compiler/src/dmd/globals.h b/compiler/src/dmd/globals.h index bd28d7be7b..45efab0947 100644 --- a/compiler/src/dmd/globals.h +++ b/compiler/src/dmd/globals.h @@ -250,6 +250,10 @@ struct Param DString resfile; DString exefile; DString mapfile; + bool fullyQualifiedObjectFiles; + bool timeTrace; + uint32_t timeTraceGranularityUs; + const char* timeTraceFile; }; struct structalign_t diff --git a/compiler/src/dmd/main.d b/compiler/src/dmd/main.d index c13b54ffc8..cd3765bee2 100644 --- a/compiler/src/dmd/main.d +++ b/compiler/src/dmd/main.d @@ -453,6 +453,17 @@ private int tryMain(size_t argc, const(char)** argv, ref Param params) // m.deleteObjFile(); m.parse(); + + // Finalize output filenames. Update if `-oq` was specified (only feasible after parsing). + if (params.fullyQualifiedObjectFiles && m.md) + { + m.objfile = m.setOutfilename(params.objname, params.objdir, m.arg, FileName.ext(m.objfile.toString())); + if (m.docfile) + m.setDocfile(); + if (m.hdrfile) + m.hdrfile = m.setOutfilename(params.dihdr.name, params.dihdr.dir, m.arg, hdr_ext); + } + if (m.filetype == FileType.dhdr) { // Remove m's object file from list of object files diff --git a/compiler/src/dmd/mars.d b/compiler/src/dmd/mars.d index 18ddd9bd7d..edbe16471b 100644 --- a/compiler/src/dmd/mars.d +++ b/compiler/src/dmd/mars.d @@ -1286,6 +1286,11 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param goto Lerror; params.preservePaths = true; break; + case 'q': + if (p[3]) + goto Lerror; + params.fullyQualifiedObjectFiles = true; + break; case 0: error("-o no longer supported, use -of or -od"); break; diff --git a/compiler/test/runnable/test_switches.sh b/compiler/test/runnable/test_switches.sh index 465dcea3ab..f0fce9e2fd 100755 --- a/compiler/test/runnable/test_switches.sh +++ b/compiler/test/runnable/test_switches.sh @@ -14,6 +14,7 @@ # -Xf= src_file=${OUTPUT_BASE}/src.d +src_file_in_pkg=${OUTPUT_BASE}/pkg/src.d clean() { @@ -26,6 +27,8 @@ prepare() mkdir -p ${OUTPUT_BASE} echo "module mymod;" > ${OUTPUT_BASE}/mymod.d echo "module src; import mymod;" > ${src_file} + mkdir ${OUTPUT_BASE}/pkg + echo "module pkg.src;" > ${src_file_in_pkg} } die() @@ -59,7 +62,9 @@ $DMD -o- -od=${OUTPUT_BASE} -D -Df=${OUTPUT_BASE}/src.html -Hf=${OUTPUT_BASE}/sr checkFiles; prepare; -$DMD -o- -od=${OUTPUT_BASE} -D -Dd=${OUTPUT_BASE} -Hd=${OUTPUT_BASE} -I=${OUTPUT_BASE} -L=-v -Xf=${OUTPUT_BASE}/json.json ${src_file} +$DMD -o- -oq -od=${OUTPUT_BASE} -D -Dd=${OUTPUT_BASE} -Hd=${OUTPUT_BASE} -I=${OUTPUT_BASE} -L=-v -Xf=${OUTPUT_BASE}/json.json ${src_file} ${src_file_in_pkg} checkFiles; +checkFile ${OUTPUT_BASE}/pkg.src.di +checkFile ${OUTPUT_BASE}/pkg.src.html clean;