Skip to content

Commit 9f9721c

Browse files
committed
Maintain same environment for dmd -run
Fix issue 18729
1 parent 0858136 commit 9f9721c

File tree

7 files changed

+161
-23
lines changed

7 files changed

+161
-23
lines changed

src/dmd/dinifile.d

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import core.sys.posix.stdlib;
1919
import core.sys.windows.winbase;
2020
import core.sys.windows.windef;
2121

22+
import dmd.env;
2223
import dmd.errors;
2324
import dmd.globals;
2425
import dmd.root.filename;
@@ -28,7 +29,6 @@ import dmd.root.stringtable;
2829
import dmd.root.rmem : xarraydup;
2930
import dmd.utils;
3031

31-
version (Windows) extern (C) int putenv(const char*) nothrow;
3232
private enum LOG = false;
3333

3434
/*****************************
@@ -145,19 +145,11 @@ void updateRealEnvironment(ref StringTable environment)
145145
static int envput(const(StringValue)* sv) nothrow
146146
{
147147
const name = sv.toDchars();
148-
const namelen = strlen(name);
149148
const value = cast(const(char)*)sv.ptrvalue;
150149
if (!value) // deleted?
151150
return 0;
152-
const valuelen = strlen(value);
153-
auto s = cast(char*)malloc(namelen + 1 + valuelen + 1);
154-
assert(s);
155-
memcpy(s, name, namelen);
156-
s[namelen] = '=';
157-
memcpy(s + namelen + 1, value, valuelen);
158-
s[namelen + 1 + valuelen] = 0;
159-
//printf("envput('%s')\n", s);
160-
putenv(s);
151+
if (0 != putenvRestorable(name.toDString, value.toDString))
152+
assert(0, "putenvRestorable failed");
161153
return 0; // do all of them
162154
}
163155

src/dmd/env.d

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
module dmd.env;
2+
3+
import core.stdc.string;
4+
import core.sys.posix.stdlib;
5+
import dmd.globals;
6+
import dmd.root.array;
7+
import dmd.root.rmem;
8+
import dmd.utils;
9+
10+
version (Windows)
11+
private extern (C) int putenv(const char*) nothrow;
12+
13+
/**
14+
Construct a variable from `name` and `value` and put it in the environment while saving
15+
the current value of the environment variable.
16+
Returns:
17+
0 on success, non-zero on failure
18+
*/
19+
int putenvRestorable(const(char)[] name, const(char)[] value) nothrow
20+
{
21+
auto var = LocalEnvVar.xmalloc(name, value);
22+
const result = var.putenvRestorable();
23+
var.xfree(result);
24+
return result;
25+
}
26+
27+
/// Holds a `VAR=value` string that can be put into the global environment.
28+
struct LocalEnvVar
29+
{
30+
string nameValueCStr; // The argument passed to `putenv`.
31+
private size_t equalsIndex; // index of '=' in nameValueCStr.
32+
33+
/// The name of the variable
34+
auto name() const { return nameValueCStr[0 .. equalsIndex]; }
35+
36+
/// The value of the variable
37+
auto value() const { return nameValueCStr[equalsIndex + 1 .. $]; }
38+
39+
/**
40+
Put this variable in the environment while saving the current value of the
41+
environment variable.
42+
Returns:
43+
0 on success, non-zero on failure
44+
*/
45+
int putenvRestorable() const nothrow
46+
{
47+
RestorableEnv.save(name);
48+
return .putenv(cast(char*)nameValueCStr.ptr);
49+
}
50+
51+
/**
52+
Allocate a new variable via xmalloc that can be promoted to the global environment.
53+
Params:
54+
name = name of the variable
55+
value = value of the variable
56+
Returns:
57+
a newly allocated variable that can be promoted to the global environment
58+
*/
59+
static LocalEnvVar xmalloc(const(char)[] name, const(char)[] value) nothrow
60+
{
61+
const length = name.length + 1 + value.length;
62+
auto str = (cast(char*)mem.xmalloc(length + 1))[0 .. length];
63+
str[0 .. name.length] = name[];
64+
str[name.length] = '=';
65+
str[name.length + 1 .. length] = value[];
66+
str.ptr[length] = '\0';
67+
return LocalEnvVar(cast(string)str, name.length);
68+
}
69+
70+
private void xfree(int putenvResult) const nothrow
71+
{
72+
bool doFree;
73+
version (Windows)
74+
doFree = true;
75+
else
76+
{
77+
// on posix, when putenv succeeds ownership of the memory is transferred
78+
// to the global environment
79+
doFree = (putenvResult != 0);
80+
}
81+
if (doFree)
82+
mem.xfree(cast(void*)nameValueCStr.ptr);
83+
}
84+
}
85+
86+
/// Provides save/restore functionality for environment variables.
87+
struct RestorableEnv
88+
{
89+
/// Holds the original values of environment variables when they are overwritten.
90+
private __gshared Array!LocalEnvVar originalVars;
91+
92+
/// Restore the original environment.
93+
static void restore()
94+
{
95+
foreach (var; originalVars)
96+
{
97+
if (0 != putenv(cast(char*)var.nameValueCStr))
98+
assert(0, "putenv failed");
99+
}
100+
}
101+
102+
/// Save the environment variable `name` if not saved already.
103+
static void save(const(char)[] name) nothrow
104+
{
105+
foreach (var; originalVars)
106+
{
107+
if (name == var.name)
108+
return; // already saved
109+
}
110+
originalVars.push(LocalEnvVar.xmalloc(name,
111+
name.toCStringThen!(n => getenv(n.ptr)).toDString));
112+
}
113+
}

