Skip to content

Commit 4e4e08e

Browse files
committed
Maintain same environment for dmd -run
Fix issue 18729
1 parent edc9b35 commit 4e4e08e

7 files changed

Lines changed: 160 additions & 24 deletions

File tree

src/dmd/dinifile.d

Lines changed: 3 additions & 12 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.rmem;
@@ -28,7 +29,6 @@ import dmd.root.port;
2829
import dmd.root.stringtable;
2930
import dmd.utils;
3031

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

3434
/*****************************
@@ -145,20 +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-
if (!s)
155-
Mem.error();
156-
memcpy(s, name, namelen);
157-
s[namelen] = '=';
158-
memcpy(s + namelen + 1, value, valuelen);
159-
s[namelen + 1 + valuelen] = 0;
160-
//printf("envput('%s')\n", s);
161-
putenv(s);
151+
if (0 != putenvRestorable(name.toDString, value.toDString))
152+
assert(0, "putenvRestorable failed");
162153
return 0; // do all of them
163154
}
164155

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**);
@@ -769,17 +769,11 @@ version (Windows)
769769
{
770770
if ((len = strlen(args)) > 255)
771771
{
772-
char* q = cast(char*)alloca(8 + len + 1);
773-
sprintf(q, "_CMDLINE=%s", args);
774-
status = putenv(q);
772+
status = putenvRestorable("_CMDLINE", args[0 .. len]);
775773
if (status == 0)
776-
{
777774
args = "@_CMDLINE";
778-
}
779775
else
780-
{
781776
error(Loc.initial, "command line length of %d is too long", len);
782-
}
783777
}
784778
}
785779
// Normalize executable path separators
@@ -897,6 +891,7 @@ public int runProgram()
897891
argv.push(a);
898892
}
899893
argv.push(null);
894+
RestorableEnv.restore();
900895
version (Windows)
901896
{
902897
const(char)[] ex = FileName.name(global.params.exefile);
@@ -1057,12 +1052,15 @@ version (Windows)
10571052
const pathlen = strlen(path);
10581053
const addpathlen = strlen(addpath);
10591054

1060-
char* npath = cast(char*)mem.xmalloc(5 + pathlen + 1 + addpathlen + 1);
1055+
const length = 5 + addpathlen + 1 + pathlen;
1056+
char* npath = cast(char*)mem.xmalloc(length + 1);
10611057
memcpy(npath, "PATH=".ptr, 5);
10621058
memcpy(npath + 5, addpath, addpathlen);
10631059
npath[5 + addpathlen] = ';';
10641060
memcpy(npath + 5 + addpathlen + 1, path, pathlen + 1);
1065-
putenv(npath);
1061+
if (LocalEnvVar(cast(string)npath[0 .. length], 4).putenvRestorable != 0)
1062+
assert(0, "putenvRestorable failed");
1063+
mem.xfree(npath); // on windows putenv makes a copy so we can free
10661064
}
10671065
return cmdbuf.extractChars();
10681066
}

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

0 commit comments

Comments
 (0)