| author | |
| committer | |
| log | cfffb9c5e96eeeae43cd724e2d02ec8c2b7714e0 |
| tree | 342ef6e97cd6f0a3a65839ab0b5e5326a9ea296d |
| parent | 936d0b18b116ea126893d2f165f9be72f8bef845 |
| signature |
std.os.getenv and std.os.getenvZ have nice compile errors when not linking
libc and using Windows.
std.os.getenvW is provided as a Windows-only API that does not require
an allocator. It uses the Process Environment Block.
std.process.getEnvVarOwned is improved to be a simple wrapper on top of
std.os.getenvW.
std.process.getEnvMap is improved to use the Process Environment Block
rather than calling GetEnvironmentVariableW.
std.zig.system.NativePaths uses process.getEnvVarOwned instead of
std.os.getenvZ, which works on Windows as well as POSIX.5 files changed, 98 insertions(+), 71 deletions(-)
lib/std/os.zig+34-8| ... | @@ -1104,10 +1104,6 @@ pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*:0]u8) | ... | @@ -1104,10 +1104,6 @@ pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*:0]u8) |
| 1104 | /// Get an environment variable. | 1104 | /// Get an environment variable. |
| 1105 | /// See also `getenvZ`. | 1105 | /// See also `getenvZ`. |
| 1106 | pub fn getenv(key: []const u8) ?[]const u8 { | 1106 | pub fn getenv(key: []const u8) ?[]const u8 { |
| 1107 | if (builtin.os == .windows) { | ||
| 1108 | // TODO update this to use the ProcessEnvironmentBlock | ||
| 1109 | @compileError("TODO implement std.os.getenv for Windows"); | ||
| 1110 | } | ||
| 1111 | if (builtin.link_libc) { | 1107 | if (builtin.link_libc) { |
| 1112 | var small_key_buf: [64]u8 = undefined; | 1108 | var small_key_buf: [64]u8 = undefined; |
| 1113 | if (key.len < small_key_buf.len) { | 1109 | if (key.len < small_key_buf.len) { |
| ... | @@ -1133,6 +1129,9 @@ pub fn getenv(key: []const u8) ?[]const u8 { | ... | @@ -1133,6 +1129,9 @@ pub fn getenv(key: []const u8) ?[]const u8 { |
| 1133 | } | 1129 | } |
| 1134 | return null; | 1130 | return null; |
| 1135 | } | 1131 | } |
| 1132 | if (builtin.os == .windows) { | ||
| 1133 | @compileError("std.os.getenv is unavailable for Windows because environment string is in WTF-16 format. See std.process.getEnvVarOwned for cross-platform API or std.os.getenvW for Windows-specific API."); | ||
| 1134 | } | ||
| 1136 | // TODO see https://github.com/ziglang/zig/issues/4524 | 1135 | // TODO see https://github.com/ziglang/zig/issues/4524 |
| 1137 | for (environ) |ptr| { | 1136 | for (environ) |ptr| { |
| 1138 | var line_i: usize = 0; | 1137 | var line_i: usize = 0; |
| ... | @@ -1155,17 +1154,44 @@ pub const getenvC = getenvZ; | ... | @@ -1155,17 +1154,44 @@ pub const getenvC = getenvZ; |
| 1155 | /// Get an environment variable with a null-terminated name. | 1154 | /// Get an environment variable with a null-terminated name. |
| 1156 | /// See also `getenv`. | 1155 | /// See also `getenv`. |
| 1157 | pub fn getenvZ(key: [*:0]const u8) ?[]const u8 { | 1156 | pub fn getenvZ(key: [*:0]const u8) ?[]const u8 { |
| 1158 | if (builtin.os == .windows) { | ||
| 1159 | // TODO update this to use the ProcessEnvironmentBlock | ||
| 1160 | @compileError("TODO implement std.os.getenv for Windows"); | ||
| 1161 | } | ||
| 1162 | if (builtin.link_libc) { | 1157 | if (builtin.link_libc) { |
| 1163 | const value = system.getenv(key) orelse return null; | 1158 | const value = system.getenv(key) orelse return null; |
| 1164 | return mem.toSliceConst(u8, value); | 1159 | return mem.toSliceConst(u8, value); |
| 1165 | } | 1160 | } |
| 1161 | if (builtin.os == .windows) { | ||
| 1162 | @compileError("std.os.getenvZ is unavailable for Windows because environment string is in WTF-16 format. See std.process.getEnvVarOwned for cross-platform API or std.os.getenvW for Windows-specific API."); | ||
| 1163 | } | ||
| 1166 | return getenv(mem.toSliceConst(u8, key)); | 1164 | return getenv(mem.toSliceConst(u8, key)); |
| 1167 | } | 1165 | } |
| 1168 | 1166 | ||
| 1167 | /// Windows-only. Get an environment variable with a null-terminated, WTF-16 encoded name. | ||
| 1168 | /// See also `getenv`. | ||
| 1169 | pub fn getenvW(key: [*:0]const u16) ?[:0]const u16 { | ||
| 1170 | if (builtin.os != .windows) { | ||
| 1171 | @compileError("std.os.getenvW is a Windows-only API"); | ||
| 1172 | } | ||
| 1173 | const key_slice = mem.toSliceConst(u16, key); | ||
| 1174 | const ptr = windows.peb().ProcessParameters.Environment; | ||
| 1175 | var i: usize = 0; | ||
| 1176 | while (ptr[i] != 0) { | ||
| 1177 | const key_start = i; | ||
| 1178 | |||
| 1179 | while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} | ||
| 1180 | const this_key = ptr[key_start..i]; | ||
| 1181 | |||
| 1182 | if (ptr[i] == '=') i += 1; | ||
| 1183 | |||
| 1184 | const value_start = i; | ||
| 1185 | while (ptr[i] != 0) : (i += 1) {} | ||
| 1186 | const this_value = ptr[value_start..i :0]; | ||
| 1187 | |||
| 1188 | if (mem.eql(u16, key_slice, this_key)) return this_value; | ||
| 1189 | |||
| 1190 | i += 1; // skip over null byte | ||
| 1191 | } | ||
| 1192 | return null; | ||
| 1193 | } | ||
| 1194 | |||
| 1169 | pub const GetCwdError = error{ | 1195 | pub const GetCwdError = error{ |
| 1170 | NameTooLong, | 1196 | NameTooLong, |
| 1171 | CurrentWorkingDirectoryUnlinked, | 1197 | CurrentWorkingDirectoryUnlinked, |
lib/std/os/test.zig+8| ... | @@ -351,3 +351,11 @@ test "mmap" { | ... | @@ -351,3 +351,11 @@ test "mmap" { |
| 351 | 351 | ||
| 352 | try fs.cwd().deleteFile(test_out_file); | 352 | try fs.cwd().deleteFile(test_out_file); |
| 353 | } | 353 | } |
| 354 | |||
| 355 | test "getenv" { | ||
| 356 | if (builtin.os == .windows) { | ||
| 357 | expect(os.getenvW(&[_:0]u16{ 'B', 'O', 'G', 'U', 'S', 0x11, 0x22, 0x33, 0x44, 0x55 }) == null); | ||
| 358 | } else { | ||
| 359 | expect(os.getenvZ("BOGUSDOESNOTEXISTENVVAR") == null); | ||
| 360 | } | ||
| 361 | } |
lib/std/os/windows/bits.zig+1-1| ... | @@ -1187,7 +1187,7 @@ pub const RTL_USER_PROCESS_PARAMETERS = extern struct { | ... | @@ -1187,7 +1187,7 @@ pub const RTL_USER_PROCESS_PARAMETERS = extern struct { |
| 1187 | DllPath: UNICODE_STRING, | 1187 | DllPath: UNICODE_STRING, |
| 1188 | ImagePathName: UNICODE_STRING, | 1188 | ImagePathName: UNICODE_STRING, |
| 1189 | CommandLine: UNICODE_STRING, | 1189 | CommandLine: UNICODE_STRING, |
| 1190 | Environment: [*]WCHAR, | 1190 | Environment: [*:0]WCHAR, |
| 1191 | dwX: ULONG, | 1191 | dwX: ULONG, |
| 1192 | dwY: ULONG, | 1192 | dwY: ULONG, |
| 1193 | dwXSize: ULONG, | 1193 | dwXSize: ULONG, |
lib/std/process.zig+14-32| ... | @@ -37,14 +37,11 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { | ... | @@ -37,14 +37,11 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { |
| 37 | errdefer result.deinit(); | 37 | errdefer result.deinit(); |
| 38 | 38 | ||
| 39 | if (builtin.os == .windows) { | 39 | if (builtin.os == .windows) { |
| 40 | // TODO update this to use the ProcessEnvironmentBlock | 40 | const ptr = windows.peb().ProcessParameters.Environment; |
| 41 | const ptr = try os.windows.GetEnvironmentStringsW(); | ||
| 42 | defer os.windows.FreeEnvironmentStringsW(ptr); | 41 | defer os.windows.FreeEnvironmentStringsW(ptr); |
| 43 | 42 | ||
| 44 | var i: usize = 0; | 43 | var i: usize = 0; |
| 45 | while (true) { | 44 | while (ptr[i] != 0) { |
| 46 | if (ptr[i] == 0) return result; | ||
| 47 | |||
| 48 | const key_start = i; | 45 | const key_start = i; |
| 49 | 46 | ||
| 50 | while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} | 47 | while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} |
| ... | @@ -64,6 +61,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { | ... | @@ -64,6 +61,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { |
| 64 | 61 | ||
| 65 | try result.setMove(key, value); | 62 | try result.setMove(key, value); |
| 66 | } | 63 | } |
| 64 | return result; | ||
| 67 | } else if (builtin.os == .wasi) { | 65 | } else if (builtin.os == .wasi) { |
| 68 | var environ_count: usize = undefined; | 66 | var environ_count: usize = undefined; |
| 69 | var environ_buf_size: usize = undefined; | 67 | var environ_buf_size: usize = undefined; |
| ... | @@ -141,34 +139,18 @@ pub const GetEnvVarOwnedError = error{ | ... | @@ -141,34 +139,18 @@ pub const GetEnvVarOwnedError = error{ |
| 141 | /// Caller must free returned memory. | 139 | /// Caller must free returned memory. |
| 142 | pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { | 140 | pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { |
| 143 | if (builtin.os == .windows) { | 141 | if (builtin.os == .windows) { |
| 144 | const key_with_null = try std.unicode.utf8ToUtf16LeWithNull(allocator, key); | 142 | const result_w = blk: { |
| 145 | defer allocator.free(key_with_null); | 143 | const key_w = try std.unicode.utf8ToUtf16LeWithNull(allocator, key); |
| 146 | 144 | defer allocator.free(key_w); | |
| 147 | var buf = try allocator.alloc(u16, 256); | ||
| 148 | defer allocator.free(buf); | ||
| 149 | |||
| 150 | while (true) { | ||
| 151 | const windows_buf_len = math.cast(os.windows.DWORD, buf.len) catch return error.OutOfMemory; | ||
| 152 | const result = os.windows.GetEnvironmentVariableW( | ||
| 153 | key_with_null.ptr, | ||
| 154 | buf.ptr, | ||
| 155 | windows_buf_len, | ||
| 156 | ) catch |err| switch (err) { | ||
| 157 | error.Unexpected => return error.EnvironmentVariableNotFound, | ||
| 158 | else => |e| return e, | ||
| 159 | }; | ||
| 160 | if (result > buf.len) { | ||
| 161 | buf = try allocator.realloc(buf, result); | ||
| 162 | continue; | ||
| 163 | } | ||
| 164 | 145 | ||
| 165 | return std.unicode.utf16leToUtf8Alloc(allocator, buf[0..result]) catch |err| switch (err) { | 146 | break :blk std.os.getenvW(key_w) orelse return error.EnvironmentVariableNotFound; |
| 166 | error.DanglingSurrogateHalf => return error.InvalidUtf8, | 147 | }; |
| 167 | error.ExpectedSecondSurrogateHalf => return error.InvalidUtf8, | 148 | return std.unicode.utf16leToUtf8Alloc(allocator, result_w) catch |err| switch (err) { |
| 168 | error.UnexpectedSecondSurrogateHalf => return error.InvalidUtf8, | 149 | error.DanglingSurrogateHalf => return error.InvalidUtf8, |
| 169 | else => |e| return e, | 150 | error.ExpectedSecondSurrogateHalf => return error.InvalidUtf8, |
| 170 | }; | 151 | error.UnexpectedSecondSurrogateHalf => return error.InvalidUtf8, |
| 171 | } | 152 | else => |e| return e, |
| 153 | }; | ||
| 172 | } else { | 154 | } else { |
| 173 | const result = os.getenv(key) orelse return error.EnvironmentVariableNotFound; | 155 | const result = os.getenv(key) orelse return error.EnvironmentVariableNotFound; |
| 174 | return mem.dupe(allocator, u8, result); | 156 | return mem.dupe(allocator, u8, result); |
lib/std/zig/system.zig+41-30| ... | @@ -5,6 +5,8 @@ const ArrayList = std.ArrayList; | ... | @@ -5,6 +5,8 @@ const ArrayList = std.ArrayList; |
| 5 | const assert = std.debug.assert; | 5 | const assert = std.debug.assert; |
| 6 | const process = std.process; | 6 | const process = std.process; |
| 7 | 7 | ||
| 8 | const is_windows = std.Target.current.isWindows(); | ||
| 9 | |||
| 8 | pub const NativePaths = struct { | 10 | pub const NativePaths = struct { |
| 9 | include_dirs: ArrayList([:0]u8), | 11 | include_dirs: ArrayList([:0]u8), |
| 10 | lib_dirs: ArrayList([:0]u8), | 12 | lib_dirs: ArrayList([:0]u8), |
| ... | @@ -21,7 +23,9 @@ pub const NativePaths = struct { | ... | @@ -21,7 +23,9 @@ pub const NativePaths = struct { |
| 21 | errdefer self.deinit(); | 23 | errdefer self.deinit(); |
| 22 | 24 | ||
| 23 | var is_nix = false; | 25 | var is_nix = false; |
| 24 | if (std.os.getenvZ("NIX_CFLAGS_COMPILE")) |nix_cflags_compile| { | 26 | if (process.getEnvVarOwned(allocator, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| { |
| 27 | defer allocator.free(nix_cflags_compile); | ||
| 28 | |||
| 25 | is_nix = true; | 29 | is_nix = true; |
| 26 | var it = mem.tokenize(nix_cflags_compile, " "); | 30 | var it = mem.tokenize(nix_cflags_compile, " "); |
| 27 | while (true) { | 31 | while (true) { |
| ... | @@ -37,8 +41,14 @@ pub const NativePaths = struct { | ... | @@ -37,8 +41,14 @@ pub const NativePaths = struct { |
| 37 | break; | 41 | break; |
| 38 | } | 42 | } |
| 39 | } | 43 | } |
| 44 | } else |err| switch (err) { | ||
| 45 | error.InvalidUtf8 => {}, | ||
| 46 | error.EnvironmentVariableNotFound => {}, | ||
| 47 | error.OutOfMemory => |e| return e, | ||
| 40 | } | 48 | } |
| 41 | if (std.os.getenvZ("NIX_LDFLAGS")) |nix_ldflags| { | 49 | if (process.getEnvVarOwned(allocator, "NIX_LDFLAGS")) |nix_ldflags| { |
| 50 | defer allocator.free(nix_ldflags); | ||
| 51 | |||
| 42 | is_nix = true; | 52 | is_nix = true; |
| 43 | var it = mem.tokenize(nix_ldflags, " "); | 53 | var it = mem.tokenize(nix_ldflags, " "); |
| 44 | while (true) { | 54 | while (true) { |
| ... | @@ -57,39 +67,40 @@ pub const NativePaths = struct { | ... | @@ -57,39 +67,40 @@ pub const NativePaths = struct { |
| 57 | break; | 67 | break; |
| 58 | } | 68 | } |
| 59 | } | 69 | } |
| 70 | } else |err| switch (err) { | ||
| 71 | error.InvalidUtf8 => {}, | ||
| 72 | error.EnvironmentVariableNotFound => {}, | ||
| 73 | error.OutOfMemory => |e| return e, | ||
| 60 | } | 74 | } |
| 61 | if (is_nix) { | 75 | if (is_nix) { |
| 62 | return self; | 76 | return self; |
| 63 | } | 77 | } |
| 64 | 78 | ||
| 65 | switch (std.builtin.os) { | 79 | if (!is_windows) { |
| 66 | .windows => {}, | 80 | const triple = try std.Target.current.linuxTriple(allocator); |
| 67 | else => { | 81 | |
| 68 | const triple = try std.Target.current.linuxTriple(allocator); | 82 | // TODO: $ ld --verbose | grep SEARCH_DIR |
| 69 | 83 | // the output contains some paths that end with lib64, maybe include them too? | |
| 70 | // TODO: $ ld --verbose | grep SEARCH_DIR | 84 | // TODO: what is the best possible order of things? |
| 71 | // the output contains some paths that end with lib64, maybe include them too? | 85 | // TODO: some of these are suspect and should only be added on some systems. audit needed. |
| 72 | // TODO: what is the best possible order of things? | 86 | |
| 73 | // TODO: some of these are suspect and should only be added on some systems. audit needed. | 87 | try self.addIncludeDir("/usr/local/include"); |
| 74 | 88 | try self.addLibDir("/usr/local/lib"); | |
| 75 | try self.addIncludeDir("/usr/local/include"); | 89 | try self.addLibDir("/usr/local/lib64"); |
| 76 | try self.addLibDir("/usr/local/lib"); | 90 | |
| 77 | try self.addLibDir("/usr/local/lib64"); | 91 | try self.addIncludeDirFmt("/usr/include/{}", .{triple}); |
| 78 | 92 | try self.addLibDirFmt("/usr/lib/{}", .{triple}); | |
| 79 | try self.addIncludeDirFmt("/usr/include/{}", .{triple}); | 93 | |
| 80 | try self.addLibDirFmt("/usr/lib/{}", .{triple}); | 94 | try self.addIncludeDir("/usr/include"); |
| 81 | 95 | try self.addLibDir("/lib"); | |
| 82 | try self.addIncludeDir("/usr/include"); | 96 | try self.addLibDir("/lib64"); |
| 83 | try self.addLibDir("/lib"); | 97 | try self.addLibDir("/usr/lib"); |
| 84 | try self.addLibDir("/lib64"); | 98 | try self.addLibDir("/usr/lib64"); |
| 85 | try self.addLibDir("/usr/lib"); | 99 | |
| 86 | try self.addLibDir("/usr/lib64"); | 100 | // example: on a 64-bit debian-based linux distro, with zlib installed from apt: |
| 87 | 101 | // zlib.h is in /usr/include (added above) | |
| 88 | // example: on a 64-bit debian-based linux distro, with zlib installed from apt: | 102 | // libz.so.1 is in /lib/x86_64-linux-gnu (added here) |
| 89 | // zlib.h is in /usr/include (added above) | 103 | try self.addLibDirFmt("/lib/{}", .{triple}); |
| 90 | // libz.so.1 is in /lib/x86_64-linux-gnu (added here) | ||
| 91 | try self.addLibDirFmt("/lib/{}", .{triple}); | ||
| 92 | }, | ||
| 93 | } | 104 | } |
| 94 | 105 | ||
| 95 | return self; | 106 | return self; |