From e29a1918366b1899063ed754cbc8443133c75468 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Mon, 9 Apr 2018 21:56:55 +0100 Subject: [PATCH 01/12] don't capture dmd output --- source/dpp/runtime/app.d | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/source/dpp/runtime/app.d b/source/dpp/runtime/app.d index 787f40f0..8772c7dc 100644 --- a/source/dpp/runtime/app.d +++ b/source/dpp/runtime/app.d @@ -11,9 +11,10 @@ import dpp.from; void run(in from!"dpp.runtime.options".Options options) @safe { import std.stdio: File; import std.exception: enforce; - import std.process: execute; + import std.process: spawnProcess, wait; import std.array: join; import std.file: remove; + import std.conv : text; foreach(dppFileName; options.dppFileNames) preprocess!File(options, dppFileName, options.toDFileName(dppFileName)); @@ -21,12 +22,12 @@ void run(in from!"dpp.runtime.options".Options options) @safe { if(options.preprocessOnly) return; const args = options.dlangCompiler ~ options.dlangCompilerArgs; - const res = execute(args); - enforce(res.status == 0, "Could not execute `" ~ args.join(" ") ~ "`:\n" ~ res.output); + const status = spawnProcess(args).wait(); if(!options.keepDlangFiles) { foreach(fileName; options.dFileNames) remove(fileName); } + enforce(status == 0, "Executing `" ~ args.join(" ") ~ "` failed with exit code\n" ~ status.text); } From 28abba7b610cac78be1f677eab4b1b5bd08031c7 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Mon, 9 Apr 2018 21:58:37 +0100 Subject: [PATCH 02/12] rsp support --- source/dpp/runtime/options.d | 3 + source/dpp/runtime/response.d | 200 ++++++++++++++++++++++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 source/dpp/runtime/response.d diff --git a/source/dpp/runtime/options.d b/source/dpp/runtime/options.d index 097f10da..a71fa88e 100644 --- a/source/dpp/runtime/options.d +++ b/source/dpp/runtime/options.d @@ -35,6 +35,7 @@ struct Options { import std.algorithm: map, filter, canFind, startsWith; import std.array: array; import std.conv: text; + import dpp.runtime.response : response_expand; parseArgs(args); if(earlyExit) return; @@ -46,6 +47,8 @@ struct Options { else enforce(args.length >= 2, "Not enough arguments\n" ~ usage); + args = response_expand(args); + dppFileNames = args.filter!(a => a.extension == ".dpp").array; enforce(dppFileNames.length != 0, "No .dpp input file specified\n" ~ usage); diff --git a/source/dpp/runtime/response.d b/source/dpp/runtime/response.d new file mode 100644 index 00000000..69ce9e8c --- /dev/null +++ b/source/dpp/runtime/response.d @@ -0,0 +1,200 @@ +module dpp.runtime.response; +import core.stdc.stdio; +import core.stdc.stdlib; +import core.stdc.string; + +string[] response_expand(string[] args) @trusted +{ + import std.algorithm : map; + import std.array : array; + import std.string : fromStringz; + + auto cargs = args + .map!toConstStringz + .array; + response_expand(cargs); + return cargs + .map!(s => s.fromStringz.idup) + .array; +} + +const(char)* toConstStringz(string s) @safe +{ + auto r = new char[](s.length + 1); + size_t i = 0; + while (i < s.length) + { + r[i] = s[i]; + if (r[i] == '\0') + break; + ++i; + } + if (i == s.length) + r[i] = '\0'; + return &r[0]; +} + +bool response_expand(ref const(char)*[] args) +{ + import std.algorithm : remove; + import std.file : readText; + import std.array : insertInPlace; + + const(char)* cp; + int recurse = 0; + for (size_t i = 0; i < args.length;) + { + cp = args[i]; + if (*cp != '@') + { + ++i; + continue; + } + args = args.remove(i); + char* buffer; + char* bufend; + cp++; + if (auto p = getenv(cp)) + { + buffer = strdup(p); + if (!buffer) + goto noexpand; + bufend = buffer + strlen(buffer); + } + else + { + auto s = cp[0 .. strlen(cp)].readText!(char[]); + buffer = s.ptr; + bufend = buffer + s.length; + } + // The logic of this should match that in setargv() + int comment = 0; + for (auto p = buffer; p < bufend; p++) + { + char* d; + char c, lastc; + ubyte instring; + int num_slashes, non_slashes; + switch (*p) + { + case 26: + /* ^Z marks end of file */ + goto L2; + case 0xD: + case '\n': + if (comment) + { + comment = 0; + } + goto case; + case 0: + case ' ': + case '\t': + continue; + // scan to start of argument + case '#': + comment = 1; + continue; + case '@': + if (comment) + { + continue; + } + recurse = 1; + goto default; + default: + /* start of new argument */ + if (comment) + { + continue; + } + args.insertInPlace(i, p); + ++i; + instring = 0; + c = 0; + num_slashes = 0; + for (d = p; 1; p++) + { + lastc = c; + if (p >= bufend) + { + *d = 0; + goto L2; + } + c = *p; + switch (c) + { + case '"': + /* + Yes this looks strange,but this is so that we are + MS Compatible, tests have shown that: + \\\\"foo bar" gets passed as \\foo bar + \\\\foo gets passed as \\\\foo + \\\"foo gets passed as \"foo + and \"foo gets passed as "foo in VC! + */ + non_slashes = num_slashes % 2; + num_slashes = num_slashes / 2; + for (; num_slashes > 0; num_slashes--) + { + d--; + *d = '\0'; + } + if (non_slashes) + { + *(d - 1) = c; + } + else + { + instring ^= 1; + } + break; + case 26: + *d = 0; // terminate argument + goto L2; + case 0xD: + // CR + c = lastc; + continue; + // ignore + case '@': + recurse = 1; + goto Ladd; + case ' ': + case '\t': + if (!instring) + { + case '\n': + case 0: + *d = 0; // terminate argument + goto Lnextarg; + } + goto default; + default: + Ladd: + if (c == '\\') + num_slashes++; + else + num_slashes = 0; + *d++ = c; + break; + } + } + break; + } + Lnextarg: + } + L2: + } + if (recurse) + { + /* Recursively expand @filename */ + if (response_expand(args)) + goto noexpand; + } + return false; /* success */ +noexpand: + /* error */ + /* BUG: any file buffers are not free'd */ + return true; +} From c17706901c34edbe7c529a176700db8701c76ef3 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Mon, 9 Apr 2018 21:59:36 +0100 Subject: [PATCH 03/12] allow zero dpp files --- source/dpp/runtime/options.d | 1 - 1 file changed, 1 deletion(-) diff --git a/source/dpp/runtime/options.d b/source/dpp/runtime/options.d index a71fa88e..b18215d4 100644 --- a/source/dpp/runtime/options.d +++ b/source/dpp/runtime/options.d @@ -50,7 +50,6 @@ struct Options { args = response_expand(args); dppFileNames = args.filter!(a => a.extension == ".dpp").array; - enforce(dppFileNames.length != 0, "No .dpp input file specified\n" ~ usage); // Remove the name of this binary and the name of the .dpp input file from args // so that a D compiler can use the remaining entries. From 4dfa937ccea6dd3ae6a03e4879333f0a49b22c3d Mon Sep 17 00:00:00 2001 From: John Colvin Date: Mon, 9 Apr 2018 22:02:30 +0100 Subject: [PATCH 04/12] leave -of to dmd --- source/dpp/runtime/options.d | 9 --------- 1 file changed, 9 deletions(-) diff --git a/source/dpp/runtime/options.d b/source/dpp/runtime/options.d index b18215d4..502a0eaf 100644 --- a/source/dpp/runtime/options.d +++ b/source/dpp/runtime/options.d @@ -57,15 +57,6 @@ struct Options { args[1..$].filter!(a => a.extension != ".dpp").array ~ dFileNames; - // if no -of option is given, default to the name of the .dpp file - if(!dlangCompilerArgs.canFind!(a => a.startsWith("-of")) && !dlangCompilerArgs.canFind("-c")) - dlangCompilerArgs ~= "-of" ~ - args. - filter!(a => a.extension == ".dpp" || a.extension == ".d") - .front - .stripExtension - ~ exeExtension; - includePaths = systemPaths ~ includePaths; } From 47ad4873bf5649834f0df74262cb4dd73b82e001 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Mon, 9 Apr 2018 23:22:37 +0200 Subject: [PATCH 05/12] documentation --- source/dpp/runtime/response.d | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/dpp/runtime/response.d b/source/dpp/runtime/response.d index 69ce9e8c..c5fab306 100644 --- a/source/dpp/runtime/response.d +++ b/source/dpp/runtime/response.d @@ -3,6 +3,8 @@ import core.stdc.stdio; import core.stdc.stdlib; import core.stdc.string; +/// wrapper to make response_expand usable +/// with strings. Yes, it allocates a lot. string[] response_expand(string[] args) @trusted { import std.algorithm : map; @@ -34,6 +36,9 @@ const(char)* toConstStringz(string s) @safe return &r[0]; } +// ported from dmd's function of the same name. +// only modifications are to use builtin arrays +// and phobos in place of dmd's bespoke types bool response_expand(ref const(char)*[] args) { import std.algorithm : remove; From 0228bc15afa01d3fc449e6d11dc13f82e3deec4a Mon Sep 17 00:00:00 2001 From: John Colvin Date: Mon, 9 Apr 2018 23:23:04 +0200 Subject: [PATCH 06/12] check response_expand return --- source/dpp/runtime/response.d | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/dpp/runtime/response.d b/source/dpp/runtime/response.d index c5fab306..06485aa1 100644 --- a/source/dpp/runtime/response.d +++ b/source/dpp/runtime/response.d @@ -10,11 +10,12 @@ string[] response_expand(string[] args) @trusted import std.algorithm : map; import std.array : array; import std.string : fromStringz; + import std.exception : enforce; auto cargs = args .map!toConstStringz .array; - response_expand(cargs); + enforce(!response_expand(cargs), "expanding args failed"); return cargs .map!(s => s.fromStringz.idup) .array; From 37d699a02ead53b1374ce7630a7b2ed37868f3a9 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Mon, 9 Apr 2018 23:34:50 +0200 Subject: [PATCH 07/12] remove test for 0 dpp files --- tests/it/issues.d | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/tests/it/issues.d b/tests/it/issues.d index e9415d62..e4a710be 100644 --- a/tests/it/issues.d +++ b/tests/it/issues.d @@ -165,22 +165,6 @@ import it; ); } -@Tags("issue") -@("14") -@safe unittest { - import dpp.runtime.options: Options; - with(immutable IncludeSandbox()) { - - writeFile("foo.h", - q{ - typedef int foo; - }); - - runPreprocessOnly("foo.h").shouldThrowWithMessage( - "No .dpp input file specified\n" ~ Options.usage); - } -} - @Tags("issue", "preprocessor") @("22.0") @safe unittest { From 4ed81dd2c63b74c76800565dab7041d95d749d7e Mon Sep 17 00:00:00 2001 From: John Colvin Date: Tue, 10 Apr 2018 10:10:27 +0200 Subject: [PATCH 08/12] preserve file order --- source/dpp/runtime/options.d | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/dpp/runtime/options.d b/source/dpp/runtime/options.d index 502a0eaf..79f93438 100644 --- a/source/dpp/runtime/options.d +++ b/source/dpp/runtime/options.d @@ -53,9 +53,9 @@ struct Options { // Remove the name of this binary and the name of the .dpp input file from args // so that a D compiler can use the remaining entries. - dlangCompilerArgs = - args[1..$].filter!(a => a.extension != ".dpp").array ~ - dFileNames; + dlangCompilerArgs = args[1..$] + .map!(a => a.extension == ".dpp" ? toDFileName(a) : a) + .array; includePaths = systemPaths ~ includePaths; } From 7d2d43118032128019af6de011707d05bf2257cf Mon Sep 17 00:00:00 2001 From: John Colvin Date: Thu, 3 May 2018 17:26:59 +0200 Subject: [PATCH 09/12] test toConstStringz --- tests/ut/response.d | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tests/ut/response.d diff --git a/tests/ut/response.d b/tests/ut/response.d new file mode 100644 index 00000000..89ef8d73 --- /dev/null +++ b/tests/ut/response.d @@ -0,0 +1,22 @@ +module ut.reponse; + +import dpp.test; +import dpp.runtime.response; + +@("toConstStringz") +@safe unittest +{ + import core.stdc.string : strlen; + import std.algorithm : until; + import std.conv : text; + foreach (s; [null, "", "a", "áåâà", "fd\0fds"]) + { + // make sure we're not being saved by string + // literals being null-terminated + auto cstr = (() => (s ~ 'a')[0 .. $ - 1])() + .toConstStringz; + static assert(is(typeof(cstr) == const(char)*)); + (() @trusted => cstr[0 .. strlen(cstr)])() + .shouldEqual(s.until('\0').text); + } +} From bf4d107826d7e0e1b333360f34f91664a5b14c3c Mon Sep 17 00:00:00 2001 From: John Colvin Date: Thu, 3 May 2018 17:27:42 +0200 Subject: [PATCH 10/12] #include doesn't parse in D --- source/dpp/runtime/app.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/dpp/runtime/app.d b/source/dpp/runtime/app.d index 8772c7dc..80733948 100644 --- a/source/dpp/runtime/app.d +++ b/source/dpp/runtime/app.d @@ -109,7 +109,7 @@ private string preamble() @safe pure { import core.stdc.config; import core.stdc.stdarg: va_list; struct __locale_data { int dummy; } // FIXME - #define __gnuc_va_list va_list + } ~ "#define __gnuc_va_list va_list\n" ~ q{ alias _Bool = bool; struct dpp { From e00b3654fe99a4db88b0d4c9173444fc41af64f9 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Thu, 3 May 2018 17:54:37 +0200 Subject: [PATCH 11/12] register response test file in test_main.d --- tests/test_main.d | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_main.d b/tests/test_main.d index 12c038fc..4a0500db 100644 --- a/tests/test_main.d +++ b/tests/test_main.d @@ -10,6 +10,7 @@ int main(string[] args) { // unit tests "ut.type", + "ut.reponse", "it.issues", From 7ef9daa6534ba588b818a15f0bb8d330027d7756 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Fri, 4 May 2018 11:37:22 +0200 Subject: [PATCH 12/12] better toConstStringz test --- tests/test_main.d | 2 +- tests/ut/response.d | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/tests/test_main.d b/tests/test_main.d index 4a0500db..dee81996 100644 --- a/tests/test_main.d +++ b/tests/test_main.d @@ -10,7 +10,7 @@ int main(string[] args) { // unit tests "ut.type", - "ut.reponse", + "ut.response", "it.issues", diff --git a/tests/ut/response.d b/tests/ut/response.d index 89ef8d73..947c3f42 100644 --- a/tests/ut/response.d +++ b/tests/ut/response.d @@ -1,4 +1,4 @@ -module ut.reponse; +module ut.response; import dpp.test; import dpp.runtime.response; @@ -6,17 +6,15 @@ import dpp.runtime.response; @("toConstStringz") @safe unittest { - import core.stdc.string : strlen; import std.algorithm : until; - import std.conv : text; foreach (s; [null, "", "a", "áåâà", "fd\0fds"]) { // make sure we're not being saved by string // literals being null-terminated - auto cstr = (() => (s ~ 'a')[0 .. $ - 1])() + auto cstr = (s ~ 'a')[0 .. $ - 1] .toConstStringz; static assert(is(typeof(cstr) == const(char)*)); - (() @trusted => cstr[0 .. strlen(cstr)])() - .shouldEqual(s.until('\0').text); + (() @trusted => cstr[0 .. s.length + 1])().until('\0') + .shouldEqual(s.until('\0')); } }