diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 7ace95947368..590ac5aadf6a 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -29,36 +29,7 @@ pub const Builder = Build; pub const InstallDirectoryOptions = Step.InstallDir.Options; pub const Step = @import("Build/Step.zig"); -/// deprecated: use `Step.CheckFile`. -pub const CheckFileStep = @import("Build/Step/CheckFile.zig"); -/// deprecated: use `Step.CheckObject`. -pub const CheckObjectStep = @import("Build/Step/CheckObject.zig"); -/// deprecated: use `Step.ConfigHeader`. -pub const ConfigHeaderStep = @import("Build/Step/ConfigHeader.zig"); -/// deprecated: use `Step.Fmt`. -pub const FmtStep = @import("Build/Step/Fmt.zig"); -/// deprecated: use `Step.InstallArtifact`. -pub const InstallArtifactStep = @import("Build/Step/InstallArtifact.zig"); -/// deprecated: use `Step.InstallDir`. -pub const InstallDirStep = @import("Build/Step/InstallDir.zig"); -/// deprecated: use `Step.InstallFile`. -pub const InstallFileStep = @import("Build/Step/InstallFile.zig"); -/// deprecated: use `Step.ObjCopy`. -pub const ObjCopyStep = @import("Build/Step/ObjCopy.zig"); -/// deprecated: use `Step.Compile`. -pub const CompileStep = @import("Build/Step/Compile.zig"); -/// deprecated: use `Step.Options`. -pub const OptionsStep = @import("Build/Step/Options.zig"); -/// deprecated: use `Step.RemoveDir`. -pub const RemoveDirStep = @import("Build/Step/RemoveDir.zig"); -/// deprecated: use `Step.Run`. -pub const RunStep = @import("Build/Step/Run.zig"); -/// deprecated: use `Step.TranslateC`. -pub const TranslateCStep = @import("Build/Step/TranslateC.zig"); -/// deprecated: use `Step.WriteFile`. -pub const WriteFileStep = @import("Build/Step/WriteFile.zig"); -/// deprecated: use `LazyPath`. -pub const FileSource = LazyPath; +pub const Module = @import("Build/Module.zig"); install_tls: TopLevelStep, uninstall_tls: TopLevelStep, @@ -634,34 +605,31 @@ pub const ExecutableOptions = struct { use_llvm: ?bool = null, use_lld: ?bool = null, zig_lib_dir: ?LazyPath = null, - main_mod_path: ?LazyPath = null, /// Embed a `.manifest` file in the compilation if the object format supports it. /// https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-files-reference /// Manifest files must have the extension `.manifest`. /// Can be set regardless of target. The `.manifest` file will be ignored /// if the target object format does not support embedded manifests. win32_manifest: ?LazyPath = null, - - /// Deprecated; use `main_mod_path`. - main_pkg_path: ?LazyPath = null, }; pub fn addExecutable(b: *Build, options: ExecutableOptions) *Step.Compile { return Step.Compile.create(b, .{ .name = options.name, - .root_source_file = options.root_source_file, + .root_module = .{ + .root_source_file = options.root_source_file, + .target = options.target, + .optimize = options.optimize, + .link_libc = options.link_libc, + .single_threaded = options.single_threaded, + }, .version = options.version, - .target = options.target, - .optimize = options.optimize, .kind = .exe, .linkage = options.linkage, .max_rss = options.max_rss, - .link_libc = options.link_libc, - .single_threaded = options.single_threaded, .use_llvm = options.use_llvm, .use_lld = options.use_lld, .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir, - .main_mod_path = options.main_mod_path orelse options.main_pkg_path, .win32_manifest = options.win32_manifest, }); } @@ -677,26 +645,23 @@ pub const ObjectOptions = struct { use_llvm: ?bool = null, use_lld: ?bool = null, zig_lib_dir: ?LazyPath = null, - main_mod_path: ?LazyPath = null, - - /// Deprecated; use `main_mod_path`. - main_pkg_path: ?LazyPath = null, }; pub fn addObject(b: *Build, options: ObjectOptions) *Step.Compile { return Step.Compile.create(b, .{ .name = options.name, - .root_source_file = options.root_source_file, - .target = options.target, - .optimize = options.optimize, + .root_module = .{ + .root_source_file = options.root_source_file, + .target = options.target, + .optimize = options.optimize, + .link_libc = options.link_libc, + .single_threaded = options.single_threaded, + }, .kind = .obj, .max_rss = options.max_rss, - .link_libc = options.link_libc, - .single_threaded = options.single_threaded, .use_llvm = options.use_llvm, .use_lld = options.use_lld, .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir, - .main_mod_path = options.main_mod_path orelse options.main_pkg_path, }); } @@ -712,34 +677,31 @@ pub const SharedLibraryOptions = struct { use_llvm: ?bool = null, use_lld: ?bool = null, zig_lib_dir: ?LazyPath = null, - main_mod_path: ?LazyPath = null, /// Embed a `.manifest` file in the compilation if the object format supports it. /// https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-files-reference /// Manifest files must have the extension `.manifest`. /// Can be set regardless of target. The `.manifest` file will be ignored /// if the target object format does not support embedded manifests. win32_manifest: ?LazyPath = null, - - /// Deprecated; use `main_mod_path`. - main_pkg_path: ?LazyPath = null, }; pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *Step.Compile { return Step.Compile.create(b, .{ .name = options.name, - .root_source_file = options.root_source_file, + .root_module = .{ + .target = options.target, + .optimize = options.optimize, + .root_source_file = options.root_source_file, + .link_libc = options.link_libc, + .single_threaded = options.single_threaded, + }, .kind = .lib, .linkage = .dynamic, .version = options.version, - .target = options.target, - .optimize = options.optimize, .max_rss = options.max_rss, - .link_libc = options.link_libc, - .single_threaded = options.single_threaded, .use_llvm = options.use_llvm, .use_lld = options.use_lld, .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir, - .main_mod_path = options.main_mod_path orelse options.main_pkg_path, .win32_manifest = options.win32_manifest, }); } @@ -756,28 +718,25 @@ pub const StaticLibraryOptions = struct { use_llvm: ?bool = null, use_lld: ?bool = null, zig_lib_dir: ?LazyPath = null, - main_mod_path: ?LazyPath = null, - - /// Deprecated; use `main_mod_path`. - main_pkg_path: ?LazyPath = null, }; pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *Step.Compile { return Step.Compile.create(b, .{ .name = options.name, - .root_source_file = options.root_source_file, + .root_module = .{ + .target = options.target, + .optimize = options.optimize, + .root_source_file = options.root_source_file, + .link_libc = options.link_libc, + .single_threaded = options.single_threaded, + }, .kind = .lib, .linkage = .static, .version = options.version, - .target = options.target, - .optimize = options.optimize, .max_rss = options.max_rss, - .link_libc = options.link_libc, - .single_threaded = options.single_threaded, .use_llvm = options.use_llvm, .use_lld = options.use_lld, .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir, - .main_mod_path = options.main_mod_path orelse options.main_pkg_path, }); } @@ -795,28 +754,25 @@ pub const TestOptions = struct { use_llvm: ?bool = null, use_lld: ?bool = null, zig_lib_dir: ?LazyPath = null, - main_mod_path: ?LazyPath = null, - - /// Deprecated; use `main_mod_path`. - main_pkg_path: ?LazyPath = null, }; pub fn addTest(b: *Build, options: TestOptions) *Step.Compile { return Step.Compile.create(b, .{ .name = options.name, .kind = .@"test", - .root_source_file = options.root_source_file, - .target = options.target, - .optimize = options.optimize, + .root_module = .{ + .root_source_file = options.root_source_file, + .target = options.target, + .optimize = options.optimize, + .link_libc = options.link_libc, + .single_threaded = options.single_threaded, + }, .max_rss = options.max_rss, .filter = options.filter, .test_runner = options.test_runner, - .link_libc = options.link_libc, - .single_threaded = options.single_threaded, .use_llvm = options.use_llvm, .use_lld = options.use_lld, .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir, - .main_mod_path = options.main_mod_path orelse options.main_pkg_path, }); } @@ -833,9 +789,10 @@ pub fn addAssembly(b: *Build, options: AssemblyOptions) *Step.Compile { const obj_step = Step.Compile.create(b, .{ .name = options.name, .kind = .obj, - .root_source_file = null, - .target = options.target, - .optimize = options.optimize, + .root_module = .{ + .target = options.target, + .optimize = options.optimize, + }, .max_rss = options.max_rss, .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir, }); @@ -846,41 +803,17 @@ pub fn addAssembly(b: *Build, options: AssemblyOptions) *Step.Compile { /// This function creates a module and adds it to the package's module set, making /// it available to other packages which depend on this one. /// `createModule` can be used instead to create a private module. -pub fn addModule(b: *Build, name: []const u8, options: CreateModuleOptions) *Module { - const module = b.createModule(options); +pub fn addModule(b: *Build, name: []const u8, options: Module.CreateOptions) *Module { + const module = Module.create(b, options); b.modules.put(b.dupe(name), module) catch @panic("OOM"); return module; } -pub const ModuleDependency = struct { - name: []const u8, - module: *Module, -}; - -pub const CreateModuleOptions = struct { - source_file: LazyPath, - dependencies: []const ModuleDependency = &.{}, -}; - /// This function creates a private module, to be used by the current package, /// but not exposed to other packages depending on this one. /// `addModule` can be used instead to create a public module. -pub fn createModule(b: *Build, options: CreateModuleOptions) *Module { - const module = b.allocator.create(Module) catch @panic("OOM"); - module.* = .{ - .builder = b, - .source_file = options.source_file.dupe(b), - .dependencies = moduleDependenciesToArrayHashMap(b.allocator, options.dependencies), - }; - return module; -} - -fn moduleDependenciesToArrayHashMap(arena: Allocator, deps: []const ModuleDependency) std.StringArrayHashMap(*Module) { - var result = std.StringArrayHashMap(*Module).init(arena); - for (deps) |dep| { - result.put(dep.name, dep.module) catch @panic("OOM"); - } - return result; +pub fn createModule(b: *Build, options: Module.CreateOptions) *Module { + return Module.create(b, options); } /// Initializes a `Step.Run` with argv, which must at least have the path to the @@ -1885,15 +1818,6 @@ pub fn runBuild(b: *Build, build_zig: anytype) anyerror!void { } } -pub const Module = struct { - builder: *Build, - /// This could either be a generated file, in which case the module - /// contains exactly one file, or it could be a path to the root source - /// file of directory of files which constitute the module. - source_file: LazyPath, - dependencies: std.StringArrayHashMap(*Module), -}; - /// A file that is generated by a build step. /// This struct is an interface that is meant to be used with `@fieldParentPtr` to implement the actual path logic. pub const GeneratedFile = struct { diff --git a/lib/std/Build/Module.zig b/lib/std/Build/Module.zig new file mode 100644 index 000000000000..31db0ad6894b --- /dev/null +++ b/lib/std/Build/Module.zig @@ -0,0 +1,616 @@ +/// The one responsible for creating this module. +owner: *std.Build, +/// Tracks the set of steps that depend on this `Module`. This ensures that +/// when making this `Module` depend on other `Module` objects and `Step` +/// objects, respective `Step` dependencies can be added. +depending_steps: std.AutoArrayHashMapUnmanaged(*std.Build.Step.Compile, void), +/// This could either be a generated file, in which case the module +/// contains exactly one file, or it could be a path to the root source +/// file of directory of files which constitute the module. +/// If `null`, it means this module is made up of only `link_objects`. +root_source_file: ?LazyPath, +/// The modules that are mapped into this module's import table. +import_table: std.StringArrayHashMap(*Module), + +target: std.zig.CrossTarget, +target_info: NativeTargetInfo, +optimize: std.builtin.OptimizeMode, +dwarf_format: ?std.dwarf.Format, + +c_macros: std.ArrayList([]const u8), +include_dirs: std.ArrayList(IncludeDir), +lib_paths: std.ArrayList(LazyPath), +rpaths: std.ArrayList(LazyPath), +frameworks: std.StringArrayHashMapUnmanaged(FrameworkLinkInfo), +c_std: std.Build.CStd, +link_objects: std.ArrayList(LinkObject), + +strip: ?bool, +unwind_tables: ?bool, +single_threaded: ?bool, +stack_protector: ?bool, +stack_check: ?bool, +sanitize_c: ?bool, +sanitize_thread: ?bool, +code_model: std.builtin.CodeModel, +/// Whether to emit machine code that integrates with Valgrind. +valgrind: ?bool, +/// Position Independent Code +pic: ?bool, +red_zone: ?bool, +/// Whether to omit the stack frame pointer. Frees up a register and makes it +/// more more difficiult to obtain stack traces. Has target-dependent effects. +omit_frame_pointer: ?bool, +/// `true` requires a compilation that includes this Module to link libc. +/// `false` causes a build failure if a compilation that includes this Module would link libc. +/// `null` neither requires nor prevents libc from being linked. +link_libc: ?bool, +/// `true` requires a compilation that includes this Module to link libc++. +/// `false` causes a build failure if a compilation that includes this Module would link libc++. +/// `null` neither requires nor prevents libc++ from being linked. +link_libcpp: ?bool, + +/// Symbols to be exported when compiling to WebAssembly. +export_symbol_names: []const []const u8 = &.{}, + +pub const LinkObject = union(enum) { + static_path: LazyPath, + other_step: *std.Build.Step.Compile, + system_lib: SystemLib, + assembly_file: LazyPath, + c_source_file: *CSourceFile, + c_source_files: *CSourceFiles, + win32_resource_file: *RcSourceFile, +}; + +pub const SystemLib = struct { + name: []const u8, + needed: bool, + weak: bool, + use_pkg_config: UsePkgConfig, + preferred_link_mode: std.builtin.LinkMode, + search_strategy: SystemLib.SearchStrategy, + + pub const UsePkgConfig = enum { + /// Don't use pkg-config, just pass -lfoo where foo is name. + no, + /// Try to get information on how to link the library from pkg-config. + /// If that fails, fall back to passing -lfoo where foo is name. + yes, + /// Try to get information on how to link the library from pkg-config. + /// If that fails, error out. + force, + }; + + pub const SearchStrategy = enum { paths_first, mode_first, no_fallback }; +}; + +pub const CSourceFiles = struct { + dependency: ?*std.Build.Dependency, + /// If `dependency` is not null relative to it, + /// else relative to the build root. + files: []const []const u8, + flags: []const []const u8, +}; + +pub const CSourceFile = struct { + file: LazyPath, + flags: []const []const u8, + + pub fn dupe(self: CSourceFile, b: *std.Build) CSourceFile { + return .{ + .file = self.file.dupe(b), + .flags = b.dupeStrings(self.flags), + }; + } +}; + +pub const RcSourceFile = struct { + file: LazyPath, + /// Any option that rc.exe accepts will work here, with the exception of: + /// - `/fo`: The output filename is set by the build system + /// - `/p`: Only running the preprocessor is not supported in this context + /// - `/:no-preprocess` (non-standard option): Not supported in this context + /// - Any MUI-related option + /// https://learn.microsoft.com/en-us/windows/win32/menurc/using-rc-the-rc-command-line- + /// + /// Implicitly defined options: + /// /x (ignore the INCLUDE environment variable) + /// /D_DEBUG or /DNDEBUG depending on the optimization mode + flags: []const []const u8 = &.{}, + + pub fn dupe(self: RcSourceFile, b: *std.Build) RcSourceFile { + return .{ + .file = self.file.dupe(b), + .flags = b.dupeStrings(self.flags), + }; + } +}; + +pub const IncludeDir = union(enum) { + path: LazyPath, + path_system: LazyPath, + path_after: LazyPath, + framework_path: LazyPath, + framework_path_system: LazyPath, + other_step: *std.Build.Step.Compile, + config_header_step: *std.Build.Step.ConfigHeader, +}; + +pub const FrameworkLinkInfo = struct { + needed: bool = false, + weak: bool = false, +}; + +pub const CreateOptions = struct { + target: std.zig.CrossTarget, + target_info: ?NativeTargetInfo = null, + optimize: std.builtin.OptimizeMode, + root_source_file: ?LazyPath = null, + import_table: []const Import = &.{}, + link_libc: ?bool = null, + link_libcpp: ?bool = null, + single_threaded: ?bool = null, + strip: ?bool = null, + unwind_tables: ?bool = null, + dwarf_format: ?std.dwarf.Format = null, + c_std: std.Build.CStd = .C99, + code_model: std.builtin.CodeModel = .default, + stack_protector: ?bool = null, + stack_check: ?bool = null, + sanitize_c: ?bool = null, + sanitize_thread: ?bool = null, + valgrind: ?bool = null, + pic: ?bool = null, + red_zone: ?bool = null, + /// Whether to omit the stack frame pointer. Frees up a register and makes it + /// more more difficiult to obtain stack traces. Has target-dependent effects. + omit_frame_pointer: ?bool = null, +}; + +pub const Import = struct { + name: []const u8, + module: *Module, +}; + +pub fn init(owner: *std.Build, options: CreateOptions, compile: ?*std.Build.Step.Compile) Module { + var m: Module = .{ + .owner = owner, + .depending_steps = .{}, + .root_source_file = if (options.root_source_file) |lp| lp.dupe(owner) else null, + .import_table = std.StringArrayHashMap(*Module).init(owner.allocator), + .target = options.target, + .target_info = options.target_info orelse + NativeTargetInfo.detect(options.target) catch @panic("unhandled error"), + .optimize = options.optimize, + .link_libc = options.link_libc, + .link_libcpp = options.link_libcpp, + .dwarf_format = options.dwarf_format, + .c_macros = std.ArrayList([]const u8).init(owner.allocator), + .include_dirs = std.ArrayList(IncludeDir).init(owner.allocator), + .lib_paths = std.ArrayList(LazyPath).init(owner.allocator), + .rpaths = std.ArrayList(LazyPath).init(owner.allocator), + .frameworks = .{}, + .c_std = options.c_std, + .link_objects = std.ArrayList(LinkObject).init(owner.allocator), + .strip = options.strip, + .unwind_tables = options.unwind_tables, + .single_threaded = options.single_threaded, + .stack_protector = options.stack_protector, + .stack_check = options.stack_check, + .sanitize_c = options.sanitize_c, + .sanitize_thread = options.sanitize_thread, + .code_model = options.code_model, + .valgrind = options.valgrind, + .pic = options.pic, + .red_zone = options.red_zone, + .omit_frame_pointer = options.omit_frame_pointer, + .export_symbol_names = &.{}, + }; + + if (compile) |c| { + m.depending_steps.put(owner.allocator, c, {}) catch @panic("OOM"); + } + + m.import_table.ensureUnusedCapacity(options.import_table.len) catch @panic("OOM"); + for (options.import_table) |dep| { + m.import_table.putAssumeCapacity(dep.name, dep.module); + } + + var it = m.iterateDependencies(null); + while (it.next()) |item| addShallowDependencies(&m, item.module); + + return m; +} + +pub fn create(owner: *std.Build, options: CreateOptions) *Module { + const m = owner.allocator.create(Module) catch @panic("OOM"); + m.* = init(owner, options, null); + return m; +} + +/// Adds an existing module to be used with `@import`. +pub fn addImport(m: *Module, name: []const u8, module: *Module) void { + const b = m.owner; + m.import_table.put(b.dupe(name), module) catch @panic("OOM"); + + var it = module.iterateDependencies(null); + while (it.next()) |item| addShallowDependencies(m, item.module); +} + +/// Creates step dependencies and updates `depending_steps` of `dependee` so that +/// subsequent calls to `addImport` on `dependee` will additionally create step +/// dependencies on `m`'s `depending_steps`. +fn addShallowDependencies(m: *Module, dependee: *Module) void { + if (dependee.root_source_file) |lazy_path| addLazyPathDependencies(m, dependee, lazy_path); + for (dependee.lib_paths.items) |lib_path| addLazyPathDependencies(m, dependee, lib_path); + for (dependee.rpaths.items) |rpath| addLazyPathDependencies(m, dependee, rpath); + + for (dependee.link_objects.items) |link_object| switch (link_object) { + .other_step => |compile| addStepDependencies(m, dependee, &compile.step), + + .static_path, + .assembly_file, + => |lp| addLazyPathDependencies(m, dependee, lp), + + .c_source_file => |x| addLazyPathDependencies(m, dependee, x.file), + .win32_resource_file => |x| addLazyPathDependencies(m, dependee, x.file), + + .c_source_files, + .system_lib, + => {}, + }; +} + +fn addLazyPathDependencies(m: *Module, module: *Module, lazy_path: LazyPath) void { + addLazyPathDependenciesOnly(m, lazy_path); + if (m != module) { + for (m.depending_steps.keys()) |compile| { + module.depending_steps.put(m.owner.allocator, compile, {}) catch @panic("OOM"); + } + } +} + +fn addLazyPathDependenciesOnly(m: *Module, lazy_path: LazyPath) void { + for (m.depending_steps.keys()) |compile| { + lazy_path.addStepDependencies(&compile.step); + } +} + +fn addStepDependencies(m: *Module, module: *Module, dependee: *std.Build.Step) void { + addStepDependenciesOnly(m, dependee); + if (m != module) { + for (m.depending_steps.keys()) |compile| { + module.depending_steps.put(m.owner.allocator, compile, {}) catch @panic("OOM"); + } + } +} + +fn addStepDependenciesOnly(m: *Module, dependee: *std.Build.Step) void { + for (m.depending_steps.keys()) |compile| { + compile.step.dependOn(dependee); + } +} + +/// Creates a new module and adds it to be used with `@import`. +pub fn addAnonymousImport(m: *Module, name: []const u8, options: std.Build.CreateModuleOptions) void { + const b = m.step.owner; + const module = b.createModule(options); + return addImport(m, name, module); +} + +pub fn addOptions(m: *Module, module_name: []const u8, options: *std.Build.Step.Options) void { + addImport(m, module_name, options.createModule()); +} + +pub const DependencyIterator = struct { + allocator: std.mem.Allocator, + index: usize, + set: std.AutoArrayHashMapUnmanaged(Key, []const u8), + + pub const Key = struct { + /// The compilation that contains the `Module`. Note that a `Module` might be + /// used by more than one compilation. + compile: ?*std.Build.Step.Compile, + module: *Module, + }; + + pub const Item = struct { + /// The compilation that contains the `Module`. Note that a `Module` might be + /// used by more than one compilation. + compile: ?*std.Build.Step.Compile, + module: *Module, + name: []const u8, + }; + + pub fn deinit(it: *DependencyIterator) void { + it.set.deinit(it.allocator); + it.* = undefined; + } + + pub fn next(it: *DependencyIterator) ?Item { + if (it.index >= it.set.count()) { + it.set.clearAndFree(it.allocator); + return null; + } + const key = it.set.keys()[it.index]; + const name = it.set.values()[it.index]; + it.index += 1; + const module = key.module; + it.set.ensureUnusedCapacity(it.allocator, module.import_table.count()) catch + @panic("OOM"); + for (module.import_table.keys(), module.import_table.values()) |dep_name, dep| { + it.set.putAssumeCapacity(.{ + .module = dep, + .compile = key.compile, + }, dep_name); + } + + if (key.compile != null) { + for (module.link_objects.items) |link_object| switch (link_object) { + .other_step => |compile| { + it.set.put(it.allocator, .{ + .module = &compile.root_module, + .compile = compile, + }, "root") catch @panic("OOM"); + }, + else => {}, + }; + } + + return .{ + .compile = key.compile, + .module = key.module, + .name = name, + }; + } +}; + +pub fn iterateDependencies( + m: *Module, + chase_steps: ?*std.Build.Step.Compile, +) DependencyIterator { + var it: DependencyIterator = .{ + .allocator = m.owner.allocator, + .index = 0, + .set = .{}, + }; + it.set.ensureUnusedCapacity(m.owner.allocator, m.import_table.count() + 1) catch @panic("OOM"); + it.set.putAssumeCapacity(.{ + .module = m, + .compile = chase_steps, + }, "root"); + return it; +} + +pub const LinkSystemLibraryOptions = struct { + needed: bool = false, + weak: bool = false, + use_pkg_config: SystemLib.UsePkgConfig = .yes, + preferred_link_mode: std.builtin.LinkMode = .Dynamic, + search_strategy: SystemLib.SearchStrategy = .paths_first, +}; + +pub fn linkSystemLibrary( + m: *Module, + name: []const u8, + options: LinkSystemLibraryOptions, +) void { + const b = m.owner; + if (m.target_info.target.is_libc_lib_name(name)) { + m.link_libc = true; + return; + } + if (m.target_info.target.is_libcpp_lib_name(name)) { + m.link_libcpp = true; + return; + } + + m.link_objects.append(.{ + .system_lib = .{ + .name = b.dupe(name), + .needed = options.needed, + .weak = options.weak, + .use_pkg_config = options.use_pkg_config, + .preferred_link_mode = options.preferred_link_mode, + .search_strategy = options.search_strategy, + }, + }) catch @panic("OOM"); +} + +pub const AddCSourceFilesOptions = struct { + /// When provided, `files` are relative to `dependency` rather than the + /// package that owns the `Compile` step. + dependency: ?*std.Build.Dependency = null, + files: []const []const u8, + flags: []const []const u8 = &.{}, +}; + +/// Handy when you have many C/C++ source files and want them all to have the same flags. +pub fn addCSourceFiles(m: *Module, options: AddCSourceFilesOptions) void { + const c_source_files = m.owner.allocator.create(CSourceFiles) catch @panic("OOM"); + c_source_files.* = .{ + .dependency = options.dependency, + .files = m.owner.dupeStrings(options.files), + .flags = m.owner.dupeStrings(options.flags), + }; + m.link_objects.append(.{ .c_source_files = c_source_files }) catch @panic("OOM"); +} + +pub fn addCSourceFile(m: *Module, source: CSourceFile) void { + const c_source_file = m.owner.allocator.create(CSourceFile) catch @panic("OOM"); + c_source_file.* = source.dupe(m.owner); + m.link_objects.append(.{ .c_source_file = c_source_file }) catch @panic("OOM"); + addLazyPathDependenciesOnly(m, source.file); +} + +/// Resource files must have the extension `.rc`. +/// Can be called regardless of target. The .rc file will be ignored +/// if the target object format does not support embedded resources. +pub fn addWin32ResourceFile(m: *Module, source: RcSourceFile) void { + // Only the PE/COFF format has a Resource Table, so for any other target + // the resource file is ignored. + if (m.target_info.target.ofmt != .coff) return; + + const rc_source_file = m.owner.allocator.create(RcSourceFile) catch @panic("OOM"); + rc_source_file.* = source.dupe(m.owner); + m.link_objects.append(.{ .win32_resource_file = rc_source_file }) catch @panic("OOM"); + addLazyPathDependenciesOnly(m, source.file); +} + +pub fn addAssemblyFile(m: *Module, source: LazyPath) void { + m.link_objects.append(.{ .assembly_file = source.dupe(m.owner) }) catch @panic("OOM"); + addLazyPathDependenciesOnly(m, source); +} + +pub fn addObjectFile(m: *Module, source: LazyPath) void { + m.link_objects.append(.{ .static_path = source.dupe(m.owner) }) catch @panic("OOM"); + addLazyPathDependencies(m, source); +} + +pub fn appendZigProcessFlags( + m: *Module, + zig_args: *std.ArrayList([]const u8), + asking_step: ?*std.Build.Step, +) !void { + const b = m.owner; + + try addFlag(zig_args, m.strip, "-fstrip", "-fno-strip"); + try addFlag(zig_args, m.unwind_tables, "-funwind-tables", "-fno-unwind-tables"); + try addFlag(zig_args, m.single_threaded, "-fsingle-threaded", "-fno-single-threaded"); + try addFlag(zig_args, m.stack_check, "-fstack-check", "-fno-stack-check"); + try addFlag(zig_args, m.stack_protector, "-fstack-protector", "-fno-stack-protector"); + try addFlag(zig_args, m.omit_frame_pointer, "-fomit-frame-pointer", "-fno-omit-frame-pointer"); + try addFlag(zig_args, m.sanitize_c, "-fsanitize-c", "-fno-sanitize-c"); + try addFlag(zig_args, m.sanitize_thread, "-fsanitize-thread", "-fno-sanitize-thread"); + try addFlag(zig_args, m.valgrind, "-fvalgrind", "-fno-valgrind"); + try addFlag(zig_args, m.pic, "-fPIC", "-fno-PIC"); + try addFlag(zig_args, m.red_zone, "-mred-zone", "-mno-red-zone"); + + if (m.dwarf_format) |dwarf_format| { + try zig_args.append(switch (dwarf_format) { + .@"32" => "-gdwarf32", + .@"64" => "-gdwarf64", + }); + } + + try zig_args.ensureUnusedCapacity(1); + switch (m.optimize) { + .Debug => {}, // Skip since it's the default. + .ReleaseSmall => zig_args.appendAssumeCapacity("-OReleaseSmall"), + .ReleaseFast => zig_args.appendAssumeCapacity("-OReleaseFast"), + .ReleaseSafe => zig_args.appendAssumeCapacity("-OReleaseSafe"), + } + + if (m.code_model != .default) { + try zig_args.append("-mcmodel"); + try zig_args.append(@tagName(m.code_model)); + } + + if (!m.target.isNative()) { + try zig_args.appendSlice(&.{ + "-target", try m.target.zigTriple(b.allocator), + "-mcpu", try std.Build.serializeCpu(b.allocator, m.target.getCpu()), + }); + + if (m.target.dynamic_linker.get()) |dynamic_linker| { + try zig_args.append("--dynamic-linker"); + try zig_args.append(dynamic_linker); + } + } + + for (m.export_symbol_names) |symbol_name| { + try zig_args.append(b.fmt("--export={s}", .{symbol_name})); + } + + for (m.include_dirs.items) |include_dir| { + switch (include_dir) { + .path => |include_path| { + try zig_args.append("-I"); + try zig_args.append(include_path.getPath(b)); + }, + .path_system => |include_path| { + try zig_args.append("-isystem"); + try zig_args.append(include_path.getPath(b)); + }, + .path_after => |include_path| { + try zig_args.append("-idirafter"); + try zig_args.append(include_path.getPath(b)); + }, + .framework_path => |include_path| { + try zig_args.append("-F"); + try zig_args.append(include_path.getPath2(b, asking_step)); + }, + .framework_path_system => |include_path| { + try zig_args.append("-iframework"); + try zig_args.append(include_path.getPath2(b, asking_step)); + }, + .other_step => |other| { + if (other.generated_h) |header| { + try zig_args.append("-isystem"); + try zig_args.append(std.fs.path.dirname(header.path.?).?); + } + if (other.installed_headers.items.len > 0) { + try zig_args.append("-I"); + try zig_args.append(b.pathJoin(&.{ + other.step.owner.install_prefix, "include", + })); + } + }, + .config_header_step => |config_header| { + const full_file_path = config_header.output_file.path.?; + const header_dir_path = full_file_path[0 .. full_file_path.len - config_header.include_path.len]; + try zig_args.appendSlice(&.{ "-I", header_dir_path }); + }, + } + } + + for (m.c_macros.items) |c_macro| { + try zig_args.append("-D"); + try zig_args.append(c_macro); + } + + try zig_args.ensureUnusedCapacity(2 * m.lib_paths.items.len); + for (m.lib_paths.items) |lib_path| { + zig_args.appendAssumeCapacity("-L"); + zig_args.appendAssumeCapacity(lib_path.getPath2(b, asking_step)); + } + + try zig_args.ensureUnusedCapacity(2 * m.rpaths.items.len); + for (m.rpaths.items) |rpath| { + zig_args.appendAssumeCapacity("-rpath"); + + if (m.target_info.target.isDarwin()) switch (rpath) { + .path, .cwd_relative => |path| { + // On Darwin, we should not try to expand special runtime paths such as + // * @executable_path + // * @loader_path + if (std.mem.startsWith(u8, path, "@executable_path") or + std.mem.startsWith(u8, path, "@loader_path")) + { + zig_args.appendAssumeCapacity(path); + continue; + } + }, + .generated, .dependency => {}, + }; + + zig_args.appendAssumeCapacity(rpath.getPath2(b, asking_step)); + } +} + +fn addFlag( + args: *std.ArrayList([]const u8), + opt: ?bool, + then_name: []const u8, + else_name: []const u8, +) !void { + const cond = opt orelse return; + return args.append(if (cond) then_name else else_name); +} + +const Module = @This(); +const std = @import("std"); +const assert = std.debug.assert; +const LazyPath = std.Build.LazyPath; +const NativeTargetInfo = std.zig.system.NativeTargetInfo; diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index c90f0cb1033e..31d07a43596b 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -24,36 +24,24 @@ const Compile = @This(); pub const base_id: Step.Id = .compile; step: Step, +root_module: Module, + name: []const u8, -target: CrossTarget, -target_info: NativeTargetInfo, -optimize: std.builtin.OptimizeMode, linker_script: ?LazyPath = null, version_script: ?[]const u8 = null, out_filename: []const u8, +out_lib_filename: []const u8, linkage: ?Linkage = null, version: ?std.SemanticVersion, kind: Kind, major_only_filename: ?[]const u8, name_only_filename: ?[]const u8, -strip: ?bool, -formatted_panics: ?bool = null, -unwind_tables: ?bool, // keep in sync with src/link.zig:CompressDebugSections compress_debug_sections: enum { none, zlib, zstd } = .none, -lib_paths: ArrayList(LazyPath), -rpaths: ArrayList(LazyPath), -frameworks: StringHashMap(FrameworkLinkInfo), verbose_link: bool, verbose_cc: bool, bundle_compiler_rt: ?bool = null, -single_threaded: ?bool, -stack_protector: ?bool = null, -disable_stack_probing: bool, -disable_sanitize_c: bool, -sanitize_thread: bool, rdynamic: bool, -dwarf_format: ?std.dwarf.Format = null, import_memory: bool = false, export_memory: bool = false, /// For WebAssembly targets, this will allow for undefined symbols to @@ -65,31 +53,16 @@ initial_memory: ?u64 = null, max_memory: ?u64 = null, shared_memory: bool = false, global_base: ?u64 = null, -c_std: std.Build.CStd, /// Set via options; intended to be read-only after that. zig_lib_dir: ?LazyPath, -/// Set via options; intended to be read-only after that. -main_mod_path: ?LazyPath, exec_cmd_args: ?[]const ?[]const u8, filter: ?[]const u8, test_evented_io: bool = false, test_runner: ?[]const u8, test_server_mode: bool, -code_model: std.builtin.CodeModel = .default, wasi_exec_model: ?std.builtin.WasiExecModel = null, -/// Symbols to be exported when compiling to wasm -export_symbol_names: []const []const u8 = &.{}, - -root_src: ?LazyPath, -out_lib_filename: []const u8, -modules: std.StringArrayHashMap(*Module), -link_objects: ArrayList(LinkObject), -include_dirs: ArrayList(IncludeDir), -c_macros: ArrayList([]const u8), installed_headers: ArrayList(*Step), -is_linking_libc: bool, -is_linking_libcpp: bool, vcpkg_bin_path: ?[]const u8 = null, // keep in sync with src/Compilation.zig:RcIncludes @@ -111,7 +84,6 @@ image_base: ?u64 = null, libc_file: ?LazyPath = null, -valgrind_support: ?bool = null, each_lib_rpath: ?bool = null, /// On ELF targets, this will emit a link section called ".note.gnu.build-id" /// which can be used to coordinate a stripped binary with its debug symbols. @@ -177,15 +149,9 @@ headerpad_max_install_names: bool = false, /// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols. dead_strip_dylibs: bool = false, -/// Position Independent Code -force_pic: ?bool = null, - /// Position Independent Executable pie: ?bool = null, -red_zone: ?bool = null, - -omit_frame_pointer: ?bool = null, dll_export_fns: ?bool = null, subsystem: ?std.Target.SubSystem = null, @@ -226,90 +192,16 @@ generated_h: ?*GeneratedFile, /// Defaults to `std.math.maxInt(u16)` error_limit: ?u32 = null, +/// Computed during make(). +is_linking_libc: bool = false, +/// Computed during make(). +is_linking_libcpp: bool = false, + pub const ExpectedCompileErrors = union(enum) { contains: []const u8, exact: []const []const u8, }; -pub const CSourceFiles = struct { - dependency: ?*std.Build.Dependency, - /// If `dependency` is not null relative to it, - /// else relative to the build root. - files: []const []const u8, - flags: []const []const u8, -}; - -pub const CSourceFile = struct { - file: LazyPath, - flags: []const []const u8, - - pub fn dupe(self: CSourceFile, b: *std.Build) CSourceFile { - return .{ - .file = self.file.dupe(b), - .flags = b.dupeStrings(self.flags), - }; - } -}; - -pub const RcSourceFile = struct { - file: LazyPath, - /// Any option that rc.exe accepts will work here, with the exception of: - /// - `/fo`: The output filename is set by the build system - /// - `/p`: Only running the preprocessor is not supported in this context - /// - `/:no-preprocess` (non-standard option): Not supported in this context - /// - Any MUI-related option - /// https://learn.microsoft.com/en-us/windows/win32/menurc/using-rc-the-rc-command-line- - /// - /// Implicitly defined options: - /// /x (ignore the INCLUDE environment variable) - /// /D_DEBUG or /DNDEBUG depending on the optimization mode - flags: []const []const u8 = &.{}, - - pub fn dupe(self: RcSourceFile, b: *std.Build) RcSourceFile { - return .{ - .file = self.file.dupe(b), - .flags = b.dupeStrings(self.flags), - }; - } -}; - -pub const LinkObject = union(enum) { - static_path: LazyPath, - other_step: *Compile, - system_lib: SystemLib, - assembly_file: LazyPath, - c_source_file: *CSourceFile, - c_source_files: *CSourceFiles, - win32_resource_file: *RcSourceFile, -}; - -pub const SystemLib = struct { - name: []const u8, - needed: bool, - weak: bool, - use_pkg_config: UsePkgConfig, - preferred_link_mode: std.builtin.LinkMode, - search_strategy: SystemLib.SearchStrategy, - - pub const UsePkgConfig = enum { - /// Don't use pkg-config, just pass -lfoo where foo is name. - no, - /// Try to get information on how to link the library from pkg-config. - /// If that fails, fall back to passing -lfoo where foo is name. - yes, - /// Try to get information on how to link the library from pkg-config. - /// If that fails, error out. - force, - }; - - pub const SearchStrategy = enum { paths_first, mode_first, no_fallback }; -}; - -const FrameworkLinkInfo = struct { - needed: bool = false, - weak: bool = false, -}; - const Entry = union(enum) { /// Let the compiler decide whether to make an entry point and what to name /// it. @@ -322,42 +214,24 @@ const Entry = union(enum) { symbol_name: []const u8, }; -pub const IncludeDir = union(enum) { - path: LazyPath, - path_system: LazyPath, - path_after: LazyPath, - framework_path: LazyPath, - framework_path_system: LazyPath, - other_step: *Compile, - config_header_step: *Step.ConfigHeader, -}; - pub const Options = struct { name: []const u8, - root_source_file: ?LazyPath = null, - target: CrossTarget, - optimize: std.builtin.OptimizeMode, + root_module: Module.CreateOptions, kind: Kind, linkage: ?Linkage = null, version: ?std.SemanticVersion = null, max_rss: usize = 0, filter: ?[]const u8 = null, test_runner: ?[]const u8 = null, - link_libc: ?bool = null, - single_threaded: ?bool = null, use_llvm: ?bool = null, use_lld: ?bool = null, zig_lib_dir: ?LazyPath = null, - main_mod_path: ?LazyPath = null, /// Embed a `.manifest` file in the compilation if the object format supports it. /// https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-files-reference /// Manifest files must have the extension `.manifest`. /// Can be set regardless of target. The `.manifest` file will be ignored /// if the target object format does not support embedded manifests. win32_manifest: ?LazyPath = null, - - /// deprecated; use `main_mod_path`. - main_pkg_path: ?LazyPath = null, }; pub const BuildId = union(enum) { @@ -447,7 +321,6 @@ pub const Linkage = enum { dynamic, static }; pub fn create(owner: *std.Build, options: Options) *Compile { const name = owner.dupe(options.name); - const root_src: ?LazyPath = if (options.root_source_file) |rsrc| rsrc.dupe(owner) else null; if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) { panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); } @@ -466,11 +339,12 @@ pub fn create(owner: *std.Build, options: Options) *Compile { .@"test" => "zig test", }, name_adjusted, - @tagName(options.optimize), - options.target.zigTriple(owner.allocator) catch @panic("OOM"), + @tagName(options.root_module.optimize), + options.root_module.target.zigTriple(owner.allocator) catch @panic("OOM"), }); - const target_info = NativeTargetInfo.detect(options.target) catch @panic("unhandled error"); + const target_info = NativeTargetInfo.detect(options.root_module.target) catch + @panic("unhandled error"); const out_filename = std.zig.binNameAlloc(owner.allocator, .{ .root_name = name, @@ -489,17 +363,12 @@ pub fn create(owner: *std.Build, options: Options) *Compile { const self = owner.allocator.create(Compile) catch @panic("OOM"); self.* = .{ - .strip = null, - .unwind_tables = null, + .root_module = Module.init(owner, options.root_module, self), .verbose_link = false, .verbose_cc = false, - .optimize = options.optimize, - .target = options.target, .linkage = options.linkage, .kind = options.kind, - .root_src = root_src, .name = name, - .frameworks = StringHashMap(FrameworkLinkInfo).init(owner.allocator), .step = Step.init(.{ .id = base_id, .name = step_name, @@ -512,23 +381,12 @@ pub fn create(owner: *std.Build, options: Options) *Compile { .out_lib_filename = undefined, .major_only_filename = null, .name_only_filename = null, - .modules = std.StringArrayHashMap(*Module).init(owner.allocator), - .include_dirs = ArrayList(IncludeDir).init(owner.allocator), - .link_objects = ArrayList(LinkObject).init(owner.allocator), - .c_macros = ArrayList([]const u8).init(owner.allocator), - .lib_paths = ArrayList(LazyPath).init(owner.allocator), - .rpaths = ArrayList(LazyPath).init(owner.allocator), .installed_headers = ArrayList(*Step).init(owner.allocator), - .c_std = std.Build.CStd.C99, .zig_lib_dir = null, - .main_mod_path = null, .exec_cmd_args = null, .filter = options.filter, .test_runner = options.test_runner, .test_server_mode = options.test_runner == null, - .disable_stack_probing = false, - .disable_sanitize_c = false, - .sanitize_thread = false, .rdynamic = false, .installed_path = null, .force_undefined_symbols = StringHashMap(void).init(owner.allocator), @@ -543,11 +401,6 @@ pub fn create(owner: *std.Build, options: Options) *Compile { .generated_llvm_ir = null, .generated_h = null, - .target_info = target_info, - - .is_linking_libc = options.link_libc orelse false, - .is_linking_libcpp = false, - .single_threaded = options.single_threaded, .use_llvm = options.use_llvm, .use_lld = options.use_lld, }; @@ -557,14 +410,9 @@ pub fn create(owner: *std.Build, options: Options) *Compile { lp.addStepDependencies(&self.step); } - if (options.main_mod_path orelse options.main_pkg_path) |lp| { - self.main_mod_path = lp.dupe(self.step.owner); - lp.addStepDependencies(&self.step); - } - // Only the PE/COFF format has a Resource Table which is where the manifest // gets embedded, so for any other target the manifest file is just ignored. - if (self.target.getObjectFormat() == .coff) { + if (target_info.target.ofmt == .coff) { if (options.win32_manifest) |lp| { self.win32_manifest = lp.dupe(self.step.owner); lp.addStepDependencies(&self.step); @@ -600,8 +448,6 @@ pub fn create(owner: *std.Build, options: Options) *Compile { } } - if (root_src) |rs| rs.addStepDependencies(&self.step); - return self; } @@ -738,19 +584,30 @@ pub fn linkFrameworkWeak(self: *Compile, framework_name: []const u8) void { } /// Returns whether the library, executable, or object depends on a particular system library. -pub fn dependsOnSystemLibrary(self: Compile, name: []const u8) bool { - if (isLibCLibrary(name)) { - return self.is_linking_libc; +pub fn dependsOnSystemLibrary(self: *const Compile, name: []const u8) bool { + var is_linking_libc = false; + var is_linking_libcpp = false; + + var it = self.root_module.iterateDependencies(self); + while (it.next()) |module| { + for (module.link_objects.items) |link_object| { + switch (link_object) { + .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true, + else => continue, + } + } + is_linking_libc = is_linking_libc or module.link_libcpp == true; + is_linking_libcpp = is_linking_libcpp or module.link_libcpp == true; } - if (isLibCppLibrary(name)) { - return self.is_linking_libcpp; + + if (self.root_module.target_info.target.is_libc_lib_name(name)) { + return is_linking_libc; } - for (self.link_objects.items) |link_object| { - switch (link_object) { - .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true, - else => continue, - } + + if (self.root_module.target_info.target.is_libcpp_lib_name(name)) { + return is_linking_libcpp; } + return false; } @@ -759,11 +616,11 @@ pub fn linkLibrary(self: *Compile, lib: *Compile) void { self.linkLibraryOrObject(lib); } -pub fn isDynamicLibrary(self: *Compile) bool { +pub fn isDynamicLibrary(self: *const Compile) bool { return self.kind == .lib and self.linkage == Linkage.dynamic; } -pub fn isStaticLibrary(self: *Compile) bool { +pub fn isStaticLibrary(self: *const Compile) bool { return self.kind == .lib and self.linkage != Linkage.dynamic; } @@ -777,15 +634,15 @@ pub fn producesPdbFile(self: *Compile) bool { } pub fn producesImplib(self: *Compile) bool { - return self.isDynamicLibrary() and self.target.isWindows(); + return self.isDynamicLibrary() and self.root_module.target_info.target.os.tag == .windows; } pub fn linkLibC(self: *Compile) void { - self.is_linking_libc = true; + self.root_module.link_libc = true; } pub fn linkLibCpp(self: *Compile) void { - self.is_linking_libcpp = true; + self.root_module.link_libcpp = true; } /// If the value is omitted, it is set to 1. @@ -802,31 +659,6 @@ pub fn defineCMacroRaw(self: *Compile, name_and_value: []const u8) void { self.c_macros.append(b.dupe(name_and_value)) catch @panic("OOM"); } -/// deprecated: use linkSystemLibrary2 -pub fn linkSystemLibraryName(self: *Compile, name: []const u8) void { - return linkSystemLibrary2(self, name, .{ .use_pkg_config = .no }); -} - -/// deprecated: use linkSystemLibrary2 -pub fn linkSystemLibraryNeededName(self: *Compile, name: []const u8) void { - return linkSystemLibrary2(self, name, .{ .needed = true, .use_pkg_config = .no }); -} - -/// deprecated: use linkSystemLibrary2 -pub fn linkSystemLibraryWeakName(self: *Compile, name: []const u8) void { - return linkSystemLibrary2(self, name, .{ .weak = true, .use_pkg_config = .no }); -} - -/// deprecated: use linkSystemLibrary2 -pub fn linkSystemLibraryPkgConfigOnly(self: *Compile, lib_name: []const u8) void { - return linkSystemLibrary2(self, lib_name, .{ .use_pkg_config = .force }); -} - -/// deprecated: use linkSystemLibrary2 -pub fn linkSystemLibraryNeededPkgConfigOnly(self: *Compile, lib_name: []const u8) void { - return linkSystemLibrary2(self, lib_name, .{ .needed = true, .use_pkg_config = .force }); -} - /// Run pkg-config for the given library name and parse the output, returning the arguments /// that should be passed to zig to link the given library. fn runPkgConfig(self: *Compile, lib_name: []const u8) ![]const []const u8 { @@ -924,98 +756,31 @@ fn runPkgConfig(self: *Compile, lib_name: []const u8) ![]const []const u8 { } pub fn linkSystemLibrary(self: *Compile, name: []const u8) void { - self.linkSystemLibrary2(name, .{}); -} - -/// deprecated: use linkSystemLibrary2 -pub fn linkSystemLibraryNeeded(self: *Compile, name: []const u8) void { - return linkSystemLibrary2(self, name, .{ .needed = true }); + return self.root_module.linkSystemLibrary(name, .{}); } -/// deprecated: use linkSystemLibrary2 -pub fn linkSystemLibraryWeak(self: *Compile, name: []const u8) void { - return linkSystemLibrary2(self, name, .{ .weak = true }); -} - -pub const LinkSystemLibraryOptions = struct { - needed: bool = false, - weak: bool = false, - use_pkg_config: SystemLib.UsePkgConfig = .yes, - preferred_link_mode: std.builtin.LinkMode = .Dynamic, - search_strategy: SystemLib.SearchStrategy = .paths_first, -}; - pub fn linkSystemLibrary2( self: *Compile, name: []const u8, - options: LinkSystemLibraryOptions, + options: Module.LinkSystemLibraryOptions, ) void { - const b = self.step.owner; - if (isLibCLibrary(name)) { - self.linkLibC(); - return; - } - if (isLibCppLibrary(name)) { - self.linkLibCpp(); - return; - } - - self.link_objects.append(.{ - .system_lib = .{ - .name = b.dupe(name), - .needed = options.needed, - .weak = options.weak, - .use_pkg_config = options.use_pkg_config, - .preferred_link_mode = options.preferred_link_mode, - .search_strategy = options.search_strategy, - }, - }) catch @panic("OOM"); + return self.root_module.linkSystemLibrary(name, options); } -pub const AddCSourceFilesOptions = struct { - /// When provided, `files` are relative to `dependency` rather than the package that owns the `Compile` step. - dependency: ?*std.Build.Dependency = null, - files: []const []const u8, - flags: []const []const u8 = &.{}, -}; - /// Handy when you have many C/C++ source files and want them all to have the same flags. -pub fn addCSourceFiles(self: *Compile, options: AddCSourceFilesOptions) void { - const b = self.step.owner; - const c_source_files = b.allocator.create(CSourceFiles) catch @panic("OOM"); - - const files_copy = b.dupeStrings(options.files); - const flags_copy = b.dupeStrings(options.flags); - - c_source_files.* = .{ - .dependency = options.dependency, - .files = files_copy, - .flags = flags_copy, - }; - self.link_objects.append(.{ .c_source_files = c_source_files }) catch @panic("OOM"); +pub fn addCSourceFiles(self: *Compile, options: Module.AddCSourceFilesOptions) void { + self.root_module.addCSourceFiles(options); } -pub fn addCSourceFile(self: *Compile, source: CSourceFile) void { - const b = self.step.owner; - const c_source_file = b.allocator.create(CSourceFile) catch @panic("OOM"); - c_source_file.* = source.dupe(b); - self.link_objects.append(.{ .c_source_file = c_source_file }) catch @panic("OOM"); - source.file.addStepDependencies(&self.step); +pub fn addCSourceFile(self: *Compile, source: Module.CSourceFile) void { + self.root_module.addCSourceFile(source); } /// Resource files must have the extension `.rc`. /// Can be called regardless of target. The .rc file will be ignored /// if the target object format does not support embedded resources. -pub fn addWin32ResourceFile(self: *Compile, source: RcSourceFile) void { - // Only the PE/COFF format has a Resource Table, so for any other target - // the resource file is just ignored. - if (self.target.getObjectFormat() != .coff) return; - - const b = self.step.owner; - const rc_source_file = b.allocator.create(RcSourceFile) catch @panic("OOM"); - rc_source_file.* = source.dupe(b); - self.link_objects.append(.{ .win32_resource_file = rc_source_file }) catch @panic("OOM"); - source.file.addStepDependencies(&self.step); +pub fn addWin32ResourceFile(self: *Compile, source: Module.RcSourceFile) void { + self.root_module.addWin32ResourceFile(source); } pub fn setVerboseLink(self: *Compile, value: bool) void { @@ -1112,16 +877,11 @@ pub fn getEmittedLlvmBc(self: *Compile) LazyPath { } pub fn addAssemblyFile(self: *Compile, source: LazyPath) void { - const b = self.step.owner; - const source_duped = source.dupe(b); - self.link_objects.append(.{ .assembly_file = source_duped }) catch @panic("OOM"); - source_duped.addStepDependencies(&self.step); + self.root_module.addAssemblyFile(source); } pub fn addObjectFile(self: *Compile, source: LazyPath) void { - const b = self.step.owner; - self.link_objects.append(.{ .static_path = source.dupe(b) }) catch @panic("OOM"); - source.addStepDependencies(&self.step); + self.root_module.addObjectFile(source); } pub fn addObject(self: *Compile, obj: *Compile) void { @@ -1131,19 +891,19 @@ pub fn addObject(self: *Compile, obj: *Compile) void { pub fn addAfterIncludePath(self: *Compile, path: LazyPath) void { const b = self.step.owner; - self.include_dirs.append(IncludeDir{ .path_after = path.dupe(b) }) catch @panic("OOM"); + self.include_dirs.append(.{ .path_after = path.dupe(b) }) catch @panic("OOM"); path.addStepDependencies(&self.step); } pub fn addSystemIncludePath(self: *Compile, path: LazyPath) void { const b = self.step.owner; - self.include_dirs.append(IncludeDir{ .path_system = path.dupe(b) }) catch @panic("OOM"); + self.include_dirs.append(.{ .path_system = path.dupe(b) }) catch @panic("OOM"); path.addStepDependencies(&self.step); } pub fn addIncludePath(self: *Compile, path: LazyPath) void { const b = self.step.owner; - self.include_dirs.append(IncludeDir{ .path = path.dupe(b) }) catch @panic("OOM"); + self.include_dirs.append(.{ .path = path.dupe(b) }) catch @panic("OOM"); path.addStepDependencies(&self.step); } @@ -1166,48 +926,16 @@ pub fn addRPath(self: *Compile, directory_source: LazyPath) void { pub fn addSystemFrameworkPath(self: *Compile, directory_source: LazyPath) void { const b = self.step.owner; - self.include_dirs.append(IncludeDir{ .framework_path_system = directory_source.dupe(b) }) catch @panic("OOM"); + self.include_dirs.append(.{ .framework_path_system = directory_source.dupe(b) }) catch @panic("OOM"); directory_source.addStepDependencies(&self.step); } pub fn addFrameworkPath(self: *Compile, directory_source: LazyPath) void { const b = self.step.owner; - self.include_dirs.append(IncludeDir{ .framework_path = directory_source.dupe(b) }) catch @panic("OOM"); + self.include_dirs.append(.{ .framework_path = directory_source.dupe(b) }) catch @panic("OOM"); directory_source.addStepDependencies(&self.step); } -/// Adds a module to be used with `@import` and exposing it in the current -/// package's module table using `name`. -pub fn addModule(cs: *Compile, name: []const u8, module: *Module) void { - const b = cs.step.owner; - cs.modules.put(b.dupe(name), module) catch @panic("OOM"); - - var done = std.AutoHashMap(*Module, void).init(b.allocator); - defer done.deinit(); - cs.addRecursiveBuildDeps(module, &done) catch @panic("OOM"); -} - -/// Adds a module to be used with `@import` without exposing it in the current -/// package's module table. -pub fn addAnonymousModule(cs: *Compile, name: []const u8, options: std.Build.CreateModuleOptions) void { - const b = cs.step.owner; - const module = b.createModule(options); - return addModule(cs, name, module); -} - -pub fn addOptions(cs: *Compile, module_name: []const u8, options: *Step.Options) void { - addModule(cs, module_name, options.createModule()); -} - -fn addRecursiveBuildDeps(cs: *Compile, module: *Module, done: *std.AutoHashMap(*Module, void)) !void { - if (done.contains(module)) return; - try done.put(module, {}); - module.source_file.addStepDependencies(&cs.step); - for (module.dependencies.values()) |dep| { - try cs.addRecursiveBuildDeps(dep, done); - } -} - /// If Vcpkg was found on the system, it will be added to include and lib /// paths for the specified target. pub fn addVcpkgPaths(self: *Compile, linkage: Compile.Linkage) !void { @@ -1236,7 +964,7 @@ pub fn addVcpkgPaths(self: *Compile, linkage: Compile.Linkage) !void { const include_path = b.pathJoin(&.{ root, "installed", triplet, "include" }); errdefer allocator.free(include_path); - try self.include_dirs.append(IncludeDir{ .path = .{ .path = include_path } }); + try self.include_dirs.append(.{ .path = .{ .path = include_path } }); const lib_path = b.pathJoin(&.{ root, "installed", triplet, "lib" }); try self.lib_paths.append(.{ .path = lib_path }); @@ -1270,87 +998,53 @@ fn linkLibraryOrObject(self: *Compile, other: *Compile) void { } } -fn appendModuleArgs( - cs: *Compile, - zig_args: *ArrayList([]const u8), -) error{OutOfMemory}!void { +fn appendModuleArgs(cs: *Compile, zig_args: *ArrayList([]const u8)) !void { const b = cs.step.owner; - // First, traverse the whole dependency graph and give every module a unique name, ideally one - // named after what it's called somewhere in the graph. It will help here to have both a mapping - // from module to name and a set of all the currently-used names. - var mod_names = std.AutoHashMap(*Module, []const u8).init(b.allocator); + // First, traverse the whole dependency graph and give every module a + // unique name, ideally one named after what it's called somewhere in the + // graph. It will help here to have both a mapping from module to name and + // a set of all the currently-used names. + var mod_names: std.AutoArrayHashMapUnmanaged(*Module, []const u8) = .{}; var names = std.StringHashMap(void).init(b.allocator); - var to_name = std.ArrayList(struct { - name: []const u8, - mod: *Module, - }).init(b.allocator); { - var it = cs.modules.iterator(); - while (it.next()) |kv| { + var it = cs.root_module.iterateDependencies(null); + _ = it.next(); // Skip over the root module. + while (it.next()) |item| { // While we're traversing the root dependencies, let's make sure that no module names // have colons in them, since the CLI forbids it. We handle this for transitive // dependencies further down. - if (std.mem.indexOfScalar(u8, kv.key_ptr.*, ':') != null) { - @panic("Module names cannot contain colons"); + if (std.mem.indexOfScalar(u8, item.name, ':') != null) { + return cs.step.fail("module '{s}' contains a colon", .{item.name}); } - try to_name.append(.{ - .name = kv.key_ptr.*, - .mod = kv.value_ptr.*, - }); - } - } - - while (to_name.popOrNull()) |dep| { - if (mod_names.contains(dep.mod)) continue; - // We'll use this buffer to store the name we decide on - var buf = try b.allocator.alloc(u8, dep.name.len + 32); - // First, try just the exposed dependency name - @memcpy(buf[0..dep.name.len], dep.name); - var name = buf[0..dep.name.len]; - var n: usize = 0; - while (names.contains(name)) { - // If that failed, append an incrementing number to the end - name = std.fmt.bufPrint(buf, "{s}{}", .{ dep.name, n }) catch unreachable; - n += 1; - } - - try mod_names.put(dep.mod, name); - try names.put(name, {}); - - var it = dep.mod.dependencies.iterator(); - while (it.next()) |kv| { - // Same colon-in-name check as above, but for transitive dependencies. - if (std.mem.indexOfScalar(u8, kv.key_ptr.*, ':') != null) { - @panic("Module names cannot contain colons"); + var name = item.name; + var n: usize = 0; + while (names.contains(name)) { + name = b.fmt("{s}{d}", .{ item.name, n }); + n += 1; } - try to_name.append(.{ - .name = kv.key_ptr.*, - .mod = kv.value_ptr.*, - }); + + try mod_names.put(b.allocator, item.module, name); + try names.put(name, {}); } } - // Since the module names given to the CLI are based off of the exposed names, we already know - // that none of the CLI names have colons in them, so there's no need to check that explicitly. + // Since the module names given to the CLI are based off of the exposed + // names, we already know that none of the CLI names have colons in them, + // so there's no need to check that explicitly. // Every module in the graph is now named; output their definitions - { - var it = mod_names.iterator(); - while (it.next()) |kv| { - const mod = kv.key_ptr.*; - const name = kv.value_ptr.*; - - const deps_str = try constructDepString(b.allocator, mod_names, mod.dependencies); - const src = mod.source_file.getPath(mod.builder); - try zig_args.append("--mod"); - try zig_args.append(try std.fmt.allocPrint(b.allocator, "{s}:{s}:{s}", .{ name, deps_str, src })); - } + for (mod_names.keys(), mod_names.values()) |mod, name| { + const root_src = mod.root_source_file orelse continue; + const deps_str = try constructDepString(b.allocator, mod_names, mod.import_table); + const src = root_src.getPath2(mod.owner, &cs.step); + try zig_args.append("--mod"); + try zig_args.append(b.fmt("{s}:{s}:{s}", .{ name, deps_str, src })); } // Lastly, output the root dependencies - const deps_str = try constructDepString(b.allocator, mod_names, cs.modules); + const deps_str = try constructDepString(b.allocator, mod_names, cs.root_module.import_table); if (deps_str.len > 0) { try zig_args.append("--deps"); try zig_args.append(deps_str); @@ -1359,7 +1053,7 @@ fn appendModuleArgs( fn constructDepString( allocator: std.mem.Allocator, - mod_names: std.AutoHashMap(*Module, []const u8), + mod_names: std.AutoArrayHashMapUnmanaged(*Module, []const u8), deps: std.StringArrayHashMap(*Module), ) ![]const u8 { var deps_str = std.ArrayList(u8).init(allocator); @@ -1408,10 +1102,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { const b = step.owner; const self = @fieldParentPtr(Compile, "step", step); - if (self.root_src == null and self.link_objects.items.len == 0) { - return step.fail("the linker needs one or more objects to link", .{}); - } - var zig_args = ArrayList([]const u8).init(b.allocator); defer zig_args.deinit(); @@ -1432,7 +1122,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try addFlag(&zig_args, "llvm", self.use_llvm); try addFlag(&zig_args, "lld", self.use_lld); - if (self.target.ofmt) |ofmt| { + if (self.root_module.target.ofmt) |ofmt| { try zig_args.append(try std.fmt.allocPrint(b.allocator, "-ofmt={s}", .{@tagName(ofmt)})); } @@ -1458,204 +1148,248 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append(try std.fmt.allocPrint(b.allocator, "{}", .{stack_size})); } - if (self.root_src) |root_src| try zig_args.append(root_src.getPath(b)); + { + var seen_system_libs: std.StringHashMapUnmanaged(void) = .{}; + var frameworks: std.StringArrayHashMapUnmanaged(Module.FrameworkLinkInfo) = .{}; + + var prev_has_cflags = false; + var prev_has_rcflags = false; + var prev_search_strategy: Module.SystemLib.SearchStrategy = .paths_first; + var prev_preferred_link_mode: std.builtin.LinkMode = .Dynamic; + // Track the number of positional arguments so that a nice error can be + // emitted if there is nothing to link. + var total_linker_objects: usize = 0; + + if (self.root_module.root_source_file) |lp| { + try zig_args.append(lp.getPath(b)); + total_linker_objects += 1; + } - // We will add link objects from transitive dependencies, but we want to keep - // all link objects in the same order provided. - // This array is used to keep self.link_objects immutable. - var transitive_deps: TransitiveDeps = .{ - .link_objects = ArrayList(LinkObject).init(b.allocator), - .seen_system_libs = StringHashMap(void).init(b.allocator), - .seen_steps = std.AutoHashMap(*const Step, void).init(b.allocator), - .is_linking_libcpp = self.is_linking_libcpp, - .is_linking_libc = self.is_linking_libc, - .frameworks = &self.frameworks, - }; + try self.root_module.appendZigProcessFlags(&zig_args, step); - try transitive_deps.seen_steps.put(&self.step, {}); - try transitive_deps.add(self.link_objects.items); - - var prev_has_cflags = false; - var prev_has_rcflags = false; - var prev_search_strategy: SystemLib.SearchStrategy = .paths_first; - var prev_preferred_link_mode: std.builtin.LinkMode = .Dynamic; - - for (transitive_deps.link_objects.items) |link_object| { - switch (link_object) { - .static_path => |static_path| try zig_args.append(static_path.getPath(b)), - - .other_step => |other| switch (other.kind) { - .exe => @panic("Cannot link with an executable build artifact"), - .@"test" => @panic("Cannot link with a test"), - .obj => { - try zig_args.append(other.getEmittedBin().getPath(b)); - }, - .lib => l: { - if (self.isStaticLibrary() and other.isStaticLibrary()) { - // Avoid putting a static library inside a static library. - break :l; - } + var it = self.root_module.iterateDependencies(self); + while (it.next()) |key| { + const module = key.module; + const compile = key.compile.?; + const dyn = compile.isDynamicLibrary(); - // For DLLs, we gotta link against the implib. For - // everything else, we directly link against the library file. - const full_path_lib = if (other.producesImplib()) - other.getGeneratedFilePath("generated_implib", &self.step) - else - other.getGeneratedFilePath("generated_bin", &self.step); - try zig_args.append(full_path_lib); - - if (other.linkage == Linkage.dynamic and !self.target.isWindows()) { - if (fs.path.dirname(full_path_lib)) |dirname| { - try zig_args.append("-rpath"); - try zig_args.append(dirname); - } - } - }, - }, + // Inherit dependency on libc and libc++. + if (module.link_libc == true) self.is_linking_libc = true; + if (module.link_libcpp == true) self.is_linking_libcpp = true; - .system_lib => |system_lib| { - if ((system_lib.search_strategy != prev_search_strategy or - system_lib.preferred_link_mode != prev_preferred_link_mode) and - self.linkage != .static) - { - switch (system_lib.search_strategy) { - .no_fallback => switch (system_lib.preferred_link_mode) { - .Dynamic => try zig_args.append("-search_dylibs_only"), - .Static => try zig_args.append("-search_static_only"), - }, - .paths_first => switch (system_lib.preferred_link_mode) { - .Dynamic => try zig_args.append("-search_paths_first"), - .Static => try zig_args.append("-search_paths_first_static"), - }, - .mode_first => switch (system_lib.preferred_link_mode) { - .Dynamic => try zig_args.append("-search_dylibs_first"), - .Static => try zig_args.append("-search_static_first"), - }, - } - prev_search_strategy = system_lib.search_strategy; - prev_preferred_link_mode = system_lib.preferred_link_mode; + // Inherit dependencies on darwin frameworks. + if (!dyn) { + for (module.frameworks.keys(), module.frameworks.values()) |name, info| { + try frameworks.put(b.allocator, name, info); } + } - const prefix: []const u8 = prefix: { - if (system_lib.needed) break :prefix "-needed-l"; - if (system_lib.weak) break :prefix "-weak-l"; - break :prefix "-l"; - }; - switch (system_lib.use_pkg_config) { - .no => try zig_args.append(b.fmt("{s}{s}", .{ prefix, system_lib.name })), - .yes, .force => { - if (self.runPkgConfig(system_lib.name)) |args| { - try zig_args.appendSlice(args); - } else |err| switch (err) { - error.PkgConfigInvalidOutput, - error.PkgConfigCrashed, - error.PkgConfigFailed, - error.PkgConfigNotInstalled, - error.PackageNotFound, - => switch (system_lib.use_pkg_config) { - .yes => { - // pkg-config failed, so fall back to linking the library - // by name directly. - try zig_args.append(b.fmt("{s}{s}", .{ - prefix, - system_lib.name, - })); + // Inherit dependencies on system libraries and static libraries. + total_linker_objects += module.link_objects.items.len; + for (module.link_objects.items) |link_object| { + switch (link_object) { + .static_path => |static_path| try zig_args.append(static_path.getPath(b)), + .system_lib => |system_lib| { + if ((try seen_system_libs.fetchPut(b.allocator, system_lib.name, {})) != null) + continue; + + if (dyn) + continue; + + if ((system_lib.search_strategy != prev_search_strategy or + system_lib.preferred_link_mode != prev_preferred_link_mode) and + self.linkage != .static) + { + switch (system_lib.search_strategy) { + .no_fallback => switch (system_lib.preferred_link_mode) { + .Dynamic => try zig_args.append("-search_dylibs_only"), + .Static => try zig_args.append("-search_static_only"), + }, + .paths_first => switch (system_lib.preferred_link_mode) { + .Dynamic => try zig_args.append("-search_paths_first"), + .Static => try zig_args.append("-search_paths_first_static"), }, - .force => { - panic("pkg-config failed for library {s}", .{system_lib.name}); + .mode_first => switch (system_lib.preferred_link_mode) { + .Dynamic => try zig_args.append("-search_dylibs_first"), + .Static => try zig_args.append("-search_static_first"), }, - .no => unreachable, + } + prev_search_strategy = system_lib.search_strategy; + prev_preferred_link_mode = system_lib.preferred_link_mode; + } + + const prefix: []const u8 = prefix: { + if (system_lib.needed) break :prefix "-needed-l"; + if (system_lib.weak) break :prefix "-weak-l"; + break :prefix "-l"; + }; + switch (system_lib.use_pkg_config) { + .no => try zig_args.append(b.fmt("{s}{s}", .{ prefix, system_lib.name })), + .yes, .force => { + if (self.runPkgConfig(system_lib.name)) |args| { + try zig_args.appendSlice(args); + } else |err| switch (err) { + error.PkgConfigInvalidOutput, + error.PkgConfigCrashed, + error.PkgConfigFailed, + error.PkgConfigNotInstalled, + error.PackageNotFound, + => switch (system_lib.use_pkg_config) { + .yes => { + // pkg-config failed, so fall back to linking the library + // by name directly. + try zig_args.append(b.fmt("{s}{s}", .{ + prefix, + system_lib.name, + })); + }, + .force => { + panic("pkg-config failed for library {s}", .{system_lib.name}); + }, + .no => unreachable, + }, + + else => |e| return e, + } + }, + } + }, + .other_step => |other| { + const included_in_lib = (compile.kind == .lib and other.kind == .obj); + if (dyn or included_in_lib) + continue; + + switch (other.kind) { + .exe => return step.fail("cannot link with an executable build artifact", .{}), + .@"test" => return step.fail("cannot link with a test", .{}), + .obj => { + try zig_args.append(other.getEmittedBin().getPath(b)); + }, + .lib => l: { + if (self.isStaticLibrary() and other.isStaticLibrary()) { + // Avoid putting a static library inside a static library. + break :l; + } + + // For DLLs, we gotta link against the implib. For + // everything else, we directly link against the library file. + const full_path_lib = if (other.producesImplib()) + other.getGeneratedFilePath("generated_implib", &self.step) + else + other.getGeneratedFilePath("generated_bin", &self.step); + try zig_args.append(full_path_lib); + + if (other.linkage == Linkage.dynamic and + self.root_module.target_info.target.os.tag != .windows) + { + if (fs.path.dirname(full_path_lib)) |dirname| { + try zig_args.append("-rpath"); + try zig_args.append(dirname); + } + } }, + } + }, + .assembly_file => |asm_file| { + if (prev_has_cflags) { + try zig_args.append("-cflags"); + try zig_args.append("--"); + prev_has_cflags = false; + } + try zig_args.append(asm_file.getPath(b)); + }, - else => |e| return e, + .c_source_file => |c_source_file| { + if (c_source_file.flags.len == 0) { + if (prev_has_cflags) { + try zig_args.append("-cflags"); + try zig_args.append("--"); + prev_has_cflags = false; + } + } else { + try zig_args.append("-cflags"); + for (c_source_file.flags) |arg| { + try zig_args.append(arg); + } + try zig_args.append("--"); + prev_has_cflags = true; } + try zig_args.append(c_source_file.file.getPath(b)); }, - } - }, - .assembly_file => |asm_file| { - if (prev_has_cflags) { - try zig_args.append("-cflags"); - try zig_args.append("--"); - prev_has_cflags = false; - } - try zig_args.append(asm_file.getPath(b)); - }, + .c_source_files => |c_source_files| { + if (c_source_files.flags.len == 0) { + if (prev_has_cflags) { + try zig_args.append("-cflags"); + try zig_args.append("--"); + prev_has_cflags = false; + } + } else { + try zig_args.append("-cflags"); + for (c_source_files.flags) |flag| { + try zig_args.append(flag); + } + try zig_args.append("--"); + prev_has_cflags = true; + } + if (c_source_files.dependency) |dep| { + for (c_source_files.files) |file| { + try zig_args.append(dep.builder.pathFromRoot(file)); + } + } else { + for (c_source_files.files) |file| { + try zig_args.append(b.pathFromRoot(file)); + } + } + }, - .c_source_file => |c_source_file| { - if (c_source_file.flags.len == 0) { - if (prev_has_cflags) { - try zig_args.append("-cflags"); - try zig_args.append("--"); - prev_has_cflags = false; - } - } else { - try zig_args.append("-cflags"); - for (c_source_file.flags) |arg| { - try zig_args.append(arg); - } - try zig_args.append("--"); - prev_has_cflags = true; + .win32_resource_file => |rc_source_file| { + if (rc_source_file.flags.len == 0) { + if (prev_has_rcflags) { + try zig_args.append("-rcflags"); + try zig_args.append("--"); + prev_has_rcflags = false; + } + } else { + try zig_args.append("-rcflags"); + for (rc_source_file.flags) |arg| { + try zig_args.append(arg); + } + try zig_args.append("--"); + prev_has_rcflags = true; + } + try zig_args.append(rc_source_file.file.getPath(b)); + }, } - try zig_args.append(c_source_file.file.getPath(b)); - }, + } + } - .c_source_files => |c_source_files| { - if (c_source_files.flags.len == 0) { - if (prev_has_cflags) { - try zig_args.append("-cflags"); - try zig_args.append("--"); - prev_has_cflags = false; - } - } else { - try zig_args.append("-cflags"); - for (c_source_files.flags) |flag| { - try zig_args.append(flag); - } - try zig_args.append("--"); - prev_has_cflags = true; - } - if (c_source_files.dependency) |dep| { - for (c_source_files.files) |file| { - try zig_args.append(dep.builder.pathFromRoot(file)); - } - } else { - for (c_source_files.files) |file| { - try zig_args.append(b.pathFromRoot(file)); - } - } - }, + if (total_linker_objects == 0) { + return step.fail("the linker needs one or more objects to link", .{}); + } - .win32_resource_file => |rc_source_file| { - if (rc_source_file.flags.len == 0) { - if (prev_has_rcflags) { - try zig_args.append("-rcflags"); - try zig_args.append("--"); - prev_has_rcflags = false; - } - } else { - try zig_args.append("-rcflags"); - for (rc_source_file.flags) |arg| { - try zig_args.append(arg); - } - try zig_args.append("--"); - prev_has_rcflags = true; - } - try zig_args.append(rc_source_file.file.getPath(b)); - }, + for (frameworks.keys(), frameworks.values()) |name, info| { + if (info.needed) { + try zig_args.append("-needed_framework"); + } else if (info.weak) { + try zig_args.append("-weak_framework"); + } else { + try zig_args.append("-framework"); + } + try zig_args.append(name); } - } - if (self.win32_manifest) |manifest_file| { - try zig_args.append(manifest_file.getPath(b)); - } + if (self.is_linking_libcpp) { + try zig_args.append("-lc++"); + } - if (transitive_deps.is_linking_libcpp) { - try zig_args.append("-lc++"); + if (self.is_linking_libc) { + try zig_args.append("-lc"); + } } - if (transitive_deps.is_linking_libc) { - try zig_args.append("-lc"); + if (self.win32_manifest) |manifest_file| { + try zig_args.append(manifest_file.getPath(b)); } if (self.image_base) |image_base| { @@ -1702,17 +1436,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { if (self.generated_llvm_ir != null) try zig_args.append("-femit-llvm-ir"); if (self.generated_h != null) try zig_args.append("-femit-h"); - try addFlag(&zig_args, "strip", self.strip); - try addFlag(&zig_args, "formatted-panics", self.formatted_panics); - try addFlag(&zig_args, "unwind-tables", self.unwind_tables); - - if (self.dwarf_format) |dwarf_format| { - try zig_args.append(switch (dwarf_format) { - .@"32" => "-gdwarf32", - .@"64" => "-gdwarf64", - }); - } - switch (self.compress_debug_sections) { .none => {}, .zlib => try zig_args.append("--compress-debug-sections=zlib"), @@ -1769,11 +1492,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append(libc_file); } - switch (self.optimize) { - .Debug => {}, // Skip since it's the default. - else => try zig_args.append(b.fmt("-O{s}", .{@tagName(self.optimize)})), - } - try zig_args.append("--cache-dir"); try zig_args.append(b.cache_root.path orelse "."); @@ -1793,11 +1511,11 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append(b.fmt("{}", .{version})); } - if (self.target.isDarwin()) { + if (self.root_module.target_info.target.isDarwin()) { const install_name = self.install_name orelse b.fmt("@rpath/{s}{s}{s}", .{ - self.target.libPrefix(), + self.root_module.target_info.target.libPrefix(), self.name, - self.target.dynamicLibSuffix(), + self.root_module.target_info.target.dynamicLibSuffix(), }); try zig_args.append("-install_name"); try zig_args.append(install_name); @@ -1823,27 +1541,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } try addFlag(&zig_args, "compiler-rt", self.bundle_compiler_rt); - try addFlag(&zig_args, "single-threaded", self.single_threaded); - if (self.disable_stack_probing) { - try zig_args.append("-fno-stack-check"); - } - try addFlag(&zig_args, "stack-protector", self.stack_protector); - if (self.red_zone) |red_zone| { - if (red_zone) { - try zig_args.append("-mred-zone"); - } else { - try zig_args.append("-mno-red-zone"); - } - } - try addFlag(&zig_args, "omit-frame-pointer", self.omit_frame_pointer); try addFlag(&zig_args, "dll-export-fns", self.dll_export_fns); - - if (self.disable_sanitize_c) { - try zig_args.append("-fno-sanitize-c"); - } - if (self.sanitize_thread) { - try zig_args.append("-fsanitize-thread"); - } if (self.rdynamic) { try zig_args.append("-rdynamic"); } @@ -1875,29 +1573,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append(b.fmt("--global-base={d}", .{global_base})); } - if (self.code_model != .default) { - try zig_args.append("-mcmodel"); - try zig_args.append(@tagName(self.code_model)); - } if (self.wasi_exec_model) |model| { try zig_args.append(b.fmt("-mexec-model={s}", .{@tagName(model)})); } - for (self.export_symbol_names) |symbol_name| { - try zig_args.append(b.fmt("--export={s}", .{symbol_name})); - } - - if (!self.target.isNative()) { - try zig_args.appendSlice(&.{ - "-target", try self.target.zigTriple(b.allocator), - "-mcpu", try std.Build.serializeCpu(b.allocator, self.target.getCpu()), - }); - - if (self.target.dynamic_linker.get()) |dynamic_linker| { - try zig_args.append("--dynamic-linker"); - try zig_args.append(dynamic_linker); - } - } - if (self.linker_script) |linker_script| { try zig_args.append("--script"); try zig_args.append(linker_script.getPath(b)); @@ -1923,97 +1601,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try self.appendModuleArgs(&zig_args); - for (self.include_dirs.items) |include_dir| { - switch (include_dir) { - .path => |include_path| { - try zig_args.append("-I"); - try zig_args.append(include_path.getPath(b)); - }, - .path_system => |include_path| { - try zig_args.append("-isystem"); - try zig_args.append(include_path.getPath(b)); - }, - .path_after => |include_path| { - try zig_args.append("-idirafter"); - try zig_args.append(include_path.getPath(b)); - }, - .framework_path => |include_path| { - try zig_args.append("-F"); - try zig_args.append(include_path.getPath2(b, step)); - }, - .framework_path_system => |include_path| { - try zig_args.append("-iframework"); - try zig_args.append(include_path.getPath2(b, step)); - }, - .other_step => |other| { - if (other.generated_h) |header| { - try zig_args.append("-isystem"); - try zig_args.append(fs.path.dirname(header.path.?).?); - } - if (other.installed_headers.items.len > 0) { - try zig_args.append("-I"); - try zig_args.append(b.pathJoin(&.{ - other.step.owner.install_prefix, "include", - })); - } - }, - .config_header_step => |config_header| { - const full_file_path = config_header.output_file.path.?; - const header_dir_path = full_file_path[0 .. full_file_path.len - config_header.include_path.len]; - try zig_args.appendSlice(&.{ "-I", header_dir_path }); - }, - } - } - - for (self.c_macros.items) |c_macro| { - try zig_args.append("-D"); - try zig_args.append(c_macro); - } - - try zig_args.ensureUnusedCapacity(2 * self.lib_paths.items.len); - for (self.lib_paths.items) |lib_path| { - zig_args.appendAssumeCapacity("-L"); - zig_args.appendAssumeCapacity(lib_path.getPath2(b, step)); - } - - try zig_args.ensureUnusedCapacity(2 * self.rpaths.items.len); - for (self.rpaths.items) |rpath| { - zig_args.appendAssumeCapacity("-rpath"); - - if (self.target_info.target.isDarwin()) switch (rpath) { - .path, .cwd_relative => |path| { - // On Darwin, we should not try to expand special runtime paths such as - // * @executable_path - // * @loader_path - if (mem.startsWith(u8, path, "@executable_path") or - mem.startsWith(u8, path, "@loader_path")) - { - zig_args.appendAssumeCapacity(path); - continue; - } - }, - .generated, .dependency => {}, - }; - - zig_args.appendAssumeCapacity(rpath.getPath2(b, step)); - } - - { - var it = self.frameworks.iterator(); - while (it.next()) |entry| { - const name = entry.key_ptr.*; - const info = entry.value_ptr.*; - if (info.needed) { - try zig_args.append("-needed_framework"); - } else if (info.weak) { - try zig_args.append("-weak_framework"); - } else { - try zig_args.append("-framework"); - } - try zig_args.append(name); - } - } - if (b.sysroot) |sysroot| { try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot }); } @@ -2058,7 +1645,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append(@tagName(self.rc_includes)); } - try addFlag(&zig_args, "valgrind", self.valgrind_support); try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath); if (self.build_id) |build_id| { @@ -2075,12 +1661,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append(dir.getPath(b)); } - if (self.main_mod_path) |dir| { - try zig_args.append("--main-mod-path"); - try zig_args.append(dir.getPath(b)); - } - - try addFlag(&zig_args, "PIC", self.force_pic); try addFlag(&zig_args, "PIE", self.pie); try addFlag(&zig_args, "lto", self.want_lto); @@ -2223,7 +1803,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and - self.version != null and self.target.wantSharedLibSymLinks()) + self.version != null and self.root_module.target.wantSharedLibSymLinks()) { try doAtomicSymLinks( step, @@ -2234,24 +1814,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } } -fn isLibCLibrary(name: []const u8) bool { - const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" }; - for (libc_libraries) |libc_lib_name| { - if (mem.eql(u8, name, libc_lib_name)) - return true; - } - return false; -} - -fn isLibCppLibrary(name: []const u8) bool { - const libcpp_libraries = [_][]const u8{ "c++", "stdc++" }; - for (libcpp_libraries) |libcpp_lib_name| { - if (mem.eql(u8, name, libcpp_lib_name)) - return true; - } - return false; -} - /// Returned slice must be freed by the caller. fn findVcpkgRoot(allocator: Allocator) !?[]const u8 { const appdata_path = try fs.getAppDataDir(allocator, "vcpkg"); @@ -2345,67 +1907,6 @@ fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool) } } -const TransitiveDeps = struct { - link_objects: ArrayList(LinkObject), - seen_system_libs: StringHashMap(void), - seen_steps: std.AutoHashMap(*const Step, void), - is_linking_libcpp: bool, - is_linking_libc: bool, - frameworks: *StringHashMap(FrameworkLinkInfo), - - fn add(td: *TransitiveDeps, link_objects: []const LinkObject) !void { - try td.link_objects.ensureUnusedCapacity(link_objects.len); - - for (link_objects) |link_object| { - try td.link_objects.append(link_object); - switch (link_object) { - .other_step => |other| try addInner(td, other, other.isDynamicLibrary()), - else => {}, - } - } - } - - fn addInner(td: *TransitiveDeps, other: *Compile, dyn: bool) !void { - // Inherit dependency on libc and libc++ - td.is_linking_libcpp = td.is_linking_libcpp or other.is_linking_libcpp; - td.is_linking_libc = td.is_linking_libc or other.is_linking_libc; - - // Inherit dependencies on darwin frameworks - if (!dyn) { - var it = other.frameworks.iterator(); - while (it.next()) |framework| { - try td.frameworks.put(framework.key_ptr.*, framework.value_ptr.*); - } - } - - // Inherit dependencies on system libraries and static libraries. - for (other.link_objects.items) |other_link_object| { - switch (other_link_object) { - .system_lib => |system_lib| { - if ((try td.seen_system_libs.fetchPut(system_lib.name, {})) != null) - continue; - - if (dyn) - continue; - - try td.link_objects.append(other_link_object); - }, - .other_step => |inner_other| { - if ((try td.seen_steps.fetchPut(&inner_other.step, {})) != null) - continue; - - const included_in_lib = (other.kind == .lib and inner_other.kind == .obj); - if (!dyn and !included_in_lib) - try td.link_objects.append(other_link_object); - - try addInner(td, inner_other, dyn or inner_other.isDynamicLibrary()); - }, - else => continue, - } - } - } -}; - fn checkCompileErrors(self: *Compile) !void { // Clear this field so that it does not get printed by the build runner. const actual_eb = self.step.result_error_bundle; diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig index 5555b03e94ef..855eabeae9ba 100644 --- a/lib/std/Build/Step/Run.zig +++ b/lib/std/Build/Step/Run.zig @@ -488,7 +488,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { man.hash.addBytes(file_path); }, .artifact => |artifact| { - if (artifact.target.isWindows()) { + if (artifact.root_module.target_info.target.os.tag == .windows) { // On Windows we don't have rpaths so we have to add .dll search paths to PATH self.addPathForDynLibs(artifact); } @@ -682,8 +682,9 @@ fn runCommand( else => break :interpret, } - const need_cross_glibc = exe.target.isGnuLibC() and exe.is_linking_libc; - switch (b.host.getExternalExecutor(&exe.target_info, .{ + const need_cross_glibc = exe.root_module.target_info.target.isGnuLibC() and + exe.is_linking_libc; + switch (b.host.getExternalExecutor(&exe.root_module.target_info, .{ .qemu_fixes_dl = need_cross_glibc and b.glibc_runtimes_dir != null, .link_libc = exe.is_linking_libc, })) { @@ -714,9 +715,9 @@ fn runCommand( // needs the directory to be called "i686" rather than // "x86" which is why we do it manually here. const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}"; - const cpu_arch = exe.target.getCpuArch(); - const os_tag = exe.target.getOsTag(); - const abi = exe.target.getAbi(); + const cpu_arch = exe.root_module.target_info.target.cpu.arch; + const os_tag = exe.root_module.target_info.target.os.tag; + const abi = exe.root_module.target_info.target.abi; const cpu_arch_name: []const u8 = if (cpu_arch == .x86) "i686" else @@ -769,7 +770,7 @@ fn runCommand( if (allow_skip) return error.MakeSkipped; const host_name = try b.host.target.zigTriple(b.allocator); - const foreign_name = try exe.target.zigTriple(b.allocator); + const foreign_name = try exe.root_module.target_info.target.zigTriple(b.allocator); return step.fail("the host system ({s}) is unable to execute binaries from the target ({s})", .{ host_name, foreign_name, @@ -777,7 +778,7 @@ fn runCommand( }, } - if (exe.target.isWindows()) { + if (exe.root_module.target_info.target.os.tag == .windows) { // On Windows we don't have rpaths so we have to add .dll search paths to PATH self.addPathForDynLibs(exe); } @@ -1295,15 +1296,14 @@ fn evalGeneric(self: *Run, child: *std.process.Child) !StdIoResult { fn addPathForDynLibs(self: *Run, artifact: *Step.Compile) void { const b = self.step.owner; - for (artifact.link_objects.items) |link_object| { - switch (link_object) { - .other_step => |other| { - if (other.target.isWindows() and other.isDynamicLibrary()) { - addPathDir(self, fs.path.dirname(other.getEmittedBin().getPath(b)).?); - addPathForDynLibs(self, other); - } - }, - else => {}, + var it = artifact.root_module.iterateDependencies(artifact); + while (it.next()) |item| { + const other = item.compile.?; + if (item.module == &other.root_module) { + if (item.module.target_info.target.os.tag == .windows and other.isDynamicLibrary()) { + addPathDir(self, fs.path.dirname(other.getEmittedBin().getPath(b)).?); + addPathForDynLibs(self, other); + } } } } @@ -1321,7 +1321,7 @@ fn failForeign( const b = self.step.owner; const host_name = try b.host.target.zigTriple(b.allocator); - const foreign_name = try exe.target.zigTriple(b.allocator); + const foreign_name = try exe.root_module.target_info.target.zigTriple(b.allocator); return self.step.fail( \\unable to spawn foreign binary '{s}' ({s}) on host system ({s})