Skip to content

Commit

Permalink
Add betterC probing for performance and for applications without drun… (
Browse files Browse the repository at this point in the history
#2753)

* Add betterC probing for performance and for applications without druntime
* Build probe pragmas using a pragmaGen function rather than string duplication.
* Add some formatting adjustments
  • Loading branch information
etcimon authored Jan 10, 2024
1 parent 96f495b commit 509beb8
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 56 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ __dummy.html
/bin/dub-test-library
/bin/libdub.a
/bin/dub-*
/bin/dub.*

# Ignore files or directories created by the test suite.
*.exe
Expand Down
13 changes: 5 additions & 8 deletions source/dub/compilers/compiler.d
Original file line number Diff line number Diff line change
Expand Up @@ -177,24 +177,21 @@ interface Compiler {
args = arguments for the probe compilation
arch_override = special handler for x86_mscoff
*/
protected final BuildPlatform probePlatform(string compiler_binary, string[] args,
string arch_override)
protected final BuildPlatform probePlatform(string compiler_binary, string[] args, string arch_override)
{
import dub.compilers.utils : generatePlatformProbeFile, readPlatformJsonProbe;
import dub.compilers.utils : generatePlatformProbeFile, readPlatformSDLProbe;
import std.string : format, strip;

auto fil = generatePlatformProbeFile();
NativePath fil = generatePlatformProbeFile();

auto result = execute(compiler_binary ~ args ~ fil.toNativeString());
enforce!CompilerInvocationException(result.status == 0,
format("Failed to invoke the compiler %s to determine the build platform: %s",
compiler_binary, result.output));

auto build_platform = readPlatformJsonProbe(result.output);
BuildPlatform build_platform = readPlatformSDLProbe(result.output);
string ver = determineVersion(compiler_binary, result.output).strip;
build_platform.compilerBinary = compiler_binary;

auto ver = determineVersion(compiler_binary, result.output)
.strip;
if (ver.empty) {
logWarn(`Could not probe the compiler version for "%s". ` ~
`Toolchain requirements might be ineffective`, build_platform.compiler);
Expand Down
120 changes: 72 additions & 48 deletions source/dub/compilers/utils.d
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
module dub.compilers.utils;

import dub.compilers.buildsettings;
import dub.platform : BuildPlatform, archCheck, compilerCheck, platformCheck;
import dub.platform : BuildPlatform, archCheck, compilerCheckPragmas, platformCheck, pragmaGen;
import dub.internal.vibecompat.inet.path;
import dub.internal.logging;

Expand Down Expand Up @@ -269,77 +269,101 @@ private enum probeEndMark = "__dub_probe_end__";
NativePath generatePlatformProbeFile()
{
import dub.internal.vibecompat.core.file;
import dub.internal.vibecompat.data.json;
import dub.internal.utils;
import std.string : format;

// try to not use phobos in the probe to avoid long import times
enum probe = q{
module dub_platform_probe;

template toString(int v) { enum toString = v.stringof; }
string stringArray(string[] ary) {
string res;
foreach (i, e; ary) {
if (i)
res ~= ", ";
res ~= '"' ~ e ~ '"';
}
return res;
}
enum moduleInfo = q{
module object;
alias string = const(char)[];
};

pragma(msg, `%1$s`
~ '\n' ~ `{`
~ '\n' ~ ` "compiler": "`~ determineCompiler() ~ `",`
~ '\n' ~ ` "frontendVersion": ` ~ toString!__VERSION__ ~ `,`
~ '\n' ~ ` "compilerVendor": "` ~ __VENDOR__ ~ `",`
~ '\n' ~ ` "platform": [`
~ '\n' ~ ` ` ~ determinePlatform().stringArray
~ '\n' ~ ` ],`
~ '\n' ~ ` "architecture": [`
~ '\n' ~ ` ` ~ determineArchitecture().stringArray
~ '\n' ~ ` ],`
~ '\n' ~ `}`
~ '\n' ~ `%2$s`);

string[] determinePlatform() { %3$s }
string[] determineArchitecture() { %4$s }
string determineCompiler() { %5$s }

}.format(probeBeginMark, probeEndMark, platformCheck, archCheck, compilerCheck);
// avoid druntime so that this compiles without a compiler's builtin object.d
enum probe = q{
%1$s
pragma(msg, `%2$s`);
pragma(msg, `\n`);
pragma(msg, `compiler`);
%6$s
pragma(msg, `\n`);
pragma(msg, `frontendVersion "`);
pragma(msg, __VERSION__.stringof);
pragma(msg, `"\n`);
pragma(msg, `compilerVendor "`);
pragma(msg, __VENDOR__);
pragma(msg, `"\n`);
pragma(msg, `platform`);
%4$s
pragma(msg, `\n`);
pragma(msg, `architecture `);
%5$s
pragma(msg, `\n`);
pragma(msg, `%3$s`);
}.format(moduleInfo, probeBeginMark, probeEndMark, pragmaGen(platformCheck), pragmaGen(archCheck), compilerCheckPragmas);

auto path = getTempFile("dub_platform_probe", ".d");
writeFile(path, probe);

return path;
}


/**
Processes the JSON output generated by compiling the platform probe file.
Processes the SDL output generated by compiling the platform probe file.
See_Also: `generatePlatformProbeFile`.
*/
BuildPlatform readPlatformJsonProbe(string output)
BuildPlatform readPlatformSDLProbe(string output)
{
import std.algorithm : map;
import std.algorithm : map, max, splitter, joiner, count, filter;
import std.array : array;
import std.exception : enforce;
import std.range : front;
import std.ascii : newline;
import std.string;
import dub.internal.sdlang.parser;
import dub.internal.sdlang.ast;
import std.conv;

// work around possible additional output of the compiler
auto idx1 = output.indexOf(probeBeginMark);
auto idx2 = output.lastIndexOf(probeEndMark);
auto idx1 = output.indexOf(probeBeginMark ~ newline ~ "\\n");
auto idx2 = output[max(0, idx1) .. $].indexOf(probeEndMark) + idx1;
enforce(idx1 >= 0 && idx1 < idx2,
"Unexpected platform information output - does not contain a JSON object.");
output = output[idx1+probeBeginMark.length .. idx2];
output = output[idx1 + probeBeginMark.length .. idx2].replace(newline, "").replace("\\n", "\n");

import dub.internal.vibecompat.data.json;
auto json = parseJsonString(output);
output = output.splitter("\n").filter!((e) => e.length > 0)
.map!((e) {
if (e.count("\"") == 0)
{
return e ~ ` ""`;
}
return e;
})
.joiner("\n").array().to!string;

BuildPlatform build_platform;
build_platform.platform = json["platform"].get!(Json[]).map!(e => e.get!string()).array();
build_platform.architecture = json["architecture"].get!(Json[]).map!(e => e.get!string()).array();
build_platform.compiler = json["compiler"].get!string;
build_platform.frontendVersion = json["frontendVersion"].get!int;
Tag sdl = parseSource(output);

foreach (n; sdl.all.tags)
{
switch (n.name)
{
default:
break;
case "platform":
build_platform.platform = n.values.map!(e => e.toString()).array();
break;
case "architecture":
build_platform.architecture = n.values.map!(e => e.toString()).array();
break;
case "compiler":
build_platform.compiler = n.values.front.toString();
break;
case "frontendVersion":
build_platform.frontendVersion = n.values.front.toString()
.filter!((e) => e >= '0' && e <= '9').array().to!string
.to!int;
break;
}
}
return build_platform;
}
15 changes: 15 additions & 0 deletions source/dub/platform.d
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,21 @@ enum string compilerCheck = q{
else return null;
};

/// private
enum string compilerCheckPragmas = q{
version(DigitalMars) pragma(msg, ` "dmd"`);
else version(GNU) pragma(msg, ` "gdc"`);
else version(LDC) pragma(msg, ` "ldc"`);
else version(SDC) pragma(msg, ` "sdc"`);
};

/// private, converts the above appender strings to pragmas
string pragmaGen(string str) {
import std.string : replace;
return str.replace("return ret;", "").replace("string[] ret;", "").replace(`["`, `"`).replace(`", "`,`" "`).replace(`"]`, `"`).replace(`;`, "`);").replace("ret ~= ", "pragma(msg, ` ");
}


/** Determines the full build platform used for the current build.
Note that the `BuildPlatform.compilerBinary` field will be left empty.
Expand Down

0 comments on commit 509beb8

Please sign in to comment.