diff --git a/src/macro/eval/evalLuv.ml b/src/macro/eval/evalLuv.ml index 3ad36b44f82..21c117832c6 100644 --- a/src/macro/eval/evalLuv.ml +++ b/src/macro/eval/evalLuv.ml @@ -2141,6 +2141,10 @@ let env_fields = [ and value = decode_native_string v2 in encode_unit_result (Env.setenv name ~value) ); + "unsetEnv", vfun1 (fun v -> + let name = decode_string v in + encode_unit_result (Env.unsetenv name) + ); "environ", vfun0 (fun() -> let encode env = let map = diff --git a/src/macro/eval/evalStdLib.ml b/src/macro/eval/evalStdLib.ml index 53f38d44065..1a08b936e29 100644 --- a/src/macro/eval/evalStdLib.ml +++ b/src/macro/eval/evalStdLib.ml @@ -2637,11 +2637,14 @@ module StdSys = struct | _ -> vnull ) - let putEnv = vfun2 (fun s v -> - let s = decode_string s in - let v = decode_string v in - catch_unix_error Unix.putenv s v; - vnull + let putEnv = vfun2 (fun s -> function + | v when v = vnull -> + let _ = Luv.Env.unsetenv (decode_string s) in vnull + | v -> + let s = decode_string s in + let v = decode_string v in + catch_unix_error Unix.putenv s v; + vnull ) let setCwd = vfun1 (fun s -> diff --git a/std/Sys.hx b/std/Sys.hx index fe378142113..389493908a9 100644 --- a/std/Sys.hx +++ b/std/Sys.hx @@ -57,9 +57,11 @@ extern class Sys { /** Sets the value of the given environment variable. + If `v` is `null`, the environment variable is removed. + (java) This functionality is not available on Java; calling this function will throw. **/ - static function putEnv(s:String, v:String):Void; + static function putEnv(s:String, v:Null):Void; /** Returns all environment variables. diff --git a/std/cpp/NativeSys.hx b/std/cpp/NativeSys.hx index cd5851ec062..5390568d0b3 100644 --- a/std/cpp/NativeSys.hx +++ b/std/cpp/NativeSys.hx @@ -34,7 +34,7 @@ extern class NativeSys { extern static function get_env(v:String):String; @:native("_hx_std_put_env") - extern static function put_env(e:String, v:String):Void; + extern static function put_env(e:String, v:Null):Void; @:native("_hx_std_sys_sleep") extern static function sys_sleep(f:Float):Void; diff --git a/std/cpp/_std/Sys.hx b/std/cpp/_std/Sys.hx index 0d2e77ab2c1..3ee99b5e7b9 100644 --- a/std/cpp/_std/Sys.hx +++ b/std/cpp/_std/Sys.hx @@ -63,7 +63,7 @@ import haxe.SysTools; return v; } - public static function putEnv(s:String, v:String):Void { + public static function putEnv(s:String, v:Null):Void { NativeSys.put_env(s, v); } diff --git a/std/cs/_std/Sys.hx b/std/cs/_std/Sys.hx index 7cf8d55cf0d..a9bd470650f 100644 --- a/std/cs/_std/Sys.hx +++ b/std/cs/_std/Sys.hx @@ -50,9 +50,14 @@ class Sys { return Environment.GetEnvironmentVariable(s); } - public static function putEnv(s:String, v:String):Void { + public static function putEnv(s:String, v:Null):Void { Environment.SetEnvironmentVariable(s, v); - if (_env != null) + if (_env == null) + return; + + if (v == null) + _env.remove(s); + else _env.set(s, v); } diff --git a/std/eval/_std/Sys.hx b/std/eval/_std/Sys.hx index 30561e3dea5..28e2075b162 100644 --- a/std/eval/_std/Sys.hx +++ b/std/eval/_std/Sys.hx @@ -33,7 +33,7 @@ class Sys { extern static public function getEnv(s:String):String; - extern static public function putEnv(s:String, v:String):Void; + extern static public function putEnv(s:String, v:Null):Void; extern static public function environment():Map; diff --git a/std/eval/luv/Env.hx b/std/eval/luv/Env.hx index 5e86261ab57..fae97f72e3b 100644 --- a/std/eval/luv/Env.hx +++ b/std/eval/luv/Env.hx @@ -16,6 +16,11 @@ extern class Env { **/ static function setEnv(name:String, value:NativeString):Result; + /** + Deletes an environment variable. + **/ + static function unsetEnv(name:String):Result; + /** Retrieves all environment variables. **/ diff --git a/std/hl/_std/Sys.hx b/std/hl/_std/Sys.hx index feae9d03e7c..29d5e095098 100644 --- a/std/hl/_std/Sys.hx +++ b/std/hl/_std/Sys.hx @@ -86,7 +86,7 @@ class Sys { return makePath(v); } - public static function putEnv(s:String, v:String):Void { + public static function putEnv(s:String, v:Null):Void { if (!put_env(getPath(s), if (v == null) null else getPath(v))) throw "putEnv() failure"; } diff --git a/std/java/_std/Sys.hx b/std/java/_std/Sys.hx index 75b2a87ebc7..24b5ee2bd13 100644 --- a/std/java/_std/Sys.hx +++ b/std/java/_std/Sys.hx @@ -48,7 +48,7 @@ using haxe.Int64; return java.lang.System.getenv(s); } - public static function putEnv(s:String, v:String):Void { + public static function putEnv(s:String, v:Null):Void { // java offers no support for it (!) throw new haxe.exceptions.NotImplementedException("Not implemented in this platform"); } diff --git a/std/lua/_std/Sys.hx b/std/lua/_std/Sys.hx index d788dda8d54..4411a9152ec 100644 --- a/std/lua/_std/Sys.hx +++ b/std/lua/_std/Sys.hx @@ -98,7 +98,9 @@ class Sys { return Os.getenv(s); } - public inline static function putEnv(s:String, v:String):Void { + public inline static function putEnv(s:String, v:Null):Void { + if (v == null) + return Os.unsetenv(s); Os.setenv(s, v); } diff --git a/std/neko/_std/Sys.hx b/std/neko/_std/Sys.hx index ed519417bc3..c5d6be10acd 100644 --- a/std/neko/_std/Sys.hx +++ b/std/neko/_std/Sys.hx @@ -69,7 +69,7 @@ import haxe.SysTools; return new String(v); } - public static function putEnv( s : String, v : String ) : Void { + public static function putEnv( s : String, v : Null ) : Void { untyped put_env(s.__s,if( v == null ) null else v.__s); } diff --git a/std/php/_std/Sys.hx b/std/php/_std/Sys.hx index 5de11a48b0c..4afda7b0094 100644 --- a/std/php/_std/Sys.hx +++ b/std/php/_std/Sys.hx @@ -50,9 +50,14 @@ import haxe.SysTools; return value == false ? null : value; } - public static function putEnv(s:String, v:String):Void { - customEnvVars[s] = '$v'; // in case of `null` it should become `"null"` - Global.putenv('$s=$v'); + public static function putEnv(s:String, v:Null):Void { + if (v == null) { + Global.unset(customEnvVars[s]); + Global.putenv('$s'); + } else { + customEnvVars[s] = '$v'; // in case of `null` it should become `"null"` + Global.putenv('$s=$v'); + } } public static inline function sleep(seconds:Float):Void { diff --git a/std/python/_std/Sys.hx b/std/python/_std/Sys.hx index 2da89ec86c3..a194b408625 100644 --- a/std/python/_std/Sys.hx +++ b/std/python/_std/Sys.hx @@ -67,7 +67,11 @@ class Sys { return environ.get(s); } - public static function putEnv(s:String, v:String):Void { + public static function putEnv(s:String, v:Null):Void { + if (v == null) { + environ.remove(s); + return; + } python.lib.Os.putenv(s, v); environ.set(s, v); } diff --git a/tests/runci/System.hx b/tests/runci/System.hx index 0a15f500032..aee87b3fb42 100644 --- a/tests/runci/System.hx +++ b/tests/runci/System.hx @@ -83,6 +83,22 @@ class System { fail(); } + static function showAndRunCommand(cmd:String, args:Null>, displayed:String):Int { + infoMsg('Command: $displayed'); + + final t = Timer.stamp(); + final exitCode = Sys.command(cmd, args); + final dt = Math.round(Timer.stamp() - t); + + final msg = 'Command exited with $exitCode in ${dt}s: $displayed'; + if (exitCode != 0) + failMsg(msg); + else + successMsg(msg); + + return exitCode; + } + /** * Recursively delete a directory. * @return Int Exit code of a system command executed to perform deletion. @@ -172,4 +188,32 @@ class System { Sys.println('Changing directory to $path'); Sys.setCwd(path); } -} \ No newline at end of file + + static function mergeArgs(cmd:String, args:Array) { + return switch (Sys.systemName()) { + case "Windows": + [StringTools.replace(cmd, "/", "\\")].concat(args).map(haxe.SysTools.quoteWinArg.bind(_, true)).join(" "); + case _: + [cmd].concat(args).map(haxe.SysTools.quoteUnixArg).join(" "); + } + } + + /* command for setting the environment variable to set before sys tests */ + static final setCommand = if (Sys.systemName() == "Windows") "set"; else "export"; + static final nameAndValue = "EXISTS=1"; + + /** Prepares environment for system tests and runs `cmd` with `args` **/ + static public function runSysTest(cmd:String, ?args:Array) { + final cmdStr = '$setCommand [$nameAndValue] && ' + cmd + (args == null ? '' : ' $args'); + + if (args != null) + cmd = mergeArgs(cmd, args); + + final fullCmd = '$setCommand $nameAndValue && $cmd'; + + final exitCode = showAndRunCommand(fullCmd, null, cmdStr); + + if (exitCode != 0) + fail(); + } +} diff --git a/tests/runci/targets/Cpp.hx b/tests/runci/targets/Cpp.hx index 4c87ce816f1..e38d7edb021 100644 --- a/tests/runci/targets/Cpp.hx +++ b/tests/runci/targets/Cpp.hx @@ -60,7 +60,7 @@ class Cpp { changeDirectory(sysDir); runCommand("haxe", ["compile-cpp.hxml"].concat(args)); - runCpp("bin/cpp/Main-debug", []); + runSysTest(FileSystem.fullPath("bin/cpp/Main-debug")); changeDirectory(threadsDir); runCommand("haxe", ["build.hxml", "-cpp", "export/cpp"]); diff --git a/tests/runci/targets/Cs.hx b/tests/runci/targets/Cs.hx index 0060dc05eb4..d3ef982119f 100644 --- a/tests/runci/targets/Cs.hx +++ b/tests/runci/targets/Cs.hx @@ -33,7 +33,7 @@ class Cs { if (args == null) args = []; exe = FileSystem.fullPath(exe); switch (systemName) { - case "Linux", "Mac": + case "Linux" | "Mac": runCommand("mono", [exe].concat(args)); case "Windows": runCommand(exe, args); @@ -60,7 +60,13 @@ class Cs { changeDirectory(sysDir); runCommand("haxe", ["compile-cs.hxml",'-D','fast_cast'].concat(args)); - runCs("bin/cs/bin/Main-Debug.exe", []); + final exe = FileSystem.fullPath("bin/cs/bin/Main-Debug.exe"); + switch (systemName) { + case "Linux" | "Mac": + runSysTest("mono", [exe]); + case "Windows": + runSysTest(exe); + } changeDirectory(threadsDir); runCommand("haxe", ["build.hxml", "-cs", "export/cs"]); @@ -76,4 +82,4 @@ class Cs { runCs("bin/main/bin/Main.exe"); } } -} \ No newline at end of file +} diff --git a/tests/runci/targets/Hl.hx b/tests/runci/targets/Hl.hx index 09b2720047f..471be8188f7 100644 --- a/tests/runci/targets/Hl.hx +++ b/tests/runci/targets/Hl.hx @@ -83,7 +83,7 @@ class Hl { changeDirectory(sysDir); runCommand("haxe", ["compile-hl.hxml"].concat(args)); - runCommand(hlBinary, ["bin/hl/sys.hl"]); + runSysTest(hlBinary, ["bin/hl/sys.hl"]); changeDirectory(miscHlDir); runCommand("haxe", ["run.hxml"]); diff --git a/tests/runci/targets/Java.hx b/tests/runci/targets/Java.hx index 3de769115d9..c09435a7ccb 100644 --- a/tests/runci/targets/Java.hx +++ b/tests/runci/targets/Java.hx @@ -29,7 +29,7 @@ class Java { changeDirectory(sysDir); runCommand("haxe", ["compile-java.hxml"].concat(args)); - runCommand("java", ["-jar", "bin/java/Main-Debug.jar"]); + runSysTest("java", ["-jar", "bin/java/Main-Debug.jar"]); changeDirectory(threadsDir); runCommand("haxe", ["build.hxml", "-java", "export/java"].concat(args)); @@ -52,4 +52,4 @@ class Java { } } } -} \ No newline at end of file +} diff --git a/tests/runci/targets/Js.hx b/tests/runci/targets/Js.hx index bf5fcfb9843..3bd315dd057 100644 --- a/tests/runci/targets/Js.hx +++ b/tests/runci/targets/Js.hx @@ -113,7 +113,7 @@ class Js { changeDirectory(sysDir); runCommand("npm", ["install", "deasync"], true); runCommand("haxe", ["compile-js.hxml"].concat(args)); - runCommand("node", ["bin/js/sys.js"]); + runSysTest("node", ["bin/js/sys.js"]); changeDirectory(miscJsDir); runCommand("haxe", ["run.hxml"]); diff --git a/tests/runci/targets/Jvm.hx b/tests/runci/targets/Jvm.hx index 50b8c10959b..075f610a1a7 100644 --- a/tests/runci/targets/Jvm.hx +++ b/tests/runci/targets/Jvm.hx @@ -16,10 +16,10 @@ class Jvm { changeDirectory(sysDir); runCommand("haxe", ["compile-jvm.hxml"].concat(args)); - runCommand("java", ["-jar", "bin/jvm/sys.jar"]); + runSysTest("java", ["-jar", "bin/jvm/sys.jar"]); changeDirectory(threadsDir); runCommand("haxe", ["build.hxml", "--jvm", "export/threads.jar"].concat(args)); runCommand("java", ["-jar", "export/threads.jar"]); } -} \ No newline at end of file +} diff --git a/tests/runci/targets/Lua.hx b/tests/runci/targets/Lua.hx index a630ad924de..c3414925636 100644 --- a/tests/runci/targets/Lua.hx +++ b/tests/runci/targets/Lua.hx @@ -77,7 +77,7 @@ class Lua { changeDirectory(sysDir); runCommand("haxe", ["compile-lua.hxml"].concat(args)); - runCommand("lua", ["bin/lua/sys.lua"]); + runSysTest("lua", ["bin/lua/sys.lua"]); changeDirectory(miscDir + "luaDeadCode/stringReflection"); runCommand("haxe", ["compile.hxml"]); diff --git a/tests/runci/targets/Macro.hx b/tests/runci/targets/Macro.hx index f2d5ae787f6..3b23fd0c7ea 100644 --- a/tests/runci/targets/Macro.hx +++ b/tests/runci/targets/Macro.hx @@ -28,7 +28,7 @@ class Macro { runCommand("haxe", ["run.hxml"]); changeDirectory(sysDir); - runCommand("haxe", ["compile-macro.hxml"].concat(args)); + runSysTest("haxe", ["compile-macro.hxml"].concat(args)); switch Sys.systemName() { case 'Linux': @@ -40,4 +40,4 @@ class Macro { changeDirectory(threadsDir); runCommand("haxe", ["build.hxml", "--interp"]); } -} \ No newline at end of file +} diff --git a/tests/runci/targets/Neko.hx b/tests/runci/targets/Neko.hx index 8c8551056b3..a59f955cf95 100644 --- a/tests/runci/targets/Neko.hx +++ b/tests/runci/targets/Neko.hx @@ -11,10 +11,10 @@ class Neko { changeDirectory(sysDir); runCommand("haxe", ["compile-neko.hxml"].concat(args)); - runCommand("neko", ["bin/neko/sys.n"]); + runSysTest("neko", ["bin/neko/sys.n"]); changeDirectory(threadsDir); runCommand("haxe", ["build.hxml", "--neko", "export/threads.n"]); runCommand("neko", ["export/threads.n"]); } -} \ No newline at end of file +} diff --git a/tests/runci/targets/Php.hx b/tests/runci/targets/Php.hx index 19b1368db7d..2b1c16df546 100644 --- a/tests/runci/targets/Php.hx +++ b/tests/runci/targets/Php.hx @@ -25,7 +25,7 @@ class Php { } case _: } - infoMsg('php ${phpVer} has already been installed.'); + infoMsg('php $phpVer has already been installed.'); return; } switch systemName { @@ -57,19 +57,18 @@ class Php { for(prefix in prefixes) { changeDirectory(unitDir); - if(isCi()) { + if(isCi()) deleteDirectoryRecursively(binDir); - } runCommand("haxe", ["compile-php.hxml"].concat(prefix).concat(args)); runThroughPhpVersions(runCommand.bind(_, [binDir + "/index.php"])); changeDirectory(sysDir); - if(isCi()) { + if(isCi()) deleteDirectoryRecursively(binDir); - } + runCommand("haxe", ["compile-php.hxml"].concat(prefix).concat(args)); - runThroughPhpVersions(runCommand.bind(_, ["bin/php/Main/index.php"])); + runThroughPhpVersions(runSysTest.bind(_, ["bin/php/Main/index.php"])); } } @@ -83,4 +82,4 @@ class Php { fn('php'); } } -} \ No newline at end of file +} diff --git a/tests/runci/targets/Python.hx b/tests/runci/targets/Python.hx index 71795d2b28c..f41c1660e43 100644 --- a/tests/runci/targets/Python.hx +++ b/tests/runci/targets/Python.hx @@ -68,7 +68,7 @@ class Python { changeDirectory(sysDir); runCommand("haxe", ["compile-python.hxml"].concat(args)); for (py in pys) { - runCommand(py, ["bin/python/sys.py"]); + runSysTest(py, ["bin/python/sys.py"]); } changeDirectory(miscPythonDir); @@ -86,4 +86,4 @@ class Python { runCommand(py, ["export/threads.py"]); } } -} \ No newline at end of file +} diff --git a/tests/sys/run.hxml b/tests/sys/run.hxml index 81eb8a09687..d7c5847063b 100644 --- a/tests/sys/run.hxml +++ b/tests/sys/run.hxml @@ -10,28 +10,24 @@ compile.hxml # Mac/Linux --next ---cmd echo Neko && neko bin/neko/sys.n ---cmd echo Python && python3 bin/python/sys.py ---cmd echo Cpp && bin/cpp/Main-debug ---cmd echo CS && mono bin/cs/bin/Main-Debug.exe ---cmd echo Java && java -jar bin/java/Main-Debug.jar ---cmd echo Php && php bin/php/Main/index.php ---cmd echo Hl && hl bin/hl/sys.hl ---cmd echo Js && node bin/js/sys.js +--cmd echo Neko && export EXISTS=1 && neko bin/neko/sys.n +--cmd echo Python && export EXISTS=1 && python3 bin/python/sys.py +--cmd echo Cpp && export EXISTS=1 && bin/cpp/Main-debug +--cmd echo CS && export EXISTS=1 && mono bin/cs/bin/Main-Debug.exe +--cmd echo Java && export EXISTS=1 && java -jar bin/java/Main-Debug.jar +--cmd echo Php && export EXISTS=1 && php bin/php/Main/index.php +--cmd echo Hl && export EXISTS=1 && hl bin/hl/sys.hl +--cmd echo Js && export EXISTS=1 && node bin/js/sys.js +--cmd echo Macro && export EXISTS=1 && haxe compile-macro.hxml # Windows # --next -# --cmd echo Neko && neko bin\neko\sys.n -# --cmd echo Python && python3 bin\python\sys.py -# --cmd echo Cpp && bin\cpp\Main-debug.exe -# --cmd echo CS && bin\cs\bin\Main-Debug.exe -# --cmd echo Java && java -jar bin\java\Main-Debug.jar -# --cmd echo Php && php bin\php\Main\index.php -# --cmd echo Hl && hl bin/hl/sys.hl -# --cmd echo Js && node bin/js/sys.js - -# Macro has to be placed at the end since it would exit the compilation process. ---next ---cmd echo Macro ---next -compile-macro.hxml +# --cmd echo Neko && set EXISTS=1 && neko bin\neko\sys.n +# --cmd echo Python && set EXISTS=1 && python3 bin\python\sys.py +# --cmd echo Cpp && set EXISTS=1 && bin\cpp\Main-debug.exe +# --cmd echo CS && set EXISTS=1 && bin\cs\bin\Main-Debug.exe +# --cmd echo Java && set EXISTS=1 && java -jar bin\java\Main-Debug.jar +# --cmd echo Php && set EXISTS=1 && php bin\php\Main\index.php +# --cmd echo Hl && set EXISTS=1 && hl bin/hl/sys.hl +# --cmd echo Js && set EXISTS=1 && node bin/js/sys.js +# --cmd echo Macro && set EXISTS=1 && haxe compile-macro.hxml diff --git a/tests/sys/src/Main.hx b/tests/sys/src/Main.hx index 5190fbd182d..1044eea0869 100644 --- a/tests/sys/src/Main.hx +++ b/tests/sys/src/Main.hx @@ -23,15 +23,9 @@ class Main { #end #end #if php - switch (Sys.systemName()) { - case "Windows": - // pass - case _: - runner.addCase(new net.TestSocket()); - } - #else - runner.addCase(new net.TestSocket()); + if (Sys.systemName() != "Windows") #end + runner.addCase(new net.TestSocket()); var report = Report.create(runner); report.displayHeader = AlwaysShowHeader; report.displaySuccessResults = NeverShowSuccessResults; diff --git a/tests/sys/src/TestSys.hx b/tests/sys/src/TestSys.hx index 9ad77b4cc42..1edb70af125 100644 --- a/tests/sys/src/TestSys.hx +++ b/tests/sys/src/TestSys.hx @@ -5,18 +5,38 @@ class TestSys extends TestCommandBase { return Sys.command(cmd, args); } - function testEnv() { - #if !(java) + function testEnvironment() { + var env = Sys.environment(); + // EXISTS should be set manually via the command line + Assert.notNull(env.get("EXISTS")); + Assert.isNull(env.get("doesn't exist")); + } + + function testGetEnv() { + // EXISTS should be set manually via the command line + Assert.notNull(Sys.getEnv("EXISTS")); + Assert.isNull(Sys.getEnv("doesn't exist")); + } + + #if !java + function testPutEnv() { Sys.putEnv("foo", "value"); Assert.equals("value", Sys.getEnv("foo")); - #end - Assert.equals(null, Sys.getEnv("doesn't exist")); - #if !(java) var env = Sys.environment(); Assert.equals("value", env.get("foo")); + + // null + Sys.putEnv("foo", null); + Assert.isNull(Sys.getEnv("foo")); + + #if !(python || cs) // #10401 + env = Sys.environment(); #end + + Assert.isFalse(env.exists("foo")); } + #end function testProgramPath() { var p = Sys.programPath();