Skip to content

Commit

Permalink
Maintain same environment for dmd -run
Browse files Browse the repository at this point in the history
Fix issue 18729
  • Loading branch information
marler8997 committed Jul 16, 2019
1 parent 0858136 commit 36abee0
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 23 deletions.
14 changes: 3 additions & 11 deletions src/dmd/dinifile.d
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import core.sys.posix.stdlib;
import core.sys.windows.winbase;
import core.sys.windows.windef;

import dmd.env;
import dmd.errors;
import dmd.globals;
import dmd.root.filename;
Expand All @@ -28,7 +29,6 @@ import dmd.root.stringtable;
import dmd.root.rmem : xarraydup;
import dmd.utils;

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

/*****************************
Expand Down Expand Up @@ -145,19 +145,11 @@ void updateRealEnvironment(ref StringTable environment)
static int envput(const(StringValue)* sv) nothrow
{
const name = sv.toDchars();
const namelen = strlen(name);
const value = cast(const(char)*)sv.ptrvalue;
if (!value) // deleted?
return 0;
const valuelen = strlen(value);
auto s = cast(char*)malloc(namelen + 1 + valuelen + 1);
assert(s);
memcpy(s, name, namelen);
s[namelen] = '=';
memcpy(s + namelen + 1, value, valuelen);
s[namelen + 1 + valuelen] = 0;
//printf("envput('%s')\n", s);
putenv(s);
if (0 != putenvRestorable(name.toDString, value.toDString))
assert(0, "putenvRestorable failed");
return 0; // do all of them
}

Expand Down
113 changes: 113 additions & 0 deletions src/dmd/env.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
module dmd.env;

import core.stdc.string;
import core.sys.posix.stdlib;
import dmd.globals;
import dmd.root.array;
import dmd.root.rmem;
import dmd.utils;

version (Windows)
private extern (C) int putenv(const char*) nothrow;

/**
Construct a variable from `name` and `value` and put it in the environment while saving
the current value of the environment variable.
Returns:
0 on success, non-zero on failure
*/
int putenvRestorable(const(char)[] name, const(char)[] value) nothrow
{
auto var = LocalEnvVar.xmalloc(name, value);
const result = var.putenvRestorable();
var.xfree(result);
return result;
}

/// Holds a `VAR=value` string that can be put into the global environment.
struct LocalEnvVar
{
string nameValueCStr; // The argument passed to `putenv`.
private size_t equalsIndex; // index of '=' in nameValueCStr.

/// The name of the variable
auto name() const { return nameValueCStr[0 .. equalsIndex]; }

/// The value of the variable
auto value() const { return nameValueCStr[equalsIndex + 1 .. $]; }

/**
Put this variable in the environment while saving the current value of the
environment variable.
Returns:
0 on success, non-zero on failure
*/
int putenvRestorable() const nothrow
{
RestorableEnv.save(name);
return .putenv(cast(char*)nameValueCStr.ptr);
}

/**
Allocate a new variable via xmalloc that can be promoted to the global environment.
Params:
name = name of the variable
value = value of the variable
Returns:
a newly allocated variable that can be promoted to the global environment
*/
static LocalEnvVar xmalloc(const(char)[] name, const(char)[] value) nothrow
{
const length = name.length + 1 + value.length;
auto str = (cast(char*)mem.xmalloc(length + 1))[0 .. length];
str[0 .. name.length] = name[];
str[name.length] = '=';
str[name.length + 1 .. length] = value[];
str.ptr[length] = '\0';
return LocalEnvVar(cast(string)str, name.length);
}

private void xfree(int putenvResult) const nothrow
{
bool doFree;
version (Windows)
doFree = true;
else
{
// on posix, when putenv succeeds ownership of the memory is transferred
// to the global environment
doFree = (putenvResult != 0);
}
if (doFree)
mem.xfree(cast(void*)nameValueCStr.ptr);
}
}

/// Provides save/restore functionality for environment variables.
struct RestorableEnv
{
/// Holds the original values of environment variables when they are overwritten.
private __gshared Array!LocalEnvVar originalVars;

/// Restore the original environment.
static void restore()
{
foreach (var; originalVars)
{
if (0 != putenv(cast(char*)var.nameValueCStr))
assert(0, "putenv failed");
}
}

/// Save the environment variable `name` if not saved already.
static void save(const(char)[] name) nothrow
{
foreach (var; originalVars)
{
if (name == var.name)
return; // already saved
}
originalVars.push(LocalEnvVar.xmalloc(name,
name.toCStringThen!(n => getenv(n.ptr)).toDString));
}
}
18 changes: 8 additions & 10 deletions src/dmd/link.d
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import core.sys.posix.unistd;
import core.sys.windows.winbase;
import core.sys.windows.windef;
import core.sys.windows.winreg;
import dmd.env;
import dmd.errors;
import dmd.globals;
import dmd.root.file;
Expand All @@ -30,7 +31,6 @@ import dmd.root.rmem;
import dmd.utils;