src/dmd/link.d

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import core.sys.posix.unistd;
2121
import core.sys.windows.winbase;
2222
import core.sys.windows.windef;
2323
import core.sys.windows.winreg;
24+
import dmd.env;
2425
import dmd.errors;
2526
import dmd.globals;
2627
import dmd.root.file;
@@ -30,7 +31,6 @@ import dmd.root.rmem;
3031
import dmd.utils;
3132

3233
version (Posix) extern (C) int pipe(int*);
33-
version (Windows) extern (C) int putenv(const char*);
3434
version (Windows) extern (C) int spawnlp(int, const char*, const char*, const char*, const char*);
3535
version (Windows) extern (C) int spawnl(int, const char*, const char*, const char*, const char*);
3636
version (Windows) extern (C) int spawnv(int, const char*, const char**);
@@ -765,17 +765,11 @@ version (Windows)
765765
{
766766
if ((len = strlen(args)) > 255)
767767
{
768-
char* q = cast(char*)alloca(8 + len + 1);
769-
sprintf(q, "_CMDLINE=%s", args);
770-
status = putenv(q);
768+
status = putenvRestorable("_CMDLINE", args[0 .. len]);
771769
if (status == 0)
772-
{
773770
args = "@_CMDLINE";
774-
}
775771
else
776-
{
777772
error(Loc.initial, "command line length of %d is too long", len);
778-
}
779773
}
780774
}
781775
// Normalize executable path separators
@@ -893,6 +887,7 @@ public int runProgram()
893887
argv.push(a);
894888
}
895889
argv.push(null);
890+
RestorableEnv.restore();
896891
version (Windows)
897892
{
898893
const(char)[] ex = FileName.name(global.params.exefile);
@@ -1053,12 +1048,15 @@ version (Windows)
10531048
const pathlen = strlen(path);
10541049
const addpathlen = strlen(addpath);
10551050

1056-
char* npath = cast(char*)mem.xmalloc(5 + pathlen + 1 + addpathlen + 1);
1051+
const length = 5 + addpathlen + 1 + pathlen;
1052+
char* npath = cast(char*)mem.xmalloc(length + 1);
10571053
memcpy(npath, "PATH=".ptr, 5);
10581054
memcpy(npath + 5, addpath, addpathlen);
10591055
npath[5 + addpathlen] = ';';
10601056
memcpy(npath + 5 + addpathlen + 1, path, pathlen + 1);
1061-
putenv(npath);
1057+
if (LocalEnvVar(cast(string)npath[0 .. length], 4).putenvRestorable != 0)
1058+
assert(0, "putenvRestorable failed");
1059+
mem.xfree(npath); // on windows putenv makes a copy so we can free
10621060
}
10631061
return cmdbuf.extractChars();
10641062
}

src/posix.mak

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ FRONT_SRCS=$(addsuffix .d, $(addprefix $D/,access aggregate aliasthis apply argt
317317
arraytypes astcodegen ast_node attrib builtin canthrow cli clone compiler complex cond constfold \
318318
cppmangle cppmanglewin ctfeexpr ctorflow dcast dclass declaration delegatize denum dimport \
319319
dinifile dinterpret dmacro dmangle dmodule doc dscope dstruct dsymbol dsymbolsem \
320-
dtemplate dversion escape expression expressionsem func \
320+
dtemplate dversion env escape expression expressionsem func \
321321
hdrgen id impcnvtab imphint init initsem inline inlinecost intrange \
322322
json lambdacomp lib libelf libmach link mars mtype nogc nspace objc opover optimize parse permissivevisitor sapply templateparamsem \
323323
sideeffect statement staticassert target typesem traits transitivevisitor parsetimevisitor visitor \

src/win32.mak

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ FRONT_SRCS=$D/access.d $D/aggregate.d $D/aliasthis.d $D/apply.d $D/argtypes.d $D
139139
$D/cond.d $D/constfold.d $D/cppmangle.d $D/cppmanglewin.d $D/ctfeexpr.d $D/ctorflow.d $D/dcast.d $D/dclass.d \
140140
$D/declaration.d $D/delegatize.d $D/denum.d $D/dimport.d $D/dinifile.d $D/dinterpret.d \
141141
$D/dmacro.d $D/dmangle.d $D/dmodule.d $D/doc.d $D/dscope.d $D/dstruct.d $D/dsymbol.d $D/dsymbolsem.d \
142-
$D/lambdacomp.d $D/dtemplate.d $D/dversion.d $D/escape.d \
142+
$D/lambdacomp.d $D/dtemplate.d $D/dversion.d $D/env.d $D/escape.d \
143143
$D/expression.d $D/expressionsem.d $D/func.d $D/hdrgen.d $D/id.d $D/imphint.d \
144144
$D/impcnvtab.d $D/init.d $D/initsem.d $D/inline.d $D/inlinecost.d $D/intrange.d $D/json.d $D/lib.d $D/link.d \
145145
$D/mars.d $D/mtype.d $D/nogc.d $D/nspace.d $D/objc.d $D/opover.d $D/optimize.d $D/parse.d \

test/dshell/extra-files/printenv.d

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import std.array, std.stdio, std.process, std.algorithm;
2+
void main()
3+
{
4+
foreach (varPair; environment.toAA().byKeyValue.array.sort!"a.key < b.key")
5+
{
6+
if (varPair.key != "_")
7+
{
8+
writeln(varPair.key, "=", varPair.value);
9+
}
10+
}
11+
}

test/dshell/sameenv.d

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import dshell;
2+
void main()
3+
{
4+
run("$DMD -m$MODEL -of$OUTPUT_BASE/printenv$EXE $EXTRA_FILES/printenv.d");
5+
6+
const envFromExe = shellExpand("$OUTPUT_BASE/envFromExe.txt");
7+
const envFromRun = shellExpand("$OUTPUT_BASE/envFromRun.txt");
8+
run("$OUTPUT_BASE/printenv$EXE", File(envFromExe, "wb"));
9+
10+
run("$DMD -m$MODEL -run $EXTRA_FILES/printenv.d", File(envFromRun, "wb"));
11+
12+
const fromExe = readText(envFromExe);
13+
const fromRun = readText(envFromRun);
14+
if (fromExe != fromRun)
15+
{
16+
writefln("FromExe:");
17+
writeln("-----------");
18+
writeln(fromExe);
19+
writefln("FromRun:");
20+
writeln("-----------");
21+
writeln(fromRun);
22+
assert(0, "output from exe/run differ");
23+
}
24+
}

0 commit comments

Comments
 (0)