implement environment variables for windows

master
Andrew Kelley 2017-10-15 01:23:10 -04:00
parent 55e8bbd167
commit 8ab5313043
6 changed files with 135 additions and 20 deletions

View File

@ -308,7 +308,7 @@ pub const Builder = struct {
} }
fn processNixOSEnvVars(self: &Builder) { 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, " "); var it = mem.split(nix_cflags_compile, " ");
while (true) { while (true) {
const word = it.next() ?? break; const word = it.next() ?? break;
@ -323,8 +323,10 @@ pub const Builder = struct {
break; 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, " "); var it = mem.split(nix_ldflags, " ");
while (true) { while (true) {
const word = it.next() ?? break; const word = it.next() ?? break;
@ -342,6 +344,8 @@ pub const Builder = struct {
break; break;
} }
} }
} else |err| {
assert(err == error.EnvironmentVariableNotFound);
} }
} }
@ -1248,7 +1252,13 @@ pub const LibExeObjStep = struct {
} }
fn makeC(self: &LibExeObjStep) -> %void { 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; const builder = self.builder;
assert(!self.is_zig); assert(!self.is_zig);

View File

@ -564,7 +564,7 @@ pub const ChildProcess = struct {
const cwd_ptr = if (cwd_slice) |cwd| cwd.ptr else null; const cwd_ptr = if (cwd_slice) |cwd| cwd.ptr else null;
const maybe_envp_buf = if (self.env_map) |env_map| { 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 { } else {
null null
}; };
@ -578,6 +578,7 @@ pub const ChildProcess = struct {
const err = windows.GetLastError(); const err = windows.GetLastError();
return switch (err) { return switch (err) {
windows.ERROR.FILE_NOT_FOUND => error.FileNotFound, windows.ERROR.FILE_NOT_FOUND => error.FileNotFound,
windows.ERROR.INVALID_PARAMETER => unreachable,
else => error.Unexpected, else => error.Unexpected,
}; };
} }

View File

@ -32,6 +32,7 @@ pub const windowsWrite = windows_util.windowsWrite;
pub const windowsIsTty = windows_util.windowsIsTty; pub const windowsIsTty = windows_util.windowsIsTty;
pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty; pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty;
pub const windowsOpen = windows_util.windowsOpen; pub const windowsOpen = windows_util.windowsOpen;
pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;
const debug = @import("../debug.zig"); const debug = @import("../debug.zig");
const assert = debug.assert; const assert = debug.assert;
@ -48,6 +49,7 @@ const io = @import("../io.zig");
const base64 = @import("../base64.zig"); const base64 = @import("../base64.zig");
const ArrayList = @import("../array_list.zig").ArrayList; const ArrayList = @import("../array_list.zig").ArrayList;
const Buffer = @import("../buffer.zig").Buffer; const Buffer = @import("../buffer.zig").Buffer;
const math = @import("../index.zig").math;
error Unexpected; error Unexpected;
error SystemResources; 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))); 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 // PATH.len because it is >= the largest search_path
// +1 for the / to join the search path and exe_path // +1 for the / to join the search path and exe_path
// +1 for the null terminating byte // +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. /// Caller must free result when done.
pub fn getEnvMap(allocator: &Allocator) -> %BufMap { pub fn getEnvMap(allocator: &Allocator) -> %BufMap {
var result = BufMap.init(allocator); var result = BufMap.init(allocator);
%defer result.deinit(); %defer result.deinit();
for (environ_raw) |ptr| { if (is_windows) {
var line_i: usize = 0; const ptr = windows.GetEnvironmentStringsA() ?? return error.OutOfMemory;
while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} defer assert(windows.FreeEnvironmentStringsA(ptr));
const key = ptr[0..line_i];
var end_i: usize = line_i; var i: usize = 0;
while (ptr[end_i] != 0) : (end_i += 1) {} while (true) {
const value = ptr[line_i + 1..end_i]; if (ptr[i] == 0)
return result;
%return result.set(key, value); const key_start = i;
while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {}
const key = ptr[key_start..i];
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 { pub fn getEnvPosix(key: []const u8) -> ?[]const u8 {
for (environ_raw) |ptr| { for (posix_environ_raw) |ptr| {
var line_i: usize = 0; var line_i: usize = 0;
while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
const this_key = ptr[0..line_i]; const this_key = ptr[0..line_i];
@ -444,6 +472,42 @@ pub fn getEnv(key: []const u8) -> ?[]const u8 {
return null; 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. /// Caller must free the returned memory.
pub fn getCwd(allocator: &Allocator) -> %[]u8 { pub fn getCwd(allocator: &Allocator) -> %[]u8 {
switch (builtin.os) { switch (builtin.os) {

View File

@ -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 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 GetCommandLineA() -> LPSTR;
pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: &DWORD) -> bool; 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 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 GetExitCodeProcess(hProcess: HANDLE, lpExitCode: &DWORD) -> BOOL;
pub extern "kernel32" stdcallcc fn GetLastError() -> DWORD; 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, pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpszFilePath: LPSTR,
cchFilePath: DWORD, dwFlags: DWORD) -> DWORD; 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 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; 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 HINSTANCE = &@OpaqueType();
pub const INT = c_int; pub const INT = c_int;
pub const LPBYTE = &BYTE; pub const LPBYTE = &BYTE;
pub const LPCH = &CHAR;
pub const LPCSTR = &const CHAR; pub const LPCSTR = &const CHAR;
pub const LPCTSTR = &const TCHAR; pub const LPCTSTR = &const TCHAR;
pub const LPCVOID = &const c_void; pub const LPCVOID = &const c_void;

View File

@ -3,6 +3,7 @@ const os = std.os;
const windows = std.os.windows; const windows = std.os.windows;
const assert = std.debug.assert; const assert = std.debug.assert;
const mem = std.mem; const mem = std.mem;
const BufMap = std.BufMap;
error WaitAbandoned; error WaitAbandoned;
error WaitTimeOut; error WaitTimeOut;
@ -111,3 +112,35 @@ pub fn windowsOpen(file_path: []const u8, desired_access: windows.DWORD, share_m
return result; 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;
}

View File

@ -56,7 +56,7 @@ fn callMain(argc: usize, argv: &&u8, envp: &?&u8) -> %void {
var env_count: usize = 0; var env_count: usize = 0;
while (envp[env_count] != null) : (env_count += 1) {} 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.debug.user_main_fn = root.main;