From 8944dea23fb554290a4b54ca40b0594f6e3f77a9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 19 Dec 2023 15:03:55 -0700 Subject: [PATCH] CLI: fix regressed logic for any_dyn_libs This value needs access to the fully resolved set of system libraries, which required restructuring a bunch of CLI logic. --- src/Compilation.zig | 11 + src/main.zig | 621 +++++++++++++++++++++++--------------------- 2 files changed, 329 insertions(+), 303 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index fa674e28fa3de8c9cc8046da22af59d8b86b162a..f825d8c93079a4fb51b230703155f7bfe02459f0 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -86,6 +86,7 @@ skip_linker_dependencies: bool, no_builtin: bool, function_sections: bool, data_sections: bool, +native_system_include_paths: []const []const u8, c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{}, win32_resource_table: if (build_options.only_core_functionality) void else std.AutoArrayHashMapUnmanaged(*Win32Resource, void) = @@ -1065,6 +1066,7 @@ pub const InitOptions = struct { version: ?std.SemanticVersion = null, compatibility_version: ?std.SemanticVersion = null, libc_installation: ?*const LibCInstallation = null, + native_system_include_paths: []const []const u8 = &.{}, clang_preprocessor_mode: ClangPreprocessorMode = .no, /// This is for stage1 and should be deleted upon completion of self-hosting. color: Color = .auto, @@ -1508,6 +1510,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .job_queued_update_builtin_zig = have_zcu, .function_sections = options.function_sections, .data_sections = options.data_sections, + .native_system_include_paths = options.native_system_include_paths, }; const lf_open_opts: link.File.OpenOptions = .{ @@ -5296,6 +5299,14 @@ pub fn addCCArgs( try argv.append("-ffreestanding"); } + if (mod.resolved_target.is_native_os and mod.resolved_target.is_native_abi) { + try argv.ensureUnusedCapacity(comp.native_system_include_paths.len * 2); + for (comp.native_system_include_paths) |include_path| { + argv.appendAssumeCapacity("-isystem"); + argv.appendAssumeCapacity(include_path); + } + } + try argv.appendSlice(mod.cc_argv); } diff --git a/src/main.zig b/src/main.zig index aee670756fb0fcd4cbc5a871d1ed78d05250096b..8a1286bc8ea4aa2cfecc286018bb21cc7bcc36bf 100644 --- a/src/main.zig +++ b/src/main.zig @@ -821,7 +821,6 @@ fn buildOutputType( var target_mcpu: ?[]const u8 = null; var emit_h: Emit = .no; var soname: SOName = undefined; - var want_native_include_dirs = false; var want_compiler_rt: ?bool = null; var linker_script: ?[]const u8 = null; var version_script: ?[]const u8 = null; @@ -863,8 +862,6 @@ fn buildOutputType( var link_emit_relocs = false; var each_lib_rpath: ?bool = null; var build_id: ?std.zig.BuildId = null; - var sysroot: ?[]const u8 = null; - var libc_paths_file: ?[]const u8 = try EnvVar.ZIG_LIBC.get(arena); var runtime_args_start: ?usize = null; var test_filter: ?[]const u8 = null; var test_name_prefix: ?[]const u8 = null; @@ -892,16 +889,11 @@ fn buildOutputType( var pdb_out_path: ?[]const u8 = null; var error_limit: ?Module.ErrorInt = null; // These are before resolving sysroot. - var lib_dir_args: std.ArrayListUnmanaged([]const u8) = .{}; var extra_cflags: std.ArrayListUnmanaged([]const u8) = .{}; var extra_rcflags: std.ArrayListUnmanaged([]const u8) = .{}; var symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .{}; - var rpath_list: std.ArrayListUnmanaged([]const u8) = .{}; var rc_includes: Compilation.RcIncludes = .any; var manifest_file: ?[]const u8 = null; - var link_objects: std.ArrayListUnmanaged(Compilation.LinkObject) = .{}; - var framework_dirs: std.ArrayListUnmanaged([]const u8) = .{}; - var frameworks: std.StringArrayHashMapUnmanaged(Framework) = .{}; var linker_export_symbol_names: std.ArrayListUnmanaged([]const u8) = .{}; // Tracks the position in c_source_files which have already their owner populated. @@ -951,7 +943,6 @@ fn buildOutputType( .resolved_options = undefined, .system_libs = .{}, - .external_system_libs = .{}, .resolved_system_libs = .{}, .wasi_emulated_libs = .{}, @@ -959,6 +950,17 @@ fn buildOutputType( .rc_source_files = .{}, .llvm_m_args = .{}, + .sysroot = null, + .lib_dirs = .{}, // populated by createModule() + .lib_dir_args = .{}, // populated from CLI arg parsing + .libc_installation = null, + .want_native_include_dirs = false, + .frameworks = .{}, + .framework_dirs = .{}, + .rpath_list = .{}, + .libc_paths_file = try EnvVar.ZIG_LIBC.get(arena), + .link_objects = .{}, + .native_system_include_paths = &.{}, }; // before arg parsing, check for the NO_COLOR environment variable @@ -1137,17 +1139,17 @@ fn buildOutputType( if (!mem.eql(u8, provided_name.?, fs.path.basename(provided_name.?))) fatal("invalid package name '{s}': cannot contain folder separators", .{provided_name.?}); } else if (mem.eql(u8, arg, "-rpath")) { - try rpath_list.append(arena, args_iter.nextOrFatal()); + try create_module.rpath_list.append(arena, args_iter.nextOrFatal()); } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) { - try lib_dir_args.append(arena, args_iter.nextOrFatal()); + try create_module.lib_dir_args.append(arena, args_iter.nextOrFatal()); } else if (mem.eql(u8, arg, "-F")) { - try framework_dirs.append(arena, args_iter.nextOrFatal()); + try create_module.framework_dirs.append(arena, args_iter.nextOrFatal()); } else if (mem.eql(u8, arg, "-framework")) { - try frameworks.put(arena, args_iter.nextOrFatal(), .{}); + try create_module.frameworks.put(arena, args_iter.nextOrFatal(), .{}); } else if (mem.eql(u8, arg, "-weak_framework")) { - try frameworks.put(arena, args_iter.nextOrFatal(), .{ .weak = true }); + try create_module.frameworks.put(arena, args_iter.nextOrFatal(), .{ .weak = true }); } else if (mem.eql(u8, arg, "-needed_framework")) { - try frameworks.put(arena, args_iter.nextOrFatal(), .{ .needed = true }); + try create_module.frameworks.put(arena, args_iter.nextOrFatal(), .{ .needed = true }); } else if (mem.eql(u8, arg, "-install_name")) { install_name = args_iter.nextOrFatal(); } else if (mem.startsWith(u8, arg, "--compress-debug-sections=")) { @@ -1236,11 +1238,11 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "-iframework")) { const path = args_iter.nextOrFatal(); try cssan.addIncludePath(arena, &clang_argv, .iframework, arg, path, false); - try framework_dirs.append(arena, path); // Forward to the backend as -F + try create_module.framework_dirs.append(arena, path); // Forward to the backend as -F } else if (mem.eql(u8, arg, "-iframeworkwithsysroot")) { const path = args_iter.nextOrFatal(); try cssan.addIncludePath(arena, &clang_argv, .iframeworkwithsysroot, arg, path, false); - try framework_dirs.append(arena, path); // Forward to the backend as -F + try create_module.framework_dirs.append(arena, path); // Forward to the backend as -F } else if (mem.eql(u8, arg, "--version")) { const next_arg = args_iter.nextOrFatal(); version = std.SemanticVersion.parse(next_arg) catch |err| { @@ -1265,10 +1267,10 @@ fn buildOutputType( create_module.dynamic_linker = args_iter.nextOrFatal(); } else if (mem.eql(u8, arg, "--sysroot")) { const next_arg = args_iter.nextOrFatal(); - sysroot = next_arg; + create_module.sysroot = next_arg; try clang_argv.appendSlice(arena, &.{ "-isysroot", next_arg }); } else if (mem.eql(u8, arg, "--libc")) { - libc_paths_file = args_iter.nextOrFatal(); + create_module.libc_paths_file = args_iter.nextOrFatal(); } else if (mem.eql(u8, arg, "--test-filter")) { test_filter = args_iter.nextOrFatal(); } else if (mem.eql(u8, arg, "--test-name-prefix")) { @@ -1620,9 +1622,9 @@ fn buildOutputType( } else if (mem.startsWith(u8, arg, "-T")) { linker_script = arg[2..]; } else if (mem.startsWith(u8, arg, "-L")) { - try lib_dir_args.append(arena, arg[2..]); + try create_module.lib_dir_args.append(arena, arg[2..]); } else if (mem.startsWith(u8, arg, "-F")) { - try framework_dirs.append(arena, arg[2..]); + try create_module.framework_dirs.append(arena, arg[2..]); } else if (mem.startsWith(u8, arg, "-l")) { // We don't know whether this library is part of libc // or libc++ until we resolve the target, so we append @@ -1667,14 +1669,14 @@ fn buildOutputType( } } else switch (file_ext orelse Compilation.classifyFileExt(arg)) { .shared_library => { - try link_objects.append(arena, .{ .path = arg }); + try create_module.link_objects.append(arena, .{ .path = arg }); create_module.opts.any_dyn_libs = true; }, .object, .static_library => { - try link_objects.append(arena, .{ .path = arg }); + try create_module.link_objects.append(arena, .{ .path = arg }); }, .res => { - try link_objects.append(arena, .{ .path = arg }); + try create_module.link_objects.append(arena, .{ .path = arg }); contains_res_file = true; }, .manifest => { @@ -1721,7 +1723,7 @@ fn buildOutputType( soname = .no; create_module.opts.ensure_libc_on_non_freestanding = true; create_module.opts.ensure_libcpp_on_non_freestanding = arg_mode == .cpp; - want_native_include_dirs = true; + create_module.want_native_include_dirs = true; // Clang's driver enables this switch unconditionally. // Disabling the emission of .eh_frame_hdr can unexpectedly break // some functionality that depend on it, such as C++ exceptions and @@ -1786,20 +1788,20 @@ fn buildOutputType( }); }, .shared_library => { - try link_objects.append(arena, .{ + try create_module.link_objects.append(arena, .{ .path = it.only_arg, .must_link = must_link, }); create_module.opts.any_dyn_libs = true; }, .unknown, .object, .static_library => { - try link_objects.append(arena, .{ + try create_module.link_objects.append(arena, .{ .path = it.only_arg, .must_link = must_link, }); }, .res => { - try link_objects.append(arena, .{ + try create_module.link_objects.append(arena, .{ .path = it.only_arg, .must_link = must_link, }); @@ -1835,7 +1837,7 @@ fn buildOutputType( // more control over what's in the resulting // binary: no extra rpaths and DSO filename exactly // as provided. Hello, Go. - try link_objects.append(arena, .{ + try create_module.link_objects.append(arena, .{ .path = it.only_arg, .must_link = must_link, .loption = true, @@ -2036,7 +2038,7 @@ fn buildOutputType( try linker_args.append("-z"); try linker_args.append(it.only_arg); }, - .lib_dir => try lib_dir_args.append(arena, it.only_arg), + .lib_dir => try create_module.lib_dir_args.append(arena, it.only_arg), .mcpu => target_mcpu = it.only_arg, .m => try create_module.llvm_m_args.append(arena, it.only_arg), .dep_file => { @@ -2050,15 +2052,15 @@ fn buildOutputType( disable_c_depfile = true; try clang_argv.appendSlice(arena, it.other_args); }, - .framework_dir => try framework_dirs.append(arena, it.only_arg), - .framework => try frameworks.put(arena, it.only_arg, .{}), - .nostdlibinc => want_native_include_dirs = false, + .framework_dir => try create_module.framework_dirs.append(arena, it.only_arg), + .framework => try create_module.frameworks.put(arena, it.only_arg, .{}), + .nostdlibinc => create_module.want_native_include_dirs = false, .strip => mod_opts.strip = true, .exec_model => { create_module.opts.wasi_exec_model = parseWasiExecModel(it.only_arg); }, .sysroot => { - sysroot = it.only_arg; + create_module.sysroot = it.only_arg; }, .entry => { create_module.opts.entry = .{ .named = it.only_arg }; @@ -2072,7 +2074,7 @@ fn buildOutputType( .preferred_mode = lib_preferred_mode, .search_strategy = lib_search_strategy, }), - .weak_framework => try frameworks.put(arena, it.only_arg, .{ .weak = true }), + .weak_framework => try create_module.frameworks.put(arena, it.only_arg, .{ .weak = true }), .headerpad_max_install_names => headerpad_max_install_names = true, .compress_debug_sections => { if (it.only_arg.len == 0) { @@ -2133,7 +2135,7 @@ fn buildOutputType( } provided_name = name[prefix..end]; } else if (mem.eql(u8, arg, "-rpath")) { - try rpath_list.append(arena, linker_args_it.nextOrFatal()); + try create_module.rpath_list.append(arena, linker_args_it.nextOrFatal()); } else if (mem.eql(u8, arg, "--subsystem")) { subsystem = try parseSubSystem(linker_args_it.nextOrFatal()); } else if (mem.eql(u8, arg, "-I") or @@ -2345,11 +2347,11 @@ fn buildOutputType( }); }; } else if (mem.eql(u8, arg, "-framework")) { - try frameworks.put(arena, linker_args_it.nextOrFatal(), .{}); + try create_module.frameworks.put(arena, linker_args_it.nextOrFatal(), .{}); } else if (mem.eql(u8, arg, "-weak_framework")) { - try frameworks.put(arena, linker_args_it.nextOrFatal(), .{ .weak = true }); + try create_module.frameworks.put(arena, linker_args_it.nextOrFatal(), .{ .weak = true }); } else if (mem.eql(u8, arg, "-needed_framework")) { - try frameworks.put(arena, linker_args_it.nextOrFatal(), .{ .needed = true }); + try create_module.frameworks.put(arena, linker_args_it.nextOrFatal(), .{ .needed = true }); } else if (mem.eql(u8, arg, "-needed_library")) { try create_module.system_libs.put(arena, linker_args_it.nextOrFatal(), .{ .weak = false, @@ -2399,7 +2401,7 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "-install_name")) { install_name = linker_args_it.nextOrFatal(); } else if (mem.eql(u8, arg, "-force_load")) { - try link_objects.append(arena, .{ + try create_module.link_objects.append(arena, .{ .path = linker_args_it.nextOrFatal(), .must_link = true, }); @@ -2504,7 +2506,7 @@ fn buildOutputType( }, } if (create_module.c_source_files.items.len == 0 and - link_objects.items.len == 0 and + create_module.link_objects.items.len == 0 and root_src_file == null) { // For example `zig cc` and no args should print the "no input files" message. @@ -2550,8 +2552,8 @@ fn buildOutputType( if (create_module.c_source_files.items.len >= 1) break :b create_module.c_source_files.items[0].src_path; - if (link_objects.items.len >= 1) - break :b link_objects.items[0].path; + if (create_module.link_objects.items.len >= 1) + break :b create_module.link_objects.items[0].path; if (emit_bin == .yes) break :b emit_bin.yes; @@ -2761,250 +2763,10 @@ fn buildOutputType( } } - const root_name = if (provided_name) |n| n else main_mod.fully_qualified_name; - - // Resolve the library path arguments with respect to sysroot. - var lib_dirs: std.ArrayListUnmanaged([]const u8) = .{}; - if (sysroot) |root| { - try lib_dirs.ensureUnusedCapacity(arena, lib_dir_args.items.len * 2); - for (lib_dir_args.items) |dir| { - if (fs.path.isAbsolute(dir)) { - const stripped_dir = dir[fs.path.diskDesignator(dir).len..]; - const full_path = try fs.path.join(arena, &[_][]const u8{ root, stripped_dir }); - lib_dirs.appendAssumeCapacity(full_path); - } - lib_dirs.appendAssumeCapacity(dir); - } - } else { - lib_dirs = lib_dir_args; - } - lib_dir_args = undefined; // From here we use lib_dirs instead. - - if (main_mod.resolved_target.is_native_os and target.isDarwin()) { - // If we want to link against frameworks, we need system headers. - if (framework_dirs.items.len > 0 or frameworks.count() > 0) - want_native_include_dirs = true; - } - - // Trigger native system library path detection if necessary. - if (sysroot == null and - main_mod.resolved_target.is_native_os and - main_mod.resolved_target.is_native_abi and - (create_module.external_system_libs.len != 0 or want_native_include_dirs)) - { - const paths = std.zig.system.NativePaths.detect(arena, target) catch |err| { - fatal("unable to detect native system paths: {s}", .{@errorName(err)}); - }; - for (paths.warnings.items) |warning| { - warn("{s}", .{warning}); - } - - try clang_argv.ensureUnusedCapacity(arena, paths.include_dirs.items.len * 2); - for (paths.include_dirs.items) |include_dir| { - clang_argv.appendAssumeCapacity("-isystem"); - clang_argv.appendAssumeCapacity(include_dir); - } - - try framework_dirs.appendSlice(arena, paths.framework_dirs.items); - try lib_dirs.appendSlice(arena, paths.lib_dirs.items); - try rpath_list.appendSlice(arena, paths.rpaths.items); - } - - var libc_installation: ?LibCInstallation = null; - if (libc_paths_file) |paths_file| { - libc_installation = LibCInstallation.parse(arena, paths_file, target) catch |err| { - fatal("unable to parse libc paths file at path {s}: {s}", .{ paths_file, @errorName(err) }); - }; - } - - if (builtin.target.os.tag == .windows and - target.abi == .msvc and - create_module.external_system_libs.len != 0) - { - if (libc_installation == null) { - libc_installation = try LibCInstallation.findNative(.{ - .allocator = arena, - .verbose = true, - .target = target, - }); - - try lib_dirs.appendSlice(arena, &.{ - libc_installation.?.msvc_lib_dir.?, - libc_installation.?.kernel32_lib_dir.?, - }); - } - } - - // If any libs in this list are statically provided, we omit them from the - // resolved list and populate the link_objects array instead. - { - var test_path = std.ArrayList(u8).init(gpa); - defer test_path.deinit(); - - var checked_paths = std.ArrayList(u8).init(gpa); - defer checked_paths.deinit(); - - var failed_libs = std.ArrayList(struct { - name: []const u8, - strategy: SystemLib.SearchStrategy, - checked_paths: []const u8, - preferred_mode: std.builtin.LinkMode, - }).init(arena); - - syslib: for (create_module.external_system_libs.items(.name), create_module.external_system_libs.items(.info)) |lib_name, info| { - // Checked in the first pass above while looking for libc libraries. - assert(!fs.path.isAbsolute(lib_name)); - - checked_paths.clearRetainingCapacity(); - - switch (info.search_strategy) { - .mode_first, .no_fallback => { - // check for preferred mode - for (lib_dirs.items) |lib_dir_path| { - if (try accessLibPath( - &test_path, - &checked_paths, - lib_dir_path, - lib_name, - target, - info.preferred_mode, - )) { - const path = try arena.dupe(u8, test_path.items); - switch (info.preferred_mode) { - .Static => try link_objects.append(arena, .{ .path = path }), - .Dynamic => try create_module.resolved_system_libs.append(arena, .{ - .name = lib_name, - .lib = .{ - .needed = info.needed, - .weak = info.weak, - .path = path, - }, - }), - } - continue :syslib; - } - } - // check for fallback mode - if (info.search_strategy == .no_fallback) { - try failed_libs.append(.{ - .name = lib_name, - .strategy = info.search_strategy, - .checked_paths = try arena.dupe(u8, checked_paths.items), - .preferred_mode = info.preferred_mode, - }); - continue :syslib; - } - for (lib_dirs.items) |lib_dir_path| { - if (try accessLibPath( - &test_path, - &checked_paths, - lib_dir_path, - lib_name, - target, - info.fallbackMode(), - )) { - const path = try arena.dupe(u8, test_path.items); - switch (info.fallbackMode()) { - .Static => try link_objects.append(arena, .{ .path = path }), - .Dynamic => try create_module.resolved_system_libs.append(arena, .{ - .name = lib_name, - .lib = .{ - .needed = info.needed, - .weak = info.weak, - .path = path, - }, - }), - } - continue :syslib; - } - } - try failed_libs.append(.{ - .name = lib_name, - .strategy = info.search_strategy, - .checked_paths = try arena.dupe(u8, checked_paths.items), - .preferred_mode = info.preferred_mode, - }); - continue :syslib; - }, - .paths_first => { - for (lib_dirs.items) |lib_dir_path| { - // check for preferred mode - if (try accessLibPath( - &test_path, - &checked_paths, - lib_dir_path, - lib_name, - target, - info.preferred_mode, - )) { - const path = try arena.dupe(u8, test_path.items); - switch (info.preferred_mode) { - .Static => try link_objects.append(arena, .{ .path = path }), - .Dynamic => try create_module.resolved_system_libs.append(arena, .{ - .name = lib_name, - .lib = .{ - .needed = info.needed, - .weak = info.weak, - .path = path, - }, - }), - } - continue :syslib; - } - - // check for fallback mode - if (try accessLibPath( - &test_path, - &checked_paths, - lib_dir_path, - lib_name, - target, - info.fallbackMode(), - )) { - const path = try arena.dupe(u8, test_path.items); - switch (info.fallbackMode()) { - .Static => try link_objects.append(arena, .{ .path = path }), - .Dynamic => try create_module.resolved_system_libs.append(arena, .{ - .name = lib_name, - .lib = .{ - .needed = info.needed, - .weak = info.weak, - .path = path, - }, - }), - } - continue :syslib; - } - } - try failed_libs.append(.{ - .name = lib_name, - .strategy = info.search_strategy, - .checked_paths = try arena.dupe(u8, checked_paths.items), - .preferred_mode = info.preferred_mode, - }); - continue :syslib; - }, - } - @compileError("unreachable"); - } - - if (failed_libs.items.len > 0) { - for (failed_libs.items) |f| { - const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths; - std.log.err("unable to find {s} system library '{s}' using strategy '{s}'. searched paths:{s}", .{ - @tagName(f.preferred_mode), f.name, @tagName(f.strategy), searched_paths, - }); - } - process.exit(1); - } - } - // After this point, create_module.resolved_system_libs is used instead of - // create_module.external_system_libs. - // We now repeat part of the process for frameworks. var resolved_frameworks = std.ArrayList(Compilation.Framework).init(arena); - if (frameworks.keys().len > 0) { + if (create_module.frameworks.keys().len > 0) { var test_path = std.ArrayList(u8).init(gpa); defer test_path.deinit(); @@ -3016,10 +2778,10 @@ fn buildOutputType( checked_paths: []const u8, }).init(arena); - framework: for (frameworks.keys(), frameworks.values()) |framework_name, info| { + framework: for (create_module.frameworks.keys(), create_module.frameworks.values()) |framework_name, info| { checked_paths.clearRetainingCapacity(); - for (framework_dirs.items) |framework_dir_path| { + for (create_module.framework_dirs.items) |framework_dir_path| { if (try accessFrameworkPath( &test_path, &checked_paths, @@ -3054,11 +2816,13 @@ fn buildOutputType( } // After this point, resolved_frameworks is used instead of frameworks. - if (create_module.opts.output_mode == .Obj and (target.ofmt == .coff or target.ofmt == .macho)) { + if (create_module.resolved_options.output_mode == .Obj and + (target.ofmt == .coff or target.ofmt == .macho)) + { const total_obj_count = create_module.c_source_files.items.len + @intFromBool(root_src_file != null) + create_module.rc_source_files.items.len + - link_objects.items.len; + create_module.link_objects.items.len; if (total_obj_count > 1) { fatal("{s} does not support linking multiple objects into one", .{@tagName(target.ofmt)}); } @@ -3070,6 +2834,8 @@ fn buildOutputType( const output_to_cache = listen != .none; const optional_version = if (have_version) version else null; + const root_name = if (provided_name) |n| n else main_mod.fully_qualified_name; + const resolved_soname: ?[]const u8 = switch (soname) { .yes => |explicit| explicit, .no => null, @@ -3105,8 +2871,8 @@ fn buildOutputType( .basename = try std.zig.binNameAlloc(arena, .{ .root_name = root_name, .target = target, - .output_mode = create_module.opts.output_mode, - .link_mode = create_module.opts.link_mode, + .output_mode = create_module.resolved_options.output_mode, + .link_mode = create_module.resolved_options.link_mode, .version = optional_version, }), }, @@ -3224,9 +2990,9 @@ fn buildOutputType( }; defer emit_docs_resolved.deinit(); - const is_exe_or_dyn_lib = switch (create_module.opts.output_mode) { + const is_exe_or_dyn_lib = switch (create_module.resolved_options.output_mode) { .Obj => false, - .Lib => (create_module.opts.link_mode orelse .Static) == .Dynamic, + .Lib => create_module.resolved_options.link_mode == .Dynamic, .Exe => true, }; // Note that cmake when targeting Windows will try to execute @@ -3354,7 +3120,7 @@ fn buildOutputType( .self_exe_path = self_exe_path, .config = create_module.resolved_options, .root_name = root_name, - .sysroot = sysroot, + .sysroot = create_module.sysroot, .main_mod = main_mod, .root_mod = root_mod, .std_mod = std_mod, @@ -3365,15 +3131,15 @@ fn buildOutputType( .emit_llvm_bc = emit_llvm_bc_resolved.data, .emit_docs = emit_docs_resolved.data, .emit_implib = emit_implib_resolved.data, - .lib_dirs = lib_dirs.items, - .rpath_list = rpath_list.items, + .lib_dirs = create_module.lib_dirs.items, + .rpath_list = create_module.rpath_list.items, .symbol_wrap_set = symbol_wrap_set, .c_source_files = create_module.c_source_files.items, .rc_source_files = create_module.rc_source_files.items, .manifest_file = manifest_file, .rc_includes = rc_includes, - .link_objects = link_objects.items, - .framework_dirs = framework_dirs.items, + .link_objects = create_module.link_objects.items, + .framework_dirs = create_module.framework_dirs.items, .frameworks = resolved_frameworks.items, .system_lib_names = create_module.resolved_system_libs.items(.name), .system_lib_infos = create_module.resolved_system_libs.items(.lib), @@ -3427,7 +3193,7 @@ fn buildOutputType( .clang_passthrough_mode = clang_passthrough_mode, .clang_preprocessor_mode = clang_preprocessor_mode, .version = optional_version, - .libc_installation = if (libc_installation) |*lci| lci else null, + .libc_installation = if (create_module.libc_installation) |*lci| lci else null, .verbose_cc = verbose_cc, .verbose_link = verbose_link, .verbose_air = verbose_air, @@ -3458,6 +3224,7 @@ fn buildOutputType( .reference_trace = reference_trace, .pdb_out_path = pdb_out_path, .error_limit = error_limit, + .native_system_include_paths = create_module.native_system_include_paths, }) catch |err| switch (err) { error.LibCUnavailable => { const triple_name = try target.zigTriple(arena); @@ -3626,10 +3393,6 @@ const CreateModule = struct { /// link_libcpp, and then the libraries are filtered into /// `external_system_libs` and `resolved_system_libs`. system_libs: std.StringArrayHashMapUnmanaged(SystemLib), - external_system_libs: std.MultiArrayList(struct { - name: []const u8, - info: SystemLib, - }), resolved_system_libs: std.MultiArrayList(struct { name: []const u8, lib: Compilation.SystemLib, @@ -3643,6 +3406,17 @@ const CreateModule = struct { // This array is populated by zig cc frontend and then has to be converted to zig-style // CPU features. llvm_m_args: std.ArrayListUnmanaged([]const u8), + sysroot: ?[]const u8, + lib_dirs: std.ArrayListUnmanaged([]const u8), + lib_dir_args: std.ArrayListUnmanaged([]const u8), + libc_installation: ?LibCInstallation, + want_native_include_dirs: bool, + frameworks: std.StringArrayHashMapUnmanaged(Framework), + native_system_include_paths: []const []const u8, + framework_dirs: std.ArrayListUnmanaged([]const u8), + rpath_list: std.ArrayListUnmanaged([]const u8), + libc_paths_file: ?[]const u8, + link_objects: std.ArrayListUnmanaged(Compilation.LinkObject), }; fn createModule( @@ -3749,6 +3523,10 @@ fn createModule( // First, remove libc, libc++, and compiler_rt libraries from the system libraries list. // We need to know whether the set of system libraries contains anything besides these // to decide whether to trigger native path detection logic. + var external_system_libs: std.MultiArrayList(struct { + name: []const u8, + info: SystemLib, + }) = .{}; for (create_module.system_libs.keys(), create_module.system_libs.values()) |lib_name, info| { if (target.is_libc_lib_name(lib_name)) { create_module.opts.link_libc = true; @@ -3800,12 +3578,249 @@ fn createModule( } } - try create_module.external_system_libs.append(arena, .{ + try external_system_libs.append(arena, .{ .name = lib_name, .info = info, }); } // After this point, external_system_libs is used instead of system_libs. + if (external_system_libs.len != 0) + create_module.want_native_include_dirs = true; + + // Resolve the library path arguments with respect to sysroot. + if (create_module.sysroot) |root| { + try create_module.lib_dirs.ensureUnusedCapacity(arena, create_module.lib_dir_args.items.len * 2); + for (create_module.lib_dir_args.items) |dir| { + if (fs.path.isAbsolute(dir)) { + const stripped_dir = dir[fs.path.diskDesignator(dir).len..]; + const full_path = try fs.path.join(arena, &[_][]const u8{ root, stripped_dir }); + create_module.lib_dirs.appendAssumeCapacity(full_path); + } + create_module.lib_dirs.appendAssumeCapacity(dir); + } + } else { + create_module.lib_dirs = create_module.lib_dir_args; + } + create_module.lib_dir_args = undefined; // From here we use lib_dirs instead. + + if (resolved_target.is_native_os and target.isDarwin()) { + // If we want to link against frameworks, we need system headers. + if (create_module.frameworks.count() > 0) + create_module.want_native_include_dirs = true; + } + + // Trigger native system library path detection if necessary. + if (create_module.sysroot == null and + resolved_target.is_native_os and resolved_target.is_native_abi and + create_module.want_native_include_dirs) + { + var paths = std.zig.system.NativePaths.detect(arena, target) catch |err| { + fatal("unable to detect native system paths: {s}", .{@errorName(err)}); + }; + for (paths.warnings.items) |warning| { + warn("{s}", .{warning}); + } + + create_module.native_system_include_paths = try paths.include_dirs.toOwnedSlice(arena); + + try create_module.framework_dirs.appendSlice(arena, paths.framework_dirs.items); + try create_module.lib_dirs.appendSlice(arena, paths.lib_dirs.items); + try create_module.rpath_list.appendSlice(arena, paths.rpaths.items); + } + + if (create_module.libc_paths_file) |paths_file| { + create_module.libc_installation = LibCInstallation.parse(arena, paths_file, target) catch |err| { + fatal("unable to parse libc paths file at path {s}: {s}", .{ + paths_file, @errorName(err), + }); + }; + } + + if (builtin.target.os.tag == .windows and target.abi == .msvc and + external_system_libs.len != 0) + { + if (create_module.libc_installation == null) { + create_module.libc_installation = try LibCInstallation.findNative(.{ + .allocator = arena, + .verbose = true, + .target = target, + }); + + try create_module.lib_dirs.appendSlice(arena, &.{ + create_module.libc_installation.?.msvc_lib_dir.?, + create_module.libc_installation.?.kernel32_lib_dir.?, + }); + } + } + + // If any libs in this list are statically provided, we omit them from the + // resolved list and populate the link_objects array instead. + { + var test_path = std.ArrayList(u8).init(gpa); + defer test_path.deinit(); + + var checked_paths = std.ArrayList(u8).init(gpa); + defer checked_paths.deinit(); + + var failed_libs = std.ArrayList(struct { + name: []const u8, + strategy: SystemLib.SearchStrategy, + checked_paths: []const u8, + preferred_mode: std.builtin.LinkMode, + }).init(arena); + + syslib: for (external_system_libs.items(.name), external_system_libs.items(.info)) |lib_name, info| { + // Checked in the first pass above while looking for libc libraries. + assert(!fs.path.isAbsolute(lib_name)); + + checked_paths.clearRetainingCapacity(); + + switch (info.search_strategy) { + .mode_first, .no_fallback => { + // check for preferred mode + for (create_module.lib_dirs.items) |lib_dir_path| { + if (try accessLibPath( + &test_path, + &checked_paths, + lib_dir_path, + lib_name, + target, + info.preferred_mode, + )) { + const path = try arena.dupe(u8, test_path.items); + switch (info.preferred_mode) { + .Static => try create_module.link_objects.append(arena, .{ .path = path }), + .Dynamic => try create_module.resolved_system_libs.append(arena, .{ + .name = lib_name, + .lib = .{ + .needed = info.needed, + .weak = info.weak, + .path = path, + }, + }), + } + continue :syslib; + } + } + // check for fallback mode + if (info.search_strategy == .no_fallback) { + try failed_libs.append(.{ + .name = lib_name, + .strategy = info.search_strategy, + .checked_paths = try arena.dupe(u8, checked_paths.items), + .preferred_mode = info.preferred_mode, + }); + continue :syslib; + } + for (create_module.lib_dirs.items) |lib_dir_path| { + if (try accessLibPath( + &test_path, + &checked_paths, + lib_dir_path, + lib_name, + target, + info.fallbackMode(), + )) { + const path = try arena.dupe(u8, test_path.items); + switch (info.fallbackMode()) { + .Static => try create_module.link_objects.append(arena, .{ .path = path }), + .Dynamic => try create_module.resolved_system_libs.append(arena, .{ + .name = lib_name, + .lib = .{ + .needed = info.needed, + .weak = info.weak, + .path = path, + }, + }), + } + continue :syslib; + } + } + try failed_libs.append(.{ + .name = lib_name, + .strategy = info.search_strategy, + .checked_paths = try arena.dupe(u8, checked_paths.items), + .preferred_mode = info.preferred_mode, + }); + continue :syslib; + }, + .paths_first => { + for (create_module.lib_dirs.items) |lib_dir_path| { + // check for preferred mode + if (try accessLibPath( + &test_path, + &checked_paths, + lib_dir_path, + lib_name, + target, + info.preferred_mode, + )) { + const path = try arena.dupe(u8, test_path.items); + switch (info.preferred_mode) { + .Static => try create_module.link_objects.append(arena, .{ .path = path }), + .Dynamic => try create_module.resolved_system_libs.append(arena, .{ + .name = lib_name, + .lib = .{ + .needed = info.needed, + .weak = info.weak, + .path = path, + }, + }), + } + continue :syslib; + } + + // check for fallback mode + if (try accessLibPath( + &test_path, + &checked_paths, + lib_dir_path, + lib_name, + target, + info.fallbackMode(), + )) { + const path = try arena.dupe(u8, test_path.items); + switch (info.fallbackMode()) { + .Static => try create_module.link_objects.append(arena, .{ .path = path }), + .Dynamic => try create_module.resolved_system_libs.append(arena, .{ + .name = lib_name, + .lib = .{ + .needed = info.needed, + .weak = info.weak, + .path = path, + }, + }), + } + continue :syslib; + } + } + try failed_libs.append(.{ + .name = lib_name, + .strategy = info.search_strategy, + .checked_paths = try arena.dupe(u8, checked_paths.items), + .preferred_mode = info.preferred_mode, + }); + continue :syslib; + }, + } + @compileError("unreachable"); + } + + if (failed_libs.items.len > 0) { + for (failed_libs.items) |f| { + const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths; + std.log.err("unable to find {s} system library '{s}' using strategy '{s}'. searched paths:{s}", .{ + @tagName(f.preferred_mode), f.name, @tagName(f.strategy), searched_paths, + }); + } + process.exit(1); + } + } + // After this point, create_module.resolved_system_libs is used instead of + // create_module.external_system_libs. + + if (create_module.resolved_system_libs.len != 0) + create_module.opts.any_dyn_libs = true; create_module.resolved_options = Compilation.Config.resolve(create_module.opts) catch |err| switch (err) { else => fatal("unable to resolve compilation options: {s}", .{@errorName(err)}), -- 2.54.0