Commit 8ab5313043

Andrew Kelley <superjoe30@gmail.com>
2017-10-15 07:23:10
implement environment variables for windows
1 parent 55e8bbd
std/os/windows/index.zig
@@ -32,12 +32,18 @@ pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) -> bool;
 
 pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) -> noreturn;
 
+pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: LPCH) -> BOOL;
+
 pub extern "kernel32" stdcallcc fn GetCommandLineA() -> LPSTR;
 
 pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: &DWORD) -> bool;
 
 pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) -> DWORD;
 
+pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() -> ?LPCH;
+
+pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) -> DWORD;
+
 pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: &DWORD) -> BOOL;
 
 pub extern "kernel32" stdcallcc fn GetLastError() -> DWORD;
@@ -49,11 +55,11 @@ pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(in_hFile: HANDLE
 pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpszFilePath: LPSTR,
   cchFilePath: DWORD, dwFlags: DWORD) -> DWORD;
 
-pub extern "kernel32" stdcallcc fn GetProcessHeap() -> HANDLE;
+pub extern "kernel32" stdcallcc fn GetProcessHeap() -> ?HANDLE;
 
 pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) -> ?HANDLE;
 
-pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID;
+pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> ?LPVOID;
 
 pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL;
 
@@ -91,6 +97,7 @@ pub const HCRYPTPROV = ULONG_PTR;
 pub const HINSTANCE = &@OpaqueType();
 pub const INT = c_int;
 pub const LPBYTE = &BYTE;
+pub const LPCH = &CHAR;
 pub const LPCSTR = &const CHAR;
 pub const LPCTSTR = &const TCHAR;
 pub const LPCVOID = &const c_void;
std/os/windows/util.zig
@@ -3,6 +3,7 @@ const os = std.os;
 const windows = std.os.windows;
 const assert = std.debug.assert;
 const mem = std.mem;
+const BufMap = std.BufMap;
 
 error WaitAbandoned;
 error WaitTimeOut;
@@ -111,3 +112,35 @@ pub fn windowsOpen(file_path: []const u8, desired_access: windows.DWORD, share_m
 
     return result;
 }
+
+/// Caller must free result.
+pub fn createWindowsEnvBlock(allocator: &mem.Allocator, env_map: &const BufMap) -> %[]u8 {
+    // count bytes needed
+    const bytes_needed = {
+        var bytes_needed: usize = 1; // 1 for the final null byte
+        var it = env_map.iterator();
+        while (it.next()) |pair| {
+            // +1 for '='
+            // +1 for null byte
+            bytes_needed += pair.key.len + pair.value.len + 2;
+        }
+        bytes_needed
+    };
+    const result = %return allocator.alloc(u8, bytes_needed);
+    %defer allocator.free(result);
+
+    var it = env_map.iterator();
+    var i: usize = 0;
+    while (it.next()) |pair| {
+        mem.copy(u8, result[i..], pair.key);
+        i += pair.key.len;
+        result[i] = '=';
+        i += 1;
+        mem.copy(u8, result[i..], pair.value);
+        i += pair.value.len;
+        result[i] = 0;
+        i += 1;
+    }
+    result[i] = 0;
+    return result;
+}
std/os/child_process.zig
@@ -564,7 +564,7 @@ pub const ChildProcess = struct {
         const cwd_ptr = if (cwd_slice) |cwd| cwd.ptr else null;
 
         const maybe_envp_buf = if (self.env_map) |env_map| {
-            %return os.createNullDelimitedEnvMap(self.allocator, env_map)
+            %return os.createWindowsEnvBlock(self.allocator, env_map)
         } else {
             null
         };
@@ -578,6 +578,7 @@ pub const ChildProcess = struct {
             const err = windows.GetLastError();
             return switch (err) {
                 windows.ERROR.FILE_NOT_FOUND => error.FileNotFound,
+                windows.ERROR.INVALID_PARAMETER => unreachable,
                 else => error.Unexpected,
             };
         }
std/os/index.zig
@@ -32,6 +32,7 @@ pub const windowsWrite = windows_util.windowsWrite;
 pub const windowsIsTty = windows_util.windowsIsTty;
 pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty;
 pub const windowsOpen = windows_util.windowsOpen;
+pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;
 
 const debug = @import("../debug.zig");
 const assert = debug.assert;
@@ -48,6 +49,7 @@ const io = @import("../io.zig");
 const base64 = @import("../base64.zig");
 const ArrayList = @import("../array_list.zig").ArrayList;
 const Buffer = @import("../buffer.zig").Buffer;
+const math = @import("../index.zig").math;
 
 error Unexpected;
 error SystemResources;
@@ -362,7 +364,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,
         return posixExecveErrnoToErr(posix.getErrno(posix.execve(??argv_buf[0], argv_buf.ptr, envp_buf.ptr)));
     }
 
-    const PATH = getEnv("PATH") ?? "/usr/local/bin:/bin/:/usr/bin";
+    const PATH = getEnvPosix("PATH") ?? "/usr/local/bin:/bin/:/usr/bin";
     // PATH.len because it is >= the largest search_path
     // +1 for the / to join the search path and exe_path
     // +1 for the null terminating byte
@@ -406,29 +408,55 @@ fn posixExecveErrnoToErr(err: usize) -> error {
     };
 }
 