version (Posix) extern (C) int pipe(int*);
version (Windows) extern (C) int putenv(const char*);
version (Windows) extern (C) int spawnlp(int, const char*, const char*, const char*, const char*);
version (Windows) extern (C) int spawnl(int, const char*, const char*, const char*, const char*);
version (Windows) extern (C) int spawnv(int, const char*, const char**);
Expand Down Expand Up @@ -765,17 +765,11 @@ version (Windows)
{
if ((len = strlen(args)) > 255)
{
char* q = cast(char*)alloca(8 + len + 1);
sprintf(q, "_CMDLINE=%s", args);
status = putenv(q);
status = putenvRestorable("_CMDLINE", args[0 .. len]);
if (status == 0)
{
args = "@_CMDLINE";
}
else
{
error(Loc.initial, "command line length of %d is too long", len);
}
}
}
// Normalize executable path separators
Expand Down Expand Up @@ -893,6 +887,7 @@ public int runProgram()
argv.push(a);
}
argv.push(null);
RestorableEnv.restore();
version (Windows)
{
const(char)[] ex = FileName.name(global.params.exefile);
Expand Down Expand Up @@ -1053,12 +1048,15 @@ version (Windows)
const pathlen = strlen(path);
const addpathlen = strlen(addpath);

char* npath = cast(char*)mem.xmalloc(5 + pathlen + 1 + addpathlen + 1);
const length = 5 + addpathlen + 1 + pathlen;
char* npath = cast(char*)mem.xmalloc(length + 1);
memcpy(npath, "PATH=".ptr, 5);
memcpy(npath + 5, addpath, addpathlen);
npath[5 + addpathlen] = ';';
memcpy(npath + 5 + addpathlen + 1, path, pathlen + 1);
putenv(npath);
if (LocalEnvVar(cast(string)npath[0 .. length], 4).putenvRestorable != 0)
assert(0, "putenvRestorable failed");
mem.xfree(npath); // on windows putenv makes a copy so we can free
}
return cmdbuf.extractChars();
}
Expand Down
2 changes: 1 addition & 1 deletion src/posix.mak
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ FRONT_SRCS=$(addsuffix .d, $(addprefix $D/,access aggregate aliasthis apply argt
arraytypes astcodegen ast_node attrib builtin canthrow cli clone compiler complex cond constfold \
cppmangle cppmanglewin ctfeexpr ctorflow dcast dclass declaration delegatize denum dimport \
dinifile dinterpret dmacro dmangle dmodule doc dscope dstruct dsymbol dsymbolsem \
dtemplate dversion escape expression expressionsem func \
dtemplate dversion env escape expression expressionsem func \
hdrgen id impcnvtab imphint init initsem inline inlinecost intrange \
json lambdacomp lib libelf libmach link mars mtype nogc nspace objc opover optimize parse permissivevisitor sapply templateparamsem \
sideeffect statement staticassert target typesem traits transitivevisitor parsetimevisitor visitor \
Expand Down
2 changes: 1 addition & 1 deletion src/win32.mak
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ FRONT_SRCS=$D/access.d $D/aggregate.d $D/aliasthis.d $D/apply.d $D/argtypes.d $D
$D/cond.d $D/constfold.d $D/cppmangle.d $D/cppmanglewin.d $D/ctfeexpr.d $D/ctorflow.d $D/dcast.d $D/dclass.d \
$D/declaration.d $D/delegatize.d $D/denum.d $D/dimport.d $D/dinifile.d $D/dinterpret.d \
$D/dmacro.d $D/dmangle.d $D/dmodule.d $D/doc.d $D/dscope.d $D/dstruct.d $D/dsymbol.d $D/dsymbolsem.d \
$D/lambdacomp.d $D/dtemplate.d $D/dversion.d $D/escape.d \
$D/lambdacomp.d $D/dtemplate.d $D/dversion.d $D/env.d $D/escape.d \
$D/expression.d $D/expressionsem.d $D/func.d $D/hdrgen.d $D/id.d $D/imphint.d \
$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 \
$D/mars.d $D/mtype.d $D/nogc.d $D/nspace.d $D/objc.d $D/opover.d $D/optimize.d $D/parse.d \
Expand Down
11 changes: 11 additions & 0 deletions test/dshell/extra-files/printenv.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import std.array, std.stdio, std.process, std.algorithm;
void main()
{
foreach (varPair; environment.toAA().byKeyValue.array.sort!"a.key < b.key")
{
if (varPair.key != "_")
{
writeln(varPair.key, "=", varPair.value);
}
}
}
23 changes: 23 additions & 0 deletions test/dshell/sameenv.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import dshell;
void main()
{
const envFromExe = shellExpand("$OUTPUT_BASE/envFromExe.txt");
const envFromRun = shellExpand("$OUTPUT_BASE/envFromRun.txt");

run("$DMD -m$MODEL -of$OUTPUT_BASE/printenv$EXE $EXTRA_FILES/printenv.d");
run("$OUTPUT_BASE/printenv$EXE", File(envFromExe, "wb"));
run("$DMD -m$MODEL -run $EXTRA_FILES/printenv.d", File(envFromRun, "wb"));

const fromExe = readText(envFromExe);
const fromRun = readText(envFromRun);
if (fromExe != fromRun)
{
writefln("FromExe:");
writeln("-----------");
writeln(fromExe);
writefln("FromRun:");
writeln("-----------");
writeln(fromRun);
assert(0, "output from exe/run differ");
}
}

0 comments on commit 36abee0

Please sign in to comment.