1const std = @import("../../std.zig"); 2const builtin = @import("builtin"); 3const ArrayList = std.ArrayList; 4const Allocator = std.mem.Allocator; 5const process = std.process; 6const mem = std.mem; 7 8const NativePaths = @This(); 9const NativeTargetInfo = std.zig.system.NativeTargetInfo; 10 11include_dirs: ArrayList([:0]u8), 12lib_dirs: ArrayList([:0]u8), 13framework_dirs: ArrayList([:0]u8), 14rpaths: ArrayList([:0]u8), 15warnings: ArrayList([:0]u8), 16 17pub fn detect(allocator: Allocator, native_info: NativeTargetInfo) !NativePaths { 18 const native_target = native_info.target; 19 20 var self: NativePaths = .{ 21 .include_dirs = ArrayList([:0]u8).init(allocator), 22 .lib_dirs = ArrayList([:0]u8).init(allocator), 23 .framework_dirs = ArrayList([:0]u8).init(allocator), 24 .rpaths = ArrayList([:0]u8).init(allocator), 25 .warnings = ArrayList([:0]u8).init(allocator), 26 }; 27 errdefer self.deinit(); 28 29 var is_nix = false; 30 if (process.getEnvVarOwned(allocator, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| { 31 defer allocator.free(nix_cflags_compile); 32 33 is_nix = true; 34 var it = mem.tokenize(u8, nix_cflags_compile, " "); 35 while (true) { 36 const word = it.next() orelse break; 37 if (mem.eql(u8, word, "-isystem")) { 38 const include_path = it.next() orelse { 39 try self.addWarning("Expected argument after -isystem in NIX_CFLAGS_COMPILE"); 40 break; 41 }; 42 try self.addIncludeDir(include_path); 43 } else { 44 if (mem.startsWith(u8, word, "-frandom-seed=")) { 45 continue; 46 } 47 try self.addWarningFmt("Unrecognized C flag from NIX_CFLAGS_COMPILE: {s}", .{word}); 48 } 49 } 50 } else |err| switch (err) { 51 error.InvalidUtf8 => {}, 52 error.EnvironmentVariableNotFound => {}, 53 error.OutOfMemory => |e| return e, 54 } 55 if (process.getEnvVarOwned(allocator, "NIX_LDFLAGS")) |nix_ldflags| { 56 defer allocator.free(nix_ldflags); 57 58 is_nix = true; 59 var it = mem.tokenize(u8, nix_ldflags, " "); 60 while (true) { 61 const word = it.next() orelse break; 62 if (mem.eql(u8, word, "-rpath")) { 63 const rpath = it.next() orelse { 64 try self.addWarning("Expected argument after -rpath in NIX_LDFLAGS"); 65 break; 66 }; 67 try self.addRPath(rpath); 68 } else if (word.len > 2 and word[0] == '-' and word[1] == 'L') { 69 const lib_path = word[2..]; 70 try self.addLibDir(lib_path); 71 } else { 72 try self.addWarningFmt("Unrecognized C flag from NIX_LDFLAGS: {s}", .{word}); 73 break; 74 } 75 } 76 } else |err| switch (err) { 77 error.InvalidUtf8 => {}, 78 error.EnvironmentVariableNotFound => {}, 79 error.OutOfMemory => |e| return e, 80 } 81 if (is_nix) { 82 return self; 83 } 84 85 if (comptime builtin.target.isDarwin()) { 86 try self.addIncludeDir("/usr/include"); 87 try self.addIncludeDir("/usr/local/include"); 88 89 try self.addLibDir("/usr/lib"); 90 try self.addLibDir("/usr/local/lib"); 91 92 try self.addFrameworkDir("/Library/Frameworks"); 93 try self.addFrameworkDir("/System/Library/Frameworks"); 94 95 return self; 96 } 97 98 if (comptime native_target.os.tag == .solaris) { 99 try self.addLibDir("/usr/lib/64"); 100 try self.addLibDir("/usr/local/lib/64"); 101 try self.addLibDir("/lib/64"); 102 103 try self.addIncludeDir("/usr/include"); 104 try self.addIncludeDir("/usr/local/include"); 105 106 return self; 107 } 108 109 if (native_target.os.tag != .windows) { 110 const triple = try native_target.linuxTriple(allocator); 111 const qual = native_target.cpu.arch.ptrBitWidth(); 112 113 // TODO: $ ld --verbose | grep SEARCH_DIR 114 // the output contains some paths that end with lib64, maybe include them too? 115 // TODO: what is the best possible order of things? 116 // TODO: some of these are suspect and should only be added on some systems. audit needed. 117 118 try self.addIncludeDir("/usr/local/include"); 119 try self.addLibDirFmt("/usr/local/lib{d}", .{qual}); 120 try self.addLibDir("/usr/local/lib"); 121 122 try self.addIncludeDirFmt("/usr/include/{s}", .{triple}); 123 try self.addLibDirFmt("/usr/lib/{s}", .{triple}); 124 125 try self.addIncludeDir("/usr/include"); 126 try self.addLibDirFmt("/lib{d}", .{qual}); 127 try self.addLibDir("/lib"); 128 try self.addLibDirFmt("/usr/lib{d}", .{qual}); 129 try self.addLibDir("/usr/lib"); 130 131 // example: on a 64-bit debian-based linux distro, with zlib installed from apt: 132 // zlib.h is in /usr/include (added above) 133 // libz.so.1 is in /lib/x86_64-linux-gnu (added here) 134 try self.addLibDirFmt("/lib/{s}", .{triple}); 135 } 136 137 return self; 138} 139 140pub fn deinit(self: *NativePaths) void { 141 deinitArray(&self.include_dirs); 142 deinitArray(&self.lib_dirs); 143 deinitArray(&self.framework_dirs); 144 deinitArray(&self.rpaths); 145 deinitArray(&self.warnings); 146 self.* = undefined; 147} 148 149fn deinitArray(array: *ArrayList([:0]u8)) void { 150 for (array.items) |item| { 151 array.allocator.free(item); 152 } 153 array.deinit(); 154} 155 156pub fn addIncludeDir(self: *NativePaths, s: []const u8) !void { 157 return self.appendArray(&self.include_dirs, s); 158} 159 160pub fn addIncludeDirFmt(self: *NativePaths, comptime fmt: []const u8, args: anytype) !void { 161 const item = try std.fmt.allocPrintZ(self.include_dirs.allocator, fmt, args); 162 errdefer self.include_dirs.allocator.free(item); 163 try self.include_dirs.append(item); 164} 165 166pub fn addLibDir(self: *NativePaths, s: []const u8) !void { 167 return self.appendArray(&self.lib_dirs, s); 168} 169 170pub fn addLibDirFmt(self: *NativePaths, comptime fmt: []const u8, args: anytype) !void { 171 const item = try std.fmt.allocPrintZ(self.lib_dirs.allocator, fmt, args); 172 errdefer self.lib_dirs.allocator.free(item); 173 try self.lib_dirs.append(item); 174} 175 176pub fn addWarning(self: *NativePaths, s: []const u8) !void { 177 return self.appendArray(&self.warnings, s); 178} 179 180pub fn addFrameworkDir(self: *NativePaths, s: []const u8) !void { 181 return self.appendArray(&self.framework_dirs, s); 182} 183 184pub fn addFrameworkDirFmt(self: *NativePaths, comptime fmt: []const u8, args: anytype) !void { 185 const item = try std.fmt.allocPrintZ(self.framework_dirs.allocator, fmt, args); 186 errdefer self.framework_dirs.allocator.free(item); 187 try self.framework_dirs.append(item); 188} 189 190pub fn addWarningFmt(self: *NativePaths, comptime fmt: []const u8, args: anytype) !void { 191 const item = try std.fmt.allocPrintZ(self.warnings.allocator, fmt, args); 192 errdefer self.warnings.allocator.free(item); 193 try self.warnings.append(item); 194} 195 196pub fn addRPath(self: *NativePaths, s: []const u8) !void { 197 return self.appendArray(&self.rpaths, s); 198} 199 200fn appendArray(self: *NativePaths, array: *ArrayList([:0]u8), s: []const u8) !void { 201 _ = self; 202 const item = try array.allocator.dupeZ(u8, s); 203 errdefer array.allocator.free(item); 204 try array.append(item); 205} 206