-
-
Notifications
You must be signed in to change notification settings - Fork 606
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
315 additions
and
49 deletions.
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
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
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 |
---|---|---|
@@ -0,0 +1,310 @@ | ||
#!/usr/bin/env rdmd | ||
/** | ||
Allows running tests individually | ||
Usage: | ||
./run.d <test-file>... | ||
Example: | ||
./run.d runnable/template2962.d fail_compilation/fail282.d | ||
See the README.md for all available test targets | ||
*/ | ||
|
||
import std.algorithm, std.conv, std.datetime, std.exception, std.file, std.format, | ||
std.getopt, std.parallelism, std.path, std.process, std.range, std.stdio, std.string; | ||
import core.stdc.stdlib : exit; | ||
|
||
const scriptDir = __FILE_FULL_PATH__.dirName; | ||
string resultsDir = scriptDir.buildPath("test_results"); | ||
|
||
void main(string[] args) | ||
{ | ||
int jobs = totalCPUs; | ||
auto res = getopt(args, | ||
"j|jobs", "Specifies the number of jobs (commands) to run simultaneously (default: %d)".format(totalCPUs), &jobs, | ||
); | ||
if (res.helpWanted || args.length < 2) | ||
{ | ||
defaultGetoptPrinter(`./run.d <test-file>... | ||
Examples: | ||
./run.d runnable/template2962.d # runs a specific tests | ||
./run.d runnable/template2962.d fail_compilation/fail282.d # runs multiple specific tests | ||
./run.d fail_compilation # runs all tests in fail_compilation | ||
./run.d all # runs all tests | ||
./run.d clean # remove all test results | ||
Options: | ||
`, res.options); | ||
"\nSee the README.md for a more in-depth explanation of the test-runner.".writeln; | ||
return; | ||
} | ||
|
||
// parse arguments | ||
args.popFront; | ||
args2Environment(args); | ||
|
||
// allow overwrites from the environment | ||
resultsDir = environment.get("RESULTS_DIR", resultsDir); | ||
|
||
// bootstrap all needed environment variables | ||
auto env = getEnvironment; | ||
|
||
// default target | ||
if (!args.length) | ||
args = ["all"]; | ||
|
||
// preprocess | ||
auto targets = args.predefinedTargets; | ||
// normalize | ||
targets = targets.map!(f => format!"%s/%s"(f.absolutePath.dirName.baseName, f.baseName)).array; | ||
// filter | ||
targets = targets.filterTargets; | ||
|
||
if (targets.length > 0) | ||
{ | ||
auto taskPool = new TaskPool(jobs); | ||
scope(exit) taskPool.finish(); | ||
ensureToolsExists; | ||
foreach (target; taskPool.parallel(targets, 1)) | ||
{ | ||
auto makeArguments = [scriptDir.buildPath("test_results", "d_do_test"), target]; | ||
spawnProcess(makeArguments, env, Config.none, scriptDir).wait; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
Builds the binary of the tools required by the testsuite | ||
Does nothing if the tools already exist and are newer than their source | ||
*/ | ||
void ensureToolsExists() | ||
{ | ||
static toolsDir = scriptDir.buildPath("tools"); | ||
resultsDir.mkdirRecurse; | ||
auto tools = [ | ||
"d_do_test", | ||
"sanitize_json", | ||
]; | ||
// TODO: run in parallel | ||
foreach (tool; tools) | ||
{ | ||
auto targetBin = resultsDir.buildPath(tool); | ||
auto sourceFile = toolsDir.buildPath(tool ~ ".d"); | ||
if (targetBin.timeLastModified.ifThrown(SysTime.init) > sourceFile.timeLastModified) | ||
writefln("%s is already up-to-date", tool); | ||
else | ||
{ | ||
auto command = ["dmd", "-of"~targetBin, sourceFile]; | ||
writefln("Executing: %-(%s %)", command); | ||
spawnProcess(command).wait; | ||
} | ||
} | ||
|
||
// ensure output directories exist | ||
foreach (dir; ["compilable", "fail_compilation", "runnable"]) | ||
resultsDir.buildPath(dir).mkdirRecurse; | ||
} | ||
|
||
/** | ||
Goes through the target list and replaces short-hand targets with their expanded version. | ||
Special targets: | ||
- clean -> removes resultsDir + immediately stops the runner | ||
*/ | ||
auto predefinedTargets(string[] targets) | ||
{ | ||
static findFiles(string dir) | ||
{ | ||
return scriptDir.buildPath(dir).dirEntries("*{.d,.sh}", SpanMode.shallow).map!(e => e.name); | ||
} | ||
|
||
Appender!(string[]) newTargets; | ||
foreach (t; targets) | ||
{ | ||
switch (t) | ||
{ | ||
case "clean": | ||
resultsDir.rmdirRecurse; | ||
exit(0); | ||
break; | ||
|
||
case "run_runnable_tests", "runnable": | ||
newTargets.put(findFiles("runnable")); | ||
break; | ||
|
||
case "run_fail_compilation_tests", "fail_compilation", "fail": | ||
newTargets.put(findFiles("fail_compilation")); | ||
break; | ||
|
||
case "run_compilable_tests", "compilable", "compile": | ||
newTargets.put(findFiles("compilable")); | ||
break; | ||
|
||
case "all": | ||
newTargets.put(findFiles("runnable")); | ||
newTargets.put(findFiles("fail_compilation")); | ||
newTargets.put(findFiles("compilable")); | ||
break; | ||
|
||
default: | ||
newTargets ~= t; | ||
} | ||
} | ||
return newTargets.data; | ||
} | ||
|
||
// Removes targets that do not need updating (i.e. their .out file exists and is newer than the source file) | ||
auto filterTargets(string[] targets) | ||
{ | ||
bool error; | ||
foreach (target; targets) | ||
{ | ||
if (!scriptDir.buildPath(target).exists) | ||
{ | ||
writefln("Warning: %s can't be found", target); | ||
error = true; | ||
} | ||
} | ||
if (error) | ||
exit(1); | ||
|
||
string[] targetsThatNeedUpdating; | ||
foreach (t; targets) | ||
{ | ||
if (resultsDir.buildPath(t ~ ".out").timeLastModified.ifThrown(SysTime.init) > | ||
scriptDir.buildPath(t).timeLastModified) | ||
writefln("%s is already up-to-date", t); | ||
else | ||
targetsThatNeedUpdating ~= t; | ||
} | ||
return targetsThatNeedUpdating; | ||
} | ||
|
||
// Add additional make-like assignments to the environment | ||
// e.g. ./run.d ARGS=foo -> sets ARGS to 'foo' | ||
void args2Environment(ref string[] args) | ||
{ | ||
bool tryToAdd(string arg) | ||
{ | ||
if (!arg.canFind("=")) | ||
return false; | ||
|
||
auto sp = arg.splitter("="); | ||
environment[sp.front] = sp.dropOne.front; | ||
return true; | ||
} | ||
args = args.filter!(a => !tryToAdd(a)).array; | ||
} | ||
|
||
/** | ||
Checks whether the environment already contains a value for key and if so, sets | ||
the found value to the new environment object. | ||
Otherwise uses the `default_` value as fallback | ||
Params: | ||
env = environment to write the check to | ||
key = key to check for existence and write into the new env | ||
default_ = fallback value if the key doesn't exist in the global environment | ||
*/ | ||
void getDefault(string[string] env, string key, string default_) | ||
{ | ||
if (key in environment) | ||
env[key] = environment[key]; | ||
else | ||
env[key] = default_; | ||
} | ||
|
||
// Sets the environment variables required by d_do_test | ||
string[string] getEnvironment() | ||
{ | ||
string[string] env; | ||
env["RESULTS_DIR"] = resultsDir; | ||
auto os = env["OS"] = detectOS; | ||
auto build = env["BUILD"] = "release"; | ||
|
||
version(Windows) | ||
{ | ||
env.getDefault("ARGS", "-inline -release -g -O"); | ||
auto exe = env["EXE"] = ".exe"; | ||
env["OBJ"] = ".obj"; | ||
env["DSEP"] = `\\`; | ||
env["SEP"] = `\`; | ||
auto druntimePath = environment.get("DRUNTIME_PATH", `..\..\druntime`); | ||
auto phobosPath = environment.get("PHOBOS_PATH", `..\..\phobos`); | ||
env["DFLAGS"] = `-I%s\import -I%s`.format(druntimePath, phobosPath); | ||
env["LIB"] = phobosPath; | ||
|
||
// auto-tester might run the testsuite with a different $(MODEL) than DMD | ||
// has been compiled with. Hence we manually check which binary exists. | ||
// For windows the $(OS) during build is: `windows` | ||
int dmdModel = "../generated/windows/%s/64/dmd%s".format(build, exe).exists ? 64 : 32; | ||
env.getDefault("MODEL", dmdModel.text); | ||
env["DMD"] = "../generated/windows/%s/%d/dmd%s".format(build, dmdModel, exe); | ||
} | ||
else | ||
{ | ||
env.getDefault("ARGS", "-inline -release -g -O -fPIC"); | ||
env["EXE"] = ""; | ||
env["OBJ"] = ".o"; | ||
env["DSEP"] = "/"; | ||
env["SEP"] = "/"; | ||
auto druntimePath = environment.get("DRUNTIME_PATH", `../../druntime`); | ||
auto phobosPath = environment.get("PHOBOS_PATH", `../../phobos`); | ||
|
||
// auto-tester might run the testsuite with a different $(MODEL) than DMD | ||
// has been compiled with. Hence we manually check which binary exists. | ||
int dmdModel = "../generated/%s/%s/64/dmd".format(os, build).exists ? 64 : 32; | ||
env.getDefault("MODEL", dmdModel.text); | ||
auto generatedPath = "generated/%s/%s/%s/dmd".format(os, build, dmdModel); | ||
env["DMD"] = "../" ~ generatedPath; | ||
|
||
// default to PIC on x86_64, use PIC=1/0 to en-/disable PIC. | ||
// Note that shared libraries and C files are always compiled with PIC. | ||
bool pic; | ||
version(X86_64) | ||
pic = true; | ||
else version(X86) | ||
pic = false; | ||
|
||
env["PIC_FLAGS"] = pic ? "-fPIC" : ""; | ||
env["DFLAGS"] = "-I%s/import -I%s".format(druntimePath, phobosPath) | ||
~ " -L-L%s/%s".format(phobosPath, generatedPath); | ||
bool isShared = os.among("linux", "freebsd") >= 0; | ||
if (isShared) | ||
env["DFLAGS"] = env["DFLAGS"] ~ " -defaultlib=libphobos2.so -L-rpath=%s/%s".format(phobosPath, generatedPath); | ||
|
||
env["REQUIRED_ARGS"] = environment.get("REQUIRED_ARGS") ~ env["PIC_FLAGS"]; | ||
} | ||
return env; | ||
} | ||
|
||
/* | ||
Detects the host OS. | ||
Returns: a string from `{windows, osx,linux,freebsd,openbsd,netbsd,dragonflybsd,solaris}` | ||
*/ | ||
string detectOS() | ||
{ | ||
version(Windows) | ||
return "windows"; | ||
version(OSX) | ||
return "osx"; | ||
else version(linux) | ||
return "linux"; | ||
else version(FreeBSD) | ||
return "freebsd"; | ||
else version(OpenBSD) | ||
return "openbsd"; | ||
else version(NetBSD) | ||
return "netbsd"; | ||
else version(DragonFlyBSD) | ||
return "dragonflybsd"; | ||
else version(Solaris) | ||
return "solaris"; | ||
else version(SunOS) | ||
return "solaris"; | ||
else | ||
static assert(0, "Unrecognized or unsupported OS."); | ||
} |
This file was deleted.
Oops, something went wrong.