-pub var environ_raw: []&u8 = undefined;
+pub var posix_environ_raw: []&u8 = undefined;
 
 /// Caller must free result when done.
 pub fn getEnvMap(allocator: &Allocator) -> %BufMap {
     var result = BufMap.init(allocator);
     %defer result.deinit();
 
-    for (environ_raw) |ptr| {
-        var line_i: usize = 0;
-        while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
-        const key = ptr[0..line_i];
+    if (is_windows) {
+        const ptr = windows.GetEnvironmentStringsA() ?? return error.OutOfMemory;
+        defer assert(windows.FreeEnvironmentStringsA(ptr));
 
-        var end_i: usize = line_i;
-        while (ptr[end_i] != 0) : (end_i += 1) {}
-        const value = ptr[line_i + 1..end_i];
+        var i: usize = 0;
+        while (true) {
+            if (ptr[i] == 0)
+                return result;
+
+            const key_start = i;
+
+            while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {}
+            const key = ptr[key_start..i];
 
-        %return result.set(key, value);
+            if (ptr[i] == '=') i += 1;
+
+            const value_start = i;
+            while (ptr[i] != 0) : (i += 1) {}
+            const value = ptr[value_start..i];
+
+            i += 1; // skip over null byte
+
+            %return result.set(key, value);
+        }
+    } else {
+        for (posix_environ_raw) |ptr| {
+            var line_i: usize = 0;
+            while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
+            const key = ptr[0..line_i];
+
+            var end_i: usize = line_i;
+            while (ptr[end_i] != 0) : (end_i += 1) {}
+            const value = ptr[line_i + 1..end_i];
+
+            %return result.set(key, value);
+        }
+        return result;
     }
-    return result;
 }
 
-pub fn getEnv(key: []const u8) -> ?[]const u8 {
-    for (environ_raw) |ptr| {
+pub fn getEnvPosix(key: []const u8) -> ?[]const u8 {
+    for (posix_environ_raw) |ptr| {
         var line_i: usize = 0;
         while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
         const this_key = ptr[0..line_i];
@@ -444,6 +472,42 @@ pub fn getEnv(key: []const u8) -> ?[]const u8 {
     return null;
 }
 
+error EnvironmentVariableNotFound;
+
+/// Caller must free returned memory.
+pub fn getEnvVarOwned(allocator: &mem.Allocator, key: []const u8) -> %[]u8 {
+    if (is_windows) {
+        const key_with_null = %return cstr.addNullByte(allocator, key);
+        defer allocator.free(key_with_null);
+
+        var buf = %return allocator.alloc(u8, 256);
+        %defer allocator.free(buf);
+
+        while (true) {
+            const windows_buf_len = %return math.cast(windows.DWORD, buf.len);
+            const result = windows.GetEnvironmentVariableA(key_with_null.ptr, buf.ptr, windows_buf_len);
+
+            if (result == 0) {
+                const err = windows.GetLastError();
+                return switch (err) {
+                    windows.ERROR.ENVVAR_NOT_FOUND => error.EnvironmentVariableNotFound,
+                    else => error.Unexpected,
+                };
+            }
+
+            if (result > buf.len) {
+                buf = %return allocator.realloc(u8, buf, result);
+                continue;
+            }
+
+            return allocator.shrink(u8, buf, result);
+        }
+    } else {
+        const result = getEnvPosix(key) ?? return error.EnvironmentVariableNotFound;
+        return mem.dupe(u8, allocator, result);
+    }
+}
+
 /// Caller must free the returned memory.
 pub fn getCwd(allocator: &Allocator) -> %[]u8 {
     switch (builtin.os) {
std/special/bootstrap.zig
@@ -56,7 +56,7 @@ fn callMain(argc: usize, argv: &&u8, envp: &?&u8) -> %void {
 
     var env_count: usize = 0;
     while (envp[env_count] != null) : (env_count += 1) {}
-    std.os.environ_raw = @ptrCast(&&u8, envp)[0..env_count];
+    std.os.posix_environ_raw = @ptrCast(&&u8, envp)[0..env_count];
 
     std.debug.user_main_fn = root.main;
 
std/build.zig
@@ -308,7 +308,7 @@ pub const Builder = struct {
     }
 
     fn processNixOSEnvVars(self: &Builder) {
-        if (os.getEnv("NIX_CFLAGS_COMPILE")) |nix_cflags_compile| {
+        if (os.getEnvVarOwned(self.allocator, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| {
             var it = mem.split(nix_cflags_compile, " ");
             while (true) {
                 const word = it.next() ?? break;
@@ -323,8 +323,10 @@ pub const Builder = struct {
                     break;
                 }
             }
+        } else |err| {
+            assert(err == error.EnvironmentVariableNotFound);
         }
-        if (os.getEnv("NIX_LDFLAGS")) |nix_ldflags| {
+        if (os.getEnvVarOwned(self.allocator, "NIX_LDFLAGS")) |nix_ldflags| {
             var it = mem.split(nix_ldflags, " ");
             while (true) {
                 const word = it.next() ?? break;
@@ -342,6 +344,8 @@ pub const Builder = struct {
                     break;
                 }
             }
+        } else |err| {
+            assert(err == error.EnvironmentVariableNotFound);
         }
     }
 
@@ -1248,7 +1252,13 @@ pub const LibExeObjStep = struct {
     }
 
     fn makeC(self: &LibExeObjStep) -> %void {
-        const cc = os.getEnv("CC") ?? "cc";
+        const cc = os.getEnvVarOwned(self.builder.allocator, "CC") %% |err| {
+            if (err == error.EnvironmentVariableNotFound) {
+                ([]const u8)("cc")
+            } else {
+                return err
+            }
+        };
         const builder = self.builder;
 
         assert(!self.is_zig);