-
-
Notifications
You must be signed in to change notification settings - Fork 145
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use "-i" to prevent rdmd from having to invoke compiler twice. #271
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,7 +41,8 @@ else | |
private bool chatty, buildOnly, dryRun, force, preserveOutputPaths; | ||
private string exe, userTempDir; | ||
immutable string[] defaultExclusions = ["std", "etc", "core"]; | ||
private string[] exclusions = defaultExclusions; // packages that are to be excluded | ||
private string[] exclusions; // packages that are to be excluded | ||
private string[] includes; | ||
private string[] extraFiles = []; | ||
|
||
version (DigitalMars) | ||
|
@@ -139,7 +140,7 @@ int main(string[] args) | |
"eval", &eval, | ||
"loop", &loop, | ||
"exclude", &exclusions, | ||
"include", (string opt, string p) { exclusions = exclusions.filter!(ex => ex != p).array(); }, | ||
"include", &includes, | ||
"extra-file", &extraFiles, | ||
"force", &force, | ||
"help", { writeln(helpString); bailout = true; }, | ||
|
@@ -250,23 +251,8 @@ int main(string[] args) | |
} | ||
|
||
// Fetch dependencies | ||
const myDeps = getDependencies(root, workDir, objDir, compilerFlags); | ||
|
||
// --makedepend mode. Just print dependencies and exit. | ||
if (makeDepend) | ||
{ | ||
writeDeps(exe, root, myDeps, stdout); | ||
return 0; | ||
} | ||
|
||
// --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")); | ||
const depsFilename = buildPath(workDir, "rdmd.deps"); | ||
const initialDeps = tryGetDependencies(root, objDir, compilerFlags, depsFilename); | ||
|
||
// Compute executable name, check for freshness, rebuild | ||
/* | ||
|
@@ -287,7 +273,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,18 +286,22 @@ 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; | ||
|
||
// TODO: only touch build witness if build actually created output | ||
// Touch the build witness to track the build time | ||
if (buildWitness != exe) | ||
{ | ||
|
@@ -321,6 +311,28 @@ int main(string[] args) | |
} | ||
} | ||
|
||
if(makeDepend || makeDepFile !is null) | ||
{ | ||
auto resolvedDeps = (initialDeps is null) ? readDepsFile(objDir, depsFilename) : initialDeps; | ||
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, resolvedDeps, File(makeDepFile, "w")); | ||
} | ||
|
||
if (buildOnly) | ||
{ | ||
// Pretty much done! | ||
|
@@ -459,8 +471,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,10 +507,20 @@ 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 ]; | ||
foreach(exclusion; exclusions) | ||
{ | ||
todo ~= ("-i=-" ~ exclusion); | ||
} | ||
foreach(inclusion; includes) | ||
{ | ||
todo ~= ("-i=" ~ inclusion); | ||
} | ||
foreach(extraFile; extraFiles) | ||
{ | ||
todo ~= extraFile; | ||
} | ||
// Need to add void main(){}? | ||
if (addStubMain) | ||
|
@@ -526,7 +548,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 +610,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) | ||
const(string[string]) readDepsFile(string objDir, string depsFilename) | ||
{ | ||
immutable depsFilename = buildPath(workDir, "rdmd.deps"); | ||
|
||
string[string] readDepsFile() | ||
string d2obj(string dfile) | ||
{ | ||
return buildPath(objDir, dfile.baseName.chomp(".d") ~ objExt); | ||
} | ||
string findLib(string libName) | ||
{ | ||
string d2obj(string dfile) | ||
// 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"]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DYLD_LIBRARY_PATH on OSX would be nice to abstract out OS specific things eg: foreach (envVar; ["LIB", "LIBRARY_PATH", os_LD_LIBRARY_PATH]) {...}
module rdmd.util;
string os_LD_LIBRARY_PATH(){ // find a better name
version(OSX) return "DYLD_LIBRARY_PATH";
else version(linux) return "LD_LIBRARY_PATH";
else assert(0);
} |
||
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"]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. .dylib on OSX string[] names = ["lib" ~ libName ~ ".a", "lib" ~ libName ~ os_so_ext]; |
||
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 const(string[string]) tryGetDependencies(string rootModule, | ||
string objDir, string[] compilerFlags, string depsFilename) | ||
{ | ||
// Check if the old dependency file is fine | ||
if (!force) | ||
{ | ||
|
@@ -696,45 +716,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? | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume this was just for development / testing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually it's necessary. Before this change the compiler was called using
-v
for the "first call" to the compiler, now both calls are combined into a single call. rdmd still uses the output from-v
to read the dependencies to know when a rebuild is necessary.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah fair enough. Thanks for the explanation!