diff --git a/rdmd.d b/rdmd.d index 68df66d8e6..2900815e7f 100755 --- a/rdmd.d +++ b/rdmd.d @@ -250,23 +250,36 @@ int main(string[] args) } // Fetch dependencies - const myDeps = getDependencies(root, workDir, objDir, compilerFlags); - - // --makedepend mode. Just print dependencies and exit. - if (makeDepend) + const depsFilename = buildPath(workDir, "rdmd.deps"); + const initialDeps = tryGetDependencies(root, objDir, compilerFlags, depsFilename); + bool handleDeps(const(string[string]) currentDeps, bool buildAlreadyRan) { - writeDeps(exe, root, myDeps, stdout); - return 0; - } + if(currentDeps is null && !buildAlreadyRan) + return false; // continue program + + auto resolvedDeps = (currentDeps is null) ? cast(const(string[string]))readDepsFile(objDir, depsFilename) : currentDeps; + assert(resolvedDeps !is null, "code bug: dependencies were not properly generated from the build"); + + // --makedepend mode. Just print dependencies and exit. + if (makeDepend) + { + writeDeps(exe, root, resolvedDeps, stdout); + return true; // exit program + } - // --makedepfile mode. Print dependencies to a file and continue. - // This is similar to GCC's -MF option, very useful to update the - // dependencies file and compile in one go: - // -include .deps.mak - // prog: - // rdmd --makedepfile=.deps.mak --build-only prog.d - if (makeDepFile !is null) - writeDeps(exe, root, myDeps, File(makeDepFile, "w")); + // --makedepfile mode. Print dependencies to a file and continue. + // This is similar to GCC's -MF option, very useful to update the + // dependencies file and compile in one go: + // -include .deps.mak + // prog: + // rdmd --makedepfile=.deps.mak --build-only prog.d + if (makeDepFile !is null) + writeDeps(exe, root, resolvedDeps, File(makeDepFile, "w")); + + return false; // continueprogram + } + if(handleDeps(initialDeps, false)) + return 0; // Compute executable name, check for freshness, rebuild /* @@ -287,7 +300,7 @@ int main(string[] args) { // user-specified exe name buildWitness = buildPath(workDir, ".built"); - if (!exe.newerThan(buildWitness)) + if (initialDeps !is null && !exe.newerThan(buildWitness)) { // Both exe and buildWitness exist, and exe is older than // buildWitness. This is the only situation in which we @@ -300,15 +313,18 @@ int main(string[] args) { exe = buildPath(workDir, exeBasename) ~ outExt; buildWitness = exe; - yap("stat ", buildWitness); - lastBuildTime = buildWitness.timeLastModified(SysTime.min); + if(initialDeps !is null) + { + yap("stat ", buildWitness); + lastBuildTime = buildWitness.timeLastModified(SysTime.min); + } } // Have at it - if (chain(root.only, myDeps.byKey).anyNewerThan(lastBuildTime)) + if (initialDeps is null || chain(root.only, initialDeps.byKey).anyNewerThan(lastBuildTime)) { immutable result = rebuild(root, exe, workDir, objDir, - myDeps, compilerFlags, addStubMain); + compilerFlags, depsFilename, addStubMain); if (result) return result; @@ -321,6 +337,9 @@ int main(string[] args) } } + if(handleDeps(initialDeps, true)) + return 0; + if (buildOnly) { // Pretty much done! @@ -459,8 +478,8 @@ private void unlockWorkPath() // object file. private int rebuild(string root, string fullExe, - string workDir, string objDir, in string[string] myDeps, - string[] compilerFlags, bool addStubMain) + string workDir, string objDir, + string[] compilerFlags, string depsFilename, bool addStubMain) { version (Windows) fullExe = fullExe.defaultExtension(".exe"); @@ -495,11 +514,9 @@ private int rebuild(string root, string fullExe, ~ [ "-of" ~ fullExeTemp ] ~ [ "-od" ~ objDir ] ~ [ "-I" ~ dirName(root) ] + ~ [ "-i" ] + ~ [ "-v" ] ~ [ root ]; - foreach (k, objectFile; myDeps) { - if(objectFile !is null) - todo ~= [ k ]; - } // Need to add void main(){}? if (addStubMain) { @@ -526,7 +543,7 @@ private int rebuild(string root, string fullExe, todo = [ "@" ~ rspName ]; } - immutable result = run([ compiler ] ~ todo); + immutable result = run([ compiler ] ~ todo, depsFilename); if (result) { // build failed @@ -588,106 +605,104 @@ private int exec(string[] args) return run(args, null, true); } -// Given module rootModule, returns a mapping of all dependees .d -// source filenames to their corresponding .o files sitting in -// directory workDir. The mapping is obtained by running dmd -v against -// rootModule. - -private string[string] getDependencies(string rootModule, string workDir, - string objDir, string[] compilerFlags) +string[string] readDepsFile(string objDir, string depsFilename) { - immutable depsFilename = buildPath(workDir, "rdmd.deps"); - - string[string] readDepsFile() + string d2obj(string dfile) { - string d2obj(string dfile) + return buildPath(objDir, dfile.baseName.chomp(".d") ~ objExt); + } + string findLib(string libName) + { + // This can't be 100% precise without knowing exactly where the linker + // will look for libraries (which requires, but is not limited to, + // parsing the linker's command line (as specified in dmd.conf/sc.ini). + // Go for best-effort instead. + string[] dirs = ["."]; + foreach (envVar; ["LIB", "LIBRARY_PATH", "LD_LIBRARY_PATH"]) + dirs ~= environment.get(envVar, "").split(pathSeparator); + version (Windows) + string[] names = [libName ~ ".lib"]; + else { - return buildPath(objDir, dfile.baseName.chomp(".d") ~ objExt); + string[] names = ["lib" ~ libName ~ ".a", "lib" ~ libName ~ ".so"]; + dirs ~= ["/lib", "/usr/lib"]; } - string findLib(string libName) - { - // This can't be 100% precise without knowing exactly where the linker - // will look for libraries (which requires, but is not limited to, - // parsing the linker's command line (as specified in dmd.conf/sc.ini). - // Go for best-effort instead. - string[] dirs = ["."]; - foreach (envVar; ["LIB", "LIBRARY_PATH", "LD_LIBRARY_PATH"]) - dirs ~= environment.get(envVar, "").split(pathSeparator); - version (Windows) - string[] names = [libName ~ ".lib"]; - else + foreach (dir; dirs) + foreach (name; names) { - string[] names = ["lib" ~ libName ~ ".a", "lib" ~ libName ~ ".so"]; - dirs ~= ["/lib", "/usr/lib"]; + auto path = buildPath(dir, name); + if (path.exists) + return absolutePath(path); } - foreach (dir; dirs) - foreach (name; names) - { - auto path = buildPath(dir, name); - if (path.exists) - return absolutePath(path); - } - return null; - } - yap("read ", depsFilename); - auto depsReader = File(depsFilename); - scope(exit) collectException(depsReader.close()); // don't care for errors - - // Fetch all dependencies and append them to myDeps - auto pattern = ctRegex!(r"^(import|file|binary|config|library)\s+([^\(]+)\(?([^\)]*)\)?\s*$"); - string[string] result; - foreach (string line; lines(depsReader)) + return null; + } + yap("read ", depsFilename); + auto depsReader = File(depsFilename); + scope(exit) collectException(depsReader.close()); // don't care for errors + + // Fetch all dependencies and append them to myDeps + auto pattern = ctRegex!(r"^(import|file|binary|config|library)\s+([^\(]+)\(?([^\)]*)\)?\s*$"); + string[string] result; + foreach (string line; lines(depsReader)) + { + auto regexMatch = match(line, pattern); + if (regexMatch.empty) continue; + auto captures = regexMatch.captures; + switch(captures[1]) { - auto regexMatch = match(line, pattern); - if (regexMatch.empty) continue; - auto captures = regexMatch.captures; - switch(captures[1]) + case "import": + immutable moduleName = captures[2].strip(), moduleSrc = captures[3].strip(); + if (inALibrary(moduleName, moduleSrc)) continue; + immutable moduleObj = d2obj(moduleSrc); + result[moduleSrc] = moduleObj; + break; + + case "file": + result[captures[3].strip()] = null; + break; + + case "binary": + result[which(captures[2].strip())] = null; + break; + + case "config": + auto confFile = captures[2].strip; + // The config file is special: if missing, that's fine too. So + // add it as a dependency only if it actually exists. + yap("stat ", confFile); + if (confFile.exists) { - case "import": - immutable moduleName = captures[2].strip(), moduleSrc = captures[3].strip(); - if (inALibrary(moduleName, moduleSrc)) continue; - immutable moduleObj = d2obj(moduleSrc); - result[moduleSrc] = moduleObj; - break; - - case "file": - result[captures[3].strip()] = null; - break; - - case "binary": - result[which(captures[2].strip())] = null; - break; - - case "config": - auto confFile = captures[2].strip; - // The config file is special: if missing, that's fine too. So - // add it as a dependency only if it actually exists. - yap("stat ", confFile); - if (confFile.exists) - { - result[confFile] = null; - } - break; - - case "library": - immutable libName = captures[2].strip(); - immutable libPath = findLib(libName); - if (libPath.ptr) - { - yap("library ", libName, " ", libPath); - result[libPath] = null; - } - break; + result[confFile] = null; + } + break; - default: assert(0); + case "library": + immutable libName = captures[2].strip(); + immutable libPath = findLib(libName); + if (libPath.ptr) + { + yap("library ", libName, " ", libPath); + result[libPath] = null; } + break; + + default: assert(0); } - // All dependencies specified through --extra-file - foreach (immutable moduleSrc; extraFiles) - result[moduleSrc] = d2obj(moduleSrc); - return result; } + // All dependencies specified through --extra-file + foreach (immutable moduleSrc; extraFiles) + result[moduleSrc] = d2obj(moduleSrc); + return result; +} + +// Given module rootModule, returns a mapping of all dependees .d +// source filenames to their corresponding .o files sitting in +// directory workDir. The mapping is obtained by reading the 'rdmd.deps' file +// that would have been generated on a previous build. +private string[string] tryGetDependencies(string rootModule, + string objDir, string[] compilerFlags, string depsFilename) +{ // Check if the old dependency file is fine if (!force) { @@ -696,45 +711,18 @@ private string[string] getDependencies(string rootModule, string workDir, if (depsT > SysTime.min) { // See if the deps file is still in good shape - auto deps = readDepsFile(); + auto deps = readDepsFile(objDir, depsFilename); auto allDeps = chain(rootModule.only, deps.byKey); bool mustRebuildDeps = allDeps.anyNewerThan(depsT); if (!mustRebuildDeps) { - // Cool, we're in good shape + // Dependencies are up to date, return them return deps; } - // Fall through to rebuilding the deps file } } - immutable rootDir = dirName(rootModule); - - // Filter out -lib. With -o-, it will create an empty library file. - compilerFlags = compilerFlags.filter!(flag => flag != "-lib").array(); - - // Collect dependencies - auto depsGetter = - // "cd " ~ shellQuote(rootDir) ~ " && " - [ compiler ] ~ compilerFlags ~ - ["-v", "-o-", rootModule, "-I" ~ rootDir]; - - scope(failure) - { - // Delete the deps file on failure, we don't want to be fooled - // by it next time we try - collectException(std.file.remove(depsFilename)); - } - - immutable depsExitCode = run(depsGetter, depsFilename); - if (depsExitCode) - { - stderr.writefln("Failed: %s", depsGetter); - collectException(std.file.remove(depsFilename)); - exit(depsExitCode); - } - - return dryRun ? null : readDepsFile(); + return null; // dependendies are stale or have not been evaluated } // Is any file newer than the given file?