diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2023-01-31 23:15:59 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-01-31 23:15:59 -0500 |
| commit | efa25e7d5bca63e83f6a653058c05dacc771d19e (patch) | |
| tree | 56b34421822584702b1647ab0eb38aec160386b4 /lib/std | |
| parent | 6f13a725a3249c7f0a0f5258ac00003cd132bf15 (diff) | |
| parent | 8d37c6f71c790faecdb6acdd2868823be2bd2496 (diff) | |
| download | zig-efa25e7d5bca63e83f6a653058c05dacc771d19e.tar.gz zig-efa25e7d5bca63e83f6a653058c05dacc771d19e.zip | |
Merge pull request #14498 from ziglang/zig-build-api
Several enhancements to the build system. Many breaking changes to the API.
* combine `std.build` and `std.build.Builder` into `std.Build`
* eliminate `setTarget` and `setBuildMode`; use an options struct for `b.addExecutable` and friends
* implement passing options to dependency packages. closes #14285
* rename `LibExeObjStep` to `CompileStep`
* move src.type.CType to std lib, use it from std.Build, this helps with populating config.h files.
Diffstat (limited to 'lib/std')
| -rw-r--r-- | lib/std/Build.zig | 1780 | ||||
| -rw-r--r-- | lib/std/Build/CheckFileStep.zig (renamed from lib/std/build/CheckFileStep.zig) | 14 | ||||
| -rw-r--r-- | lib/std/Build/CheckObjectStep.zig (renamed from lib/std/build/CheckObjectStep.zig) | 30 | ||||
| -rw-r--r-- | lib/std/Build/CompileStep.zig (renamed from lib/std/build/LibExeObjStep.zig) | 456 | ||||
| -rw-r--r-- | lib/std/Build/ConfigHeaderStep.zig (renamed from lib/std/build/ConfigHeaderStep.zig) | 85 | ||||
| -rw-r--r-- | lib/std/Build/EmulatableRunStep.zig (renamed from lib/std/build/EmulatableRunStep.zig) | 22 | ||||
| -rw-r--r-- | lib/std/Build/FmtStep.zig (renamed from lib/std/build/FmtStep.zig) | 15 | ||||
| -rw-r--r-- | lib/std/Build/InstallArtifactStep.zig (renamed from lib/std/build/InstallArtifactStep.zig) | 25 | ||||
| -rw-r--r-- | lib/std/Build/InstallDirStep.zig (renamed from lib/std/build/InstallDirStep.zig) | 14 | ||||
| -rw-r--r-- | lib/std/Build/InstallFileStep.zig (renamed from lib/std/build/InstallFileStep.zig) | 14 | ||||
| -rw-r--r-- | lib/std/Build/InstallRawStep.zig (renamed from lib/std/build/InstallRawStep.zig) | 30 | ||||
| -rw-r--r-- | lib/std/Build/LogStep.zig (renamed from lib/std/build/LogStep.zig) | 8 | ||||
| -rw-r--r-- | lib/std/Build/OptionsStep.zig (renamed from lib/std/build/OptionsStep.zig) | 88 | ||||
| -rw-r--r-- | lib/std/Build/RemoveDirStep.zig (renamed from lib/std/build/RemoveDirStep.zig) | 8 | ||||
| -rw-r--r-- | lib/std/Build/RunStep.zig (renamed from lib/std/build/RunStep.zig) | 56 | ||||
| -rw-r--r-- | lib/std/Build/Step.zig | 97 | ||||
| -rw-r--r-- | lib/std/Build/TranslateCStep.zig (renamed from lib/std/build/TranslateCStep.zig) | 72 | ||||
| -rw-r--r-- | lib/std/Build/WriteFileStep.zig (renamed from lib/std/build/WriteFileStep.zig) | 22 | ||||
| -rw-r--r-- | lib/std/build.zig | 1781 | ||||
| -rw-r--r-- | lib/std/builtin.zig | 5 | ||||
| -rw-r--r-- | lib/std/std.zig | 5 | ||||
| -rw-r--r-- | lib/std/target.zig | 553 |
22 files changed, 2898 insertions, 2282 deletions
diff --git a/lib/std/Build.zig b/lib/std/Build.zig new file mode 100644 index 0000000000..d695637fc3 --- /dev/null +++ b/lib/std/Build.zig @@ -0,0 +1,1780 @@ +const std = @import("std.zig"); +const builtin = @import("builtin"); +const io = std.io; +const fs = std.fs; +const mem = std.mem; +const debug = std.debug; +const panic = std.debug.panic; +const assert = debug.assert; +const log = std.log; +const ArrayList = std.ArrayList; +const StringHashMap = std.StringHashMap; +const Allocator = mem.Allocator; +const process = std.process; +const EnvMap = std.process.EnvMap; +const fmt_lib = std.fmt; +const File = std.fs.File; +const CrossTarget = std.zig.CrossTarget; +const NativeTargetInfo = std.zig.system.NativeTargetInfo; +const Sha256 = std.crypto.hash.sha2.Sha256; +const Build = @This(); + +/// deprecated: use `CompileStep`. +pub const LibExeObjStep = CompileStep; +/// deprecated: use `Build`. +pub const Builder = Build; +/// deprecated: use `InstallDirStep.Options` +pub const InstallDirectoryOptions = InstallDirStep.Options; + +pub const Step = @import("Build/Step.zig"); +pub const CheckFileStep = @import("Build/CheckFileStep.zig"); +pub const CheckObjectStep = @import("Build/CheckObjectStep.zig"); +pub const ConfigHeaderStep = @import("Build/ConfigHeaderStep.zig"); +pub const EmulatableRunStep = @import("Build/EmulatableRunStep.zig"); +pub const FmtStep = @import("Build/FmtStep.zig"); +pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig"); +pub const InstallDirStep = @import("Build/InstallDirStep.zig"); +pub const InstallFileStep = @import("Build/InstallFileStep.zig"); +pub const InstallRawStep = @import("Build/InstallRawStep.zig"); +pub const CompileStep = @import("Build/CompileStep.zig"); +pub const LogStep = @import("Build/LogStep.zig"); +pub const OptionsStep = @import("Build/OptionsStep.zig"); +pub const RemoveDirStep = @import("Build/RemoveDirStep.zig"); +pub const RunStep = @import("Build/RunStep.zig"); +pub const TranslateCStep = @import("Build/TranslateCStep.zig"); +pub const WriteFileStep = @import("Build/WriteFileStep.zig"); + +install_tls: TopLevelStep, +uninstall_tls: TopLevelStep, +allocator: Allocator, +user_input_options: UserInputOptionsMap, +available_options_map: AvailableOptionsMap, +available_options_list: ArrayList(AvailableOption), +verbose: bool, +verbose_link: bool, +verbose_cc: bool, +verbose_air: bool, +verbose_llvm_ir: bool, +verbose_cimport: bool, +verbose_llvm_cpu_features: bool, +/// The purpose of executing the command is for a human to read compile errors from the terminal +prominent_compile_errors: bool, +color: enum { auto, on, off } = .auto, +reference_trace: ?u32 = null, +invalid_user_input: bool, +zig_exe: []const u8, +default_step: *Step, +env_map: *EnvMap, +top_level_steps: ArrayList(*TopLevelStep), +install_prefix: []const u8, +dest_dir: ?[]const u8, +lib_dir: []const u8, +exe_dir: []const u8, +h_dir: []const u8, +install_path: []const u8, +sysroot: ?[]const u8 = null, +search_prefixes: ArrayList([]const u8), +libc_file: ?[]const u8 = null, +installed_files: ArrayList(InstalledFile), +/// Path to the directory containing build.zig. +build_root: []const u8, +cache_root: []const u8, +global_cache_root: []const u8, +/// zig lib dir +override_lib_dir: ?[]const u8, +vcpkg_root: VcpkgRoot = .unattempted, +pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null, +args: ?[][]const u8 = null, +debug_log_scopes: []const []const u8 = &.{}, +debug_compile_errors: bool = false, + +/// Experimental. Use system Darling installation to run cross compiled macOS build artifacts. +enable_darling: bool = false, +/// Use system QEMU installation to run cross compiled foreign architecture build artifacts. +enable_qemu: bool = false, +/// Darwin. Use Rosetta to run x86_64 macOS build artifacts on arm64 macOS. +enable_rosetta: bool = false, +/// Use system Wasmtime installation to run cross compiled wasm/wasi build artifacts. +enable_wasmtime: bool = false, +/// Use system Wine installation to run cross compiled Windows build artifacts. +enable_wine: bool = false, +/// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc, +/// this will be the directory $glibc-build-dir/install/glibcs +/// Given the example of the aarch64 target, this is the directory +/// that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`. +glibc_runtimes_dir: ?[]const u8 = null, + +/// Information about the native target. Computed before build() is invoked. +host: NativeTargetInfo, + +dep_prefix: []const u8 = "", + +pub const ExecError = error{ + ReadFailure, + ExitCodeFailure, + ProcessTerminated, + ExecNotSupported, +} || std.ChildProcess.SpawnError; + +pub const PkgConfigError = error{ + PkgConfigCrashed, + PkgConfigFailed, + PkgConfigNotInstalled, + PkgConfigInvalidOutput, +}; + +pub const PkgConfigPkg = struct { + name: []const u8, + desc: []const u8, +}; + +pub const CStd = enum { + C89, + C99, + C11, +}; + +const UserInputOptionsMap = StringHashMap(UserInputOption); +const AvailableOptionsMap = StringHashMap(AvailableOption); + +const AvailableOption = struct { + name: []const u8, + type_id: TypeId, + description: []const u8, + /// If the `type_id` is `enum` this provides the list of enum options + enum_options: ?[]const []const u8, +}; + +const UserInputOption = struct { + name: []const u8, + value: UserValue, + used: bool, +}; + +const UserValue = union(enum) { + flag: void, + scalar: []const u8, + list: ArrayList([]const u8), + map: StringHashMap(*const UserValue), +}; + +const TypeId = enum { + bool, + int, + float, + @"enum", + string, + list, +}; + +const TopLevelStep = struct { + pub const base_id = .top_level; + + step: Step, + description: []const u8, +}; + +pub const DirList = struct { + lib_dir: ?[]const u8 = null, + exe_dir: ?[]const u8 = null, + include_dir: ?[]const u8 = null, +}; + +pub fn create( + allocator: Allocator, + zig_exe: []const u8, + build_root: []const u8, + cache_root: []const u8, + global_cache_root: []const u8, + host: NativeTargetInfo, +) !*Build { + const env_map = try allocator.create(EnvMap); + env_map.* = try process.getEnvMap(allocator); + + const self = try allocator.create(Build); + self.* = Build{ + .zig_exe = zig_exe, + .build_root = build_root, + .cache_root = try fs.path.relative(allocator, build_root, cache_root), + .global_cache_root = global_cache_root, + .verbose = false, + .verbose_link = false, + .verbose_cc = false, + .verbose_air = false, + .verbose_llvm_ir = false, + .verbose_cimport = false, + .verbose_llvm_cpu_features = false, + .prominent_compile_errors = false, + .invalid_user_input = false, + .allocator = allocator, + .user_input_options = UserInputOptionsMap.init(allocator), + .available_options_map = AvailableOptionsMap.init(allocator), + .available_options_list = ArrayList(AvailableOption).init(allocator), + .top_level_steps = ArrayList(*TopLevelStep).init(allocator), + .default_step = undefined, + .env_map = env_map, + .search_prefixes = ArrayList([]const u8).init(allocator), + .install_prefix = undefined, + .lib_dir = undefined, + .exe_dir = undefined, + .h_dir = undefined, + .dest_dir = env_map.get("DESTDIR"), + .installed_files = ArrayList(InstalledFile).init(allocator), + .install_tls = TopLevelStep{ + .step = Step.initNoOp(.top_level, "install", allocator), + .description = "Copy build artifacts to prefix path", + }, + .uninstall_tls = TopLevelStep{ + .step = Step.init(.top_level, "uninstall", allocator, makeUninstall), + .description = "Remove build artifacts from prefix path", + }, + .override_lib_dir = null, + .install_path = undefined, + .args = null, + .host = host, + }; + try self.top_level_steps.append(&self.install_tls); + try self.top_level_steps.append(&self.uninstall_tls); + self.default_step = &self.install_tls.step; + return self; +} + +fn createChild( + parent: *Build, + dep_name: []const u8, + build_root: []const u8, + args: anytype, +) !*Build { + const child = try createChildOnly(parent, dep_name, build_root); + try applyArgs(child, args); + return child; +} + +fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: []const u8) !*Build { + const allocator = parent.allocator; + const child = try allocator.create(Build); + child.* = .{ + .allocator = allocator, + .install_tls = .{ + .step = Step.initNoOp(.top_level, "install", allocator), + .description = "Copy build artifacts to prefix path", + }, + .uninstall_tls = .{ + .step = Step.init(.top_level, "uninstall", allocator, makeUninstall), + .description = "Remove build artifacts from prefix path", + }, + .user_input_options = UserInputOptionsMap.init(allocator), + .available_options_map = AvailableOptionsMap.init(allocator), + .available_options_list = ArrayList(AvailableOption).init(allocator), + .verbose = parent.verbose, + .verbose_link = parent.verbose_link, + .verbose_cc = parent.verbose_cc, + .verbose_air = parent.verbose_air, + .verbose_llvm_ir = parent.verbose_llvm_ir, + .verbose_cimport = parent.verbose_cimport, + .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features, + .prominent_compile_errors = parent.prominent_compile_errors, + .color = parent.color, + .reference_trace = parent.reference_trace, + .invalid_user_input = false, + .zig_exe = parent.zig_exe, + .default_step = undefined, + .env_map = parent.env_map, + .top_level_steps = ArrayList(*TopLevelStep).init(allocator), + .install_prefix = undefined, + .dest_dir = parent.dest_dir, + .lib_dir = parent.lib_dir, + .exe_dir = parent.exe_dir, + .h_dir = parent.h_dir, + .install_path = parent.install_path, + .sysroot = parent.sysroot, + .search_prefixes = ArrayList([]const u8).init(allocator), + .libc_file = parent.libc_file, + .installed_files = ArrayList(InstalledFile).init(allocator), + .build_root = build_root, + .cache_root = parent.cache_root, + .global_cache_root = parent.global_cache_root, + .override_lib_dir = parent.override_lib_dir, + .debug_log_scopes = parent.debug_log_scopes, + .debug_compile_errors = parent.debug_compile_errors, + .enable_darling = parent.enable_darling, + .enable_qemu = parent.enable_qemu, + .enable_rosetta = parent.enable_rosetta, + .enable_wasmtime = parent.enable_wasmtime, + .enable_wine = parent.enable_wine, + .glibc_runtimes_dir = parent.glibc_runtimes_dir, + .host = parent.host, + .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }), + }; + try child.top_level_steps.append(&child.install_tls); + try child.top_level_steps.append(&child.uninstall_tls); + child.default_step = &child.install_tls.step; + return child; +} + +fn applyArgs(b: *Build, args: anytype) !void { + inline for (@typeInfo(@TypeOf(args)).Struct.fields) |field| { + const v = @field(args, field.name); + const T = @TypeOf(v); + switch (T) { + CrossTarget => { + try b.user_input_options.put(field.name, .{ + .name = field.name, + .value = .{ .scalar = try v.zigTriple(b.allocator) }, + .used = false, + }); + try b.user_input_options.put("cpu", .{ + .name = "cpu", + .value = .{ .scalar = try serializeCpu(b.allocator, v.getCpu()) }, + .used = false, + }); + }, + []const u8 => { + try b.user_input_options.put(field.name, .{ + .name = field.name, + .value = .{ .scalar = v }, + .used = false, + }); + }, + else => switch (@typeInfo(T)) { + .Bool => { + try b.user_input_options.put(field.name, .{ + .name = field.name, + .value = .{ .scalar = if (v) "true" else "false" }, + .used = false, + }); + }, + .Enum => { + try b.user_input_options.put(field.name, .{ + .name = field.name, + .value = .{ .scalar = @tagName(v) }, + .used = false, + }); + }, + .Int => { + try b.user_input_options.put(field.name, .{ + .name = field.name, + .value = .{ .scalar = try std.fmt.allocPrint(b.allocator, "{d}", .{v}) }, + .used = false, + }); + }, + else => @compileError("option '" ++ field.name ++ "' has unsupported type: " ++ @typeName(T)), + }, + } + } + const Hasher = std.crypto.auth.siphash.SipHash128(1, 3); + // Random bytes to make unique. Refresh this with new random bytes when + // implementation is modified in a non-backwards-compatible way. + var hash = Hasher.init("ZaEsvQ5ClaA2IdH9"); + hash.update(b.dep_prefix); + // TODO additionally update the hash with `args`. + + var digest: [16]u8 = undefined; + hash.final(&digest); + var hash_basename: [digest.len * 2]u8 = undefined; + _ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch + unreachable; + + const install_prefix = b.pathJoin(&.{ b.cache_root, "i", &hash_basename }); + b.resolveInstallPrefix(install_prefix, .{}); +} + +pub fn destroy(self: *Build) void { + self.env_map.deinit(); + self.top_level_steps.deinit(); + self.allocator.destroy(self); +} + +/// This function is intended to be called by lib/build_runner.zig, not a build.zig file. +pub fn resolveInstallPrefix(self: *Build, install_prefix: ?[]const u8, dir_list: DirList) void { + if (self.dest_dir) |dest_dir| { + self.install_prefix = install_prefix orelse "/usr"; + self.install_path = self.pathJoin(&.{ dest_dir, self.install_prefix }); + } else { + self.install_prefix = install_prefix orelse + (self.pathJoin(&.{ self.build_root, "zig-out" })); + self.install_path = self.install_prefix; + } + + var lib_list = [_][]const u8{ self.install_path, "lib" }; + var exe_list = [_][]const u8{ self.install_path, "bin" }; + var h_list = [_][]const u8{ self.install_path, "include" }; + + if (dir_list.lib_dir) |dir| { + if (std.fs.path.isAbsolute(dir)) lib_list[0] = self.dest_dir orelse ""; + lib_list[1] = dir; + } + + if (dir_list.exe_dir) |dir| { + if (std.fs.path.isAbsolute(dir)) exe_list[0] = self.dest_dir orelse ""; + exe_list[1] = dir; + } + + if (dir_list.include_dir) |dir| { + if (std.fs.path.isAbsolute(dir)) h_list[0] = self.dest_dir orelse ""; + h_list[1] = dir; + } + + self.lib_dir = self.pathJoin(&lib_list); + self.exe_dir = self.pathJoin(&exe_list); + self.h_dir = self.pathJoin(&h_list); +} + +pub fn addOptions(self: *Build) *OptionsStep { + return OptionsStep.create(self); +} + +pub const ExecutableOptions = struct { + name: []const u8, + root_source_file: ?FileSource = null, + version: ?std.builtin.Version = null, + target: CrossTarget = .{}, + optimize: std.builtin.Mode = .Debug, + linkage: ?CompileStep.Linkage = null, +}; + +pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep { + return CompileStep.create(b, .{ + .name = options.name, + .root_source_file = options.root_source_file, + .version = options.version, + .target = options.target, + .optimize = options.optimize, + .kind = .exe, + .linkage = options.linkage, + }); +} + +pub const ObjectOptions = struct { + name: []const u8, + root_source_file: ?FileSource = null, + target: CrossTarget, + optimize: std.builtin.Mode, +}; + +pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep { + return CompileStep.create(b, .{ + .name = options.name, + .root_source_file = options.root_source_file, + .target = options.target, + .optimize = options.optimize, + .kind = .obj, + }); +} + +pub const SharedLibraryOptions = struct { + name: []const u8, + root_source_file: ?FileSource = null, + version: ?std.builtin.Version = null, + target: CrossTarget, + optimize: std.builtin.Mode, +}; + +pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep { + return CompileStep.create(b, .{ + .name = options.name, + .root_source_file = options.root_source_file, + .kind = .lib, + .linkage = .dynamic, + .version = options.version, + .target = options.target, + .optimize = options.optimize, + }); +} + +pub const StaticLibraryOptions = struct { + name: []const u8, + root_source_file: ?FileSource = null, + target: CrossTarget, + optimize: std.builtin.Mode, + version: ?std.builtin.Version = null, +}; + +pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep { + return CompileStep.create(b, .{ + .name = options.name, + .root_source_file = options.root_source_file, + .kind = .lib, + .linkage = .static, + .version = options.version, + .target = options.target, + .optimize = options.optimize, + }); +} + +pub const TestOptions = struct { + name: []const u8 = "test", + kind: CompileStep.Kind = .@"test", + root_source_file: FileSource, + target: CrossTarget = .{}, + optimize: std.builtin.Mode = .Debug, + version: ?std.builtin.Version = null, +}; + +pub fn addTest(b: *Build, options: TestOptions) *CompileStep { + return CompileStep.create(b, .{ + .name = options.name, + .kind = options.kind, + .root_source_file = options.root_source_file, + .target = options.target, + .optimize = options.optimize, + }); +} + +pub const AssemblyOptions = struct { + name: []const u8, + source_file: FileSource, + target: CrossTarget, + optimize: std.builtin.Mode, +}; + +pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep { + const obj_step = CompileStep.create(b, .{ + .name = options.name, + .root_source_file = null, + .target = options.target, + .optimize = options.optimize, + }); + obj_step.addAssemblyFileSource(options.source_file.dupe(b)); + return obj_step; +} + +/// Initializes a RunStep with argv, which must at least have the path to the +/// executable. More command line arguments can be added with `addArg`, +/// `addArgs`, and `addArtifactArg`. +/// Be careful using this function, as it introduces a system dependency. +/// To run an executable built with zig build, see `CompileStep.run`. +pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep { + assert(argv.len >= 1); + const run_step = RunStep.create(self, self.fmt("run {s}", .{argv[0]})); + run_step.addArgs(argv); + return run_step; +} + +pub fn addConfigHeader( + b: *Build, + source: FileSource, + style: ConfigHeaderStep.Style, + values: anytype, +) *ConfigHeaderStep { + const config_header_step = ConfigHeaderStep.create(b, source, style); + config_header_step.addValues(values); + return config_header_step; +} + +/// Allocator.dupe without the need to handle out of memory. +pub fn dupe(self: *Build, bytes: []const u8) []u8 { + return self.allocator.dupe(u8, bytes) catch @panic("OOM"); +} + +/// Duplicates an array of strings without the need to handle out of memory. +pub fn dupeStrings(self: *Build, strings: []const []const u8) [][]u8 { + const array = self.allocator.alloc([]u8, strings.len) catch @panic("OOM"); + for (strings) |s, i| { + array[i] = self.dupe(s); + } + return array; +} + +/// Duplicates a path and converts all slashes to the OS's canonical path separator. +pub fn dupePath(self: *Build, bytes: []const u8) []u8 { + const the_copy = self.dupe(bytes); + for (the_copy) |*byte| { + switch (byte.*) { + '/', '\\' => byte.* = fs.path.sep, + else => {}, + } + } + return the_copy; +} + +/// Duplicates a package recursively. +pub fn dupePkg(self: *Build, package: Pkg) Pkg { + var the_copy = Pkg{ + .name = self.dupe(package.name), + .source = package.source.dupe(self), + }; + + if (package.dependencies) |dependencies| { + const new_dependencies = self.allocator.alloc(Pkg, dependencies.len) catch @panic("OOM"); + the_copy.dependencies = new_dependencies; + + for (dependencies) |dep_package, i| { + new_dependencies[i] = self.dupePkg(dep_package); + } + } + return the_copy; +} + +pub fn addWriteFile(self: *Build, file_path: []const u8, data: []const u8) *WriteFileStep { + const write_file_step = self.addWriteFiles(); + write_file_step.add(file_path, data); + return write_file_step; +} + +pub fn addWriteFiles(self: *Build) *WriteFileStep { + const write_file_step = self.allocator.create(WriteFileStep) catch @panic("OOM"); + write_file_step.* = WriteFileStep.init(self); + return write_file_step; +} + +pub fn addLog(self: *Build, comptime format: []const u8, args: anytype) *LogStep { + const data = self.fmt(format, args); + const log_step = self.allocator.create(LogStep) catch @panic("OOM"); + log_step.* = LogStep.init(self, data); + return log_step; +} + +pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep { + const remove_dir_step = self.allocator.create(RemoveDirStep) catch @panic("OOM"); + remove_dir_step.* = RemoveDirStep.init(self, dir_path); + return remove_dir_step; +} + +pub fn addFmt(self: *Build, paths: []const []const u8) *FmtStep { + return FmtStep.create(self, paths); +} + +pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCStep { + return TranslateCStep.create(self, options); +} + +pub fn make(self: *Build, step_names: []const []const u8) !void { + try self.makePath(self.cache_root); + + var wanted_steps = ArrayList(*Step).init(self.allocator); + defer wanted_steps.deinit(); + + if (step_names.len == 0) { + try wanted_steps.append(self.default_step); + } else { + for (step_names) |step_name| { + const s = try self.getTopLevelStepByName(step_name); + try wanted_steps.append(s); + } + } + + for (wanted_steps.items) |s| { + try self.makeOneStep(s); + } +} + +pub fn getInstallStep(self: *Build) *Step { + return &self.install_tls.step; +} + +pub fn getUninstallStep(self: *Build) *Step { + return &self.uninstall_tls.step; +} + +fn makeUninstall(uninstall_step: *Step) anyerror!void { + const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step); + const self = @fieldParentPtr(Build, "uninstall_tls", uninstall_tls); + + for (self.installed_files.items) |installed_file| { + const full_path = self.getInstallPath(installed_file.dir, installed_file.path); + if (self.verbose) { + log.info("rm {s}", .{full_path}); + } + fs.cwd().deleteTree(full_path) catch {}; + } + + // TODO remove empty directories +} + +fn makeOneStep(self: *Build, s: *Step) anyerror!void { + if (s.loop_flag) { + log.err("Dependency loop detected:\n {s}", .{s.name}); + return error.DependencyLoopDetected; + } + s.loop_flag = true; + + for (s.dependencies.items) |dep| { + self.makeOneStep(dep) catch |err| { + if (err == error.DependencyLoopDetected) { + log.err(" {s}", .{s.name}); + } + return err; + }; + } + + s.loop_flag = false; + + try s.make(); +} + +fn getTopLevelStepByName(self: *Build, name: []const u8) !*Step { + for (self.top_level_steps.items) |top_level_step| { + if (mem.eql(u8, top_level_step.step.name, name)) { + return &top_level_step.step; + } + } + log.err("Cannot run step '{s}' because it does not exist", .{name}); + return error.InvalidStepName; +} + +pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T { + const name = self.dupe(name_raw); + const description = self.dupe(description_raw); + const type_id = comptime typeToEnum(T); + const enum_options = if (type_id == .@"enum") blk: { + const fields = comptime std.meta.fields(T); + var options = ArrayList([]const u8).initCapacity(self.allocator, fields.len) catch @panic("OOM"); + + inline for (fields) |field| { + options.appendAssumeCapacity(field.name); + } + + break :blk options.toOwnedSlice() catch @panic("OOM"); + } else null; + const available_option = AvailableOption{ + .name = name, + .type_id = type_id, + .description = description, + .enum_options = enum_options, + }; + if ((self.available_options_map.fetchPut(name, available_option) catch @panic("OOM")) != null) { + panic("Option '{s}' declared twice", .{name}); + } + self.available_options_list.append(available_option) catch @panic("OOM"); + + const option_ptr = self.user_input_options.getPtr(name) orelse return null; + option_ptr.used = true; + switch (type_id) { + .bool => switch (option_ptr.value) { + .flag => return true, + .scalar => |s| { + if (mem.eql(u8, s, "true")) { + return true; + } else if (mem.eql(u8, s, "false")) { + return false; + } else { + log.err("Expected -D{s} to be a boolean, but received '{s}'\n", .{ name, s }); + self.markInvalidUserInput(); + return null; + } + }, + .list, .map => { + log.err("Expected -D{s} to be a boolean, but received a {s}.\n", .{ + name, @tagName(option_ptr.value), + }); + self.markInvalidUserInput(); + return null; + }, + }, + .int => switch (option_ptr.value) { + .flag, .list, .map => { + log.err("Expected -D{s} to be an integer, but received a {s}.\n", .{ + name, @tagName(option_ptr.value), + }); + self.markInvalidUserInput(); + return null; + }, + .scalar => |s| { + const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) { + error.Overflow => { + log.err("-D{s} value {s} cannot fit into type {s}.\n", .{ name, s, @typeName(T) }); + self.markInvalidUserInput(); + return null; + }, + else => { + log.err("Expected -D{s} to be an integer of type {s}.\n", .{ name, @typeName(T) }); + self.markInvalidUserInput(); + return null; + }, + }; + return n; + }, + }, + .float => switch (option_ptr.value) { + .flag, .map, .list => { + log.err("Expected -D{s} to be a float, but received a {s}.\n", .{ + name, @tagName(option_ptr.value), + }); + self.markInvalidUserInput(); + return null; + }, + .scalar => |s| { + const n = std.fmt.parseFloat(T, s) catch { + log.err("Expected -D{s} to be a float of type {s}.\n", .{ name, @typeName(T) }); + self.markInvalidUserInput(); + return null; + }; + return n; + }, + }, + .@"enum" => switch (option_ptr.value) { + .flag, .map, .list => { + log.err("Expected -D{s} to be an enum, but received a {s}.\n", .{ + name, @tagName(option_ptr.value), + }); + self.markInvalidUserInput(); + return null; + }, + .scalar => |s| { + if (std.meta.stringToEnum(T, s)) |enum_lit| { + return enum_lit; + } else { + log.err("Expected -D{s} to be of type {s}.\n", .{ name, @typeName(T) }); + self.markInvalidUserInput(); + return null; + } + }, + }, + .string => switch (option_ptr.value) { + .flag, .list, .map => { + log.err("Expected -D{s} to be a string, but received a {s}.\n", .{ + name, @tagName(option_ptr.value), + }); + self.markInvalidUserInput(); + return null; + }, + .scalar => |s| return s, + }, + .list => switch (option_ptr.value) { + .flag, .map => { + log.err("Expected -D{s} to be a list, but received a {s}.\n", .{ + name, @tagName(option_ptr.value), + }); + self.markInvalidUserInput(); + return null; + }, + .scalar => |s| { + return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch @panic("OOM"); + }, + .list => |lst| return lst.items, + }, + } +} + +pub fn step(self: *Build, name: []const u8, description: []const u8) *Step { + const step_info = self.allocator.create(TopLevelStep) catch @panic("OOM"); + step_info.* = TopLevelStep{ + .step = Step.initNoOp(.top_level, name, self.allocator), + .description = self.dupe(description), + }; + self.top_level_steps.append(step_info) catch @panic("OOM"); + return &step_info.step; +} + +pub const StandardOptimizeOptionOptions = struct { + preferred_optimize_mode: ?std.builtin.Mode = null, +}; + +pub fn standardOptimizeOption(self: *Build, options: StandardOptimizeOptionOptions) std.builtin.Mode { + if (options.preferred_optimize_mode) |mode| { + if (self.option(bool, "release", "optimize for end users") orelse false) { + return mode; + } else { + return .Debug; + } + } else { + return self.option( + std.builtin.Mode, + "optimize", + "prioritize performance, safety, or binary size (-O flag)", + ) orelse .Debug; + } +} + +pub const StandardTargetOptionsArgs = struct { + whitelist: ?[]const CrossTarget = null, + + default_target: CrossTarget = CrossTarget{}, +}; + +/// Exposes standard `zig build` options for choosing a target. +pub fn standardTargetOptions(self: *Build, args: StandardTargetOptionsArgs) CrossTarget { + const maybe_triple = self.option( + []const u8, + "target", + "The CPU architecture, OS, and ABI to build for", + ); + const mcpu = self.option([]const u8, "cpu", "Target CPU features to add or subtract"); + + if (maybe_triple == null and mcpu == null) { + return args.default_target; + } + + const triple = maybe_triple orelse "native"; + + var diags: CrossTarget.ParseOptions.Diagnostics = .{}; + const selected_target = CrossTarget.parse(.{ + .arch_os_abi = triple, + .cpu_features = mcpu, + .diagnostics = &diags, + }) catch |err| switch (err) { + error.UnknownCpuModel => { + log.err("Unknown CPU: '{s}'\nAvailable CPUs for architecture '{s}':", .{ + diags.cpu_name.?, + @tagName(diags.arch.?), + }); + for (diags.arch.?.allCpuModels()) |cpu| { + log.err(" {s}", .{cpu.name}); + } + self.markInvalidUserInput(); + return args.default_target; + }, + error.UnknownCpuFeature => { + log.err( + \\Unknown CPU feature: '{s}' + \\Available CPU features for architecture '{s}': + \\ + , .{ + diags.unknown_feature_name.?, + @tagName(diags.arch.?), + }); + for (diags.arch.?.allFeaturesList()) |feature| { + log.err(" {s}: {s}", .{ feature.name, feature.description }); + } + self.markInvalidUserInput(); + return args.default_target; + }, + error.UnknownOperatingSystem => { + log.err( + \\Unknown OS: '{s}' + \\Available operating systems: + \\ + , .{diags.os_name.?}); + inline for (std.meta.fields(std.Target.Os.Tag)) |field| { + log.err(" {s}", .{field.name}); + } + self.markInvalidUserInput(); + return args.default_target; + }, + else => |e| { + log.err("Unable to parse target '{s}': {s}\n", .{ triple, @errorName(e) }); + self.markInvalidUserInput(); + return args.default_target; + }, + }; + + const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch @panic("OOM"); + + if (args.whitelist) |list| whitelist_check: { + // Make sure it's a match of one of the list. + var mismatch_triple = true; + var mismatch_cpu_features = true; + var whitelist_item = CrossTarget{}; + for (list) |t| { + mismatch_cpu_features = true; + mismatch_triple = true; + + const t_triple = t.zigTriple(self.allocator) catch @panic("OOM"); + if (mem.eql(u8, t_triple, selected_canonicalized_triple)) { + mismatch_triple = false; + whitelist_item = t; + if (t.getCpuFeatures().isSuperSetOf(selected_target.getCpuFeatures())) { + mismatch_cpu_features = false; + break :whitelist_check; + } else { + break; + } + } + } + if (mismatch_triple) { + log.err("Chosen target '{s}' does not match one of the supported targets:", .{ + selected_canonicalized_triple, + }); + for (list) |t| { + const t_triple = t.zigTriple(self.allocator) catch @panic("OOM"); + log.err(" {s}", .{t_triple}); + } + } else { + assert(mismatch_cpu_features); + const whitelist_cpu = whitelist_item.getCpu(); + const selected_cpu = selected_target.getCpu(); + log.err("Chosen CPU model '{s}' does not match one of the supported targets:", .{ + selected_cpu.model.name, + }); + log.err(" Supported feature Set: ", .{}); + const all_features = whitelist_cpu.arch.allFeaturesList(); + var populated_cpu_features = whitelist_cpu.model.features; + populated_cpu_features.populateDependencies(all_features); + for (all_features) |feature, i_usize| { + const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize); + const in_cpu_set = populated_cpu_features.isEnabled(i); + if (in_cpu_set) { + log.err("{s} ", .{feature.name}); + } + } + log.err(" Remove: ", .{}); + for (all_features) |feature, i_usize| { + const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize); + const in_cpu_set = populated_cpu_features.isEnabled(i); + const in_actual_set = selected_cpu.features.isEnabled(i); + if (in_actual_set and !in_cpu_set) { + log.err("{s} ", .{feature.name}); + } + } + } + self.markInvalidUserInput(); + return args.default_target; + } + + return selected_target; +} + +pub fn addUserInputOption(self: *Build, name_raw: []const u8, value_raw: []const u8) !bool { + const name = self.dupe(name_raw); + const value = self.dupe(value_raw); + const gop = try self.user_input_options.getOrPut(name); + if (!gop.found_existing) { + gop.value_ptr.* = UserInputOption{ + .name = name, + .value = .{ .scalar = value }, + .used = false, + }; + return false; + } + + // option already exists + switch (gop.value_ptr.value) { + .scalar => |s| { + // turn it into a list + var list = ArrayList([]const u8).init(self.allocator); + try list.append(s); + try list.append(value); + try self.user_input_options.put(name, .{ + .name = name, + .value = .{ .list = list }, + .used = false, + }); + }, + .list => |*list| { + // append to the list + try list.append(value); + try self.user_input_options.put(name, .{ + .name = name, + .value = .{ .list = list.* }, + .used = false, + }); + }, + .flag => { + log.warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.", .{ name, value, name }); + return true; + }, + .map => |*map| { + _ = map; + log.warn("TODO maps as command line arguments is not implemented yet.", .{}); + return true; + }, + } + return false; +} + +pub fn addUserInputFlag(self: *Build, name_raw: []const u8) !bool { + const name = self.dupe(name_raw); + const gop = try self.user_input_options.getOrPut(name); + if (!gop.found_existing) { + gop.value_ptr.* = .{ + .name = name, + .value = .{ .flag = {} }, + .used = false, + }; + return false; + } + + // option already exists + switch (gop.value_ptr.value) { + .scalar => |s| { + log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, s }); + return true; + }, + .list, .map => { + log.err("Flag '-D{s}' conflicts with multiple options of the same name.", .{name}); + return true; + }, + .flag => {}, + } + return false; +} + +fn typeToEnum(comptime T: type) TypeId { + return switch (@typeInfo(T)) { + .Int => .int, + .Float => .float, + .Bool => .bool, + .Enum => .@"enum", + else => switch (T) { + []const u8 => .string, + []const []const u8 => .list, + else => @compileError("Unsupported type: " ++ @typeName(T)), + }, + }; +} + +fn markInvalidUserInput(self: *Build) void { + self.invalid_user_input = true; +} + +pub fn validateUserInputDidItFail(self: *Build) bool { + // make sure all args are used + var it = self.user_input_options.iterator(); + while (it.next()) |entry| { + if (!entry.value_ptr.used) { + log.err("Invalid option: -D{s}", .{entry.key_ptr.*}); + self.markInvalidUserInput(); + } + } + + return self.invalid_user_input; +} + +pub fn spawnChild(self: *Build, argv: []const []const u8) !void { + return self.spawnChildEnvMap(null, self.env_map, argv); +} + +fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { + if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd}); + for (argv) |arg| { + std.debug.print("{s} ", .{arg}); + } + std.debug.print("\n", .{}); +} + +pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void { + if (self.verbose) { + printCmd(cwd, argv); + } + + if (!std.process.can_spawn) + return error.ExecNotSupported; + + var child = std.ChildProcess.init(argv, self.allocator); + child.cwd = cwd; + child.env_map = env_map; + + const term = child.spawnAndWait() catch |err| { + log.err("Unable to spawn {s}: {s}", .{ argv[0], @errorName(err) }); + return err; + }; + + switch (term) { + .Exited => |code| { + if (code != 0) { + log.err("The following command exited with error code {}:", .{code}); + printCmd(cwd, argv); + return error.UncleanExit; + } + }, + else => { + log.err("The following command terminated unexpectedly:", .{}); + printCmd(cwd, argv); + + return error.UncleanExit; + }, + } +} + +pub fn makePath(self: *Build, path: []const u8) !void { + fs.cwd().makePath(self.pathFromRoot(path)) catch |err| { + log.err("Unable to create path {s}: {s}", .{ path, @errorName(err) }); + return err; + }; +} + +pub fn installArtifact(self: *Build, artifact: *CompileStep) void { + self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step); +} + +pub fn addInstallArtifact(self: *Build, artifact: *CompileStep) *InstallArtifactStep { + return InstallArtifactStep.create(self, artifact); +} + +///`dest_rel_path` is relative to prefix path +pub fn installFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void { + self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .prefix, dest_rel_path).step); +} + +pub fn installDirectory(self: *Build, options: InstallDirectoryOptions) void { + self.getInstallStep().dependOn(&self.addInstallDirectory(options).step); +} + +///`dest_rel_path` is relative to bin path +pub fn installBinFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void { + self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .bin, dest_rel_path).step); +} + +///`dest_rel_path` is relative to lib path +pub fn installLibFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void { + self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .lib, dest_rel_path).step); +} + +/// Output format (BIN vs Intel HEX) determined by filename +pub fn installRaw(self: *Build, artifact: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { + const raw = self.addInstallRaw(artifact, dest_filename, options); + self.getInstallStep().dependOn(&raw.step); + return raw; +} + +///`dest_rel_path` is relative to install prefix path +pub fn addInstallFile(self: *Build, source: FileSource, dest_rel_path: []const u8) *InstallFileStep { + return self.addInstallFileWithDir(source.dupe(self), .prefix, dest_rel_path); +} + +///`dest_rel_path` is relative to bin path +pub fn addInstallBinFile(self: *Build, source: FileSource, dest_rel_path: []const u8) *InstallFileStep { + return self.addInstallFileWithDir(source.dupe(self), .bin, dest_rel_path); +} + +///`dest_rel_path` is relative to lib path +pub fn addInstallLibFile(self: *Build, source: FileSource, dest_rel_path: []const u8) *InstallFileStep { + return self.addInstallFileWithDir(source.dupe(self), .lib, dest_rel_path); +} + +pub fn addInstallHeaderFile(b: *Build, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep { + return b.addInstallFileWithDir(.{ .path = src_path }, .header, dest_rel_path); +} + +pub fn addInstallRaw(self: *Build, artifact: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { + return InstallRawStep.create(self, artifact, dest_filename, options); +} + +pub fn addInstallFileWithDir( + self: *Build, + source: FileSource, + install_dir: InstallDir, + dest_rel_path: []const u8, +) *InstallFileStep { + if (dest_rel_path.len == 0) { + panic("dest_rel_path must be non-empty", .{}); + } + const install_step = self.allocator.create(InstallFileStep) catch @panic("OOM"); + install_step.* = InstallFileStep.init(self, source.dupe(self), install_dir, dest_rel_path); + return install_step; +} + +pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *InstallDirStep { + const install_step = self.allocator.create(InstallDirStep) catch @panic("OOM"); + install_step.* = InstallDirStep.init(self, options); + return install_step; +} + +pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u8) void { + const file = InstalledFile{ + .dir = dir, + .path = dest_rel_path, + }; + self.installed_files.append(file.dupe(self)) catch @panic("OOM"); +} + +pub fn updateFile(self: *Build, source_path: []const u8, dest_path: []const u8) !void { + if (self.verbose) { + log.info("cp {s} {s} ", .{ source_path, dest_path }); + } + const cwd = fs.cwd(); + const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{}); + if (self.verbose) switch (prev_status) { + .stale => log.info("# installed", .{}), + .fresh => log.info("# up-to-date", .{}), + }; +} + +pub fn truncateFile(self: *Build, dest_path: []const u8) !void { + if (self.verbose) { + log.info("truncate {s}", .{dest_path}); + } + const cwd = fs.cwd(); + var src_file = cwd.createFile(dest_path, .{}) catch |err| switch (err) { + error.FileNotFound => blk: { + if (fs.path.dirname(dest_path)) |dirname| { + try cwd.makePath(dirname); + } + break :blk try cwd.createFile(dest_path, .{}); + }, + else => |e| return e, + }; + src_file.close(); +} + +pub fn pathFromRoot(self: *Build, rel_path: []const u8) []u8 { + return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch @panic("OOM"); +} + +pub fn pathJoin(self: *Build, paths: []const []const u8) []u8 { + return fs.path.join(self.allocator, paths) catch @panic("OOM"); +} + +pub fn fmt(self: *Build, comptime format: []const u8, args: anytype) []u8 { + return fmt_lib.allocPrint(self.allocator, format, args) catch @panic("OOM"); +} + +pub fn findProgram(self: *Build, names: []const []const u8, paths: []const []const u8) ![]const u8 { + // TODO report error for ambiguous situations + const exe_extension = @as(CrossTarget, .{}).exeFileExt(); + for (self.search_prefixes.items) |search_prefix| { + for (names) |name| { + if (fs.path.isAbsolute(name)) { + return name; + } + const full_path = self.pathJoin(&.{ + search_prefix, + "bin", + self.fmt("{s}{s}", .{ name, exe_extension }), + }); + return fs.realpathAlloc(self.allocator, full_path) catch continue; + } + } + if (self.env_map.get("PATH")) |PATH| { + for (names) |name| { + if (fs.path.isAbsolute(name)) { + return name; + } + var it = mem.tokenize(u8, PATH, &[_]u8{fs.path.delimiter}); + while (it.next()) |path| { + const full_path = self.pathJoin(&.{ + path, + self.fmt("{s}{s}", .{ name, exe_extension }), + }); + return fs.realpathAlloc(self.allocator, full_path) catch continue; + } + } + } + for (names) |name| { + if (fs.path.isAbsolute(name)) { + return name; + } + for (paths) |path| { + const full_path = self.pathJoin(&.{ + path, + self.fmt("{s}{s}", .{ name, exe_extension }), + }); + return fs.realpathAlloc(self.allocator, full_path) catch continue; + } + } + return error.FileNotFound; +} + +pub fn execAllowFail( + self: *Build, + argv: []const []const u8, + out_code: *u8, + stderr_behavior: std.ChildProcess.StdIo, +) ExecError![]u8 { + assert(argv.len != 0); + + if (!std.process.can_spawn) + return error.ExecNotSupported; + + const max_output_size = 400 * 1024; + var child = std.ChildProcess.init(argv, self.allocator); + child.stdin_behavior = .Ignore; + child.stdout_behavior = .Pipe; + child.stderr_behavior = stderr_behavior; + child.env_map = self.env_map; + + try child.spawn(); + + const stdout = child.stdout.?.reader().readAllAlloc(self.allocator, max_output_size) catch { + return error.ReadFailure; + }; + errdefer self.allocator.free(stdout); + + const term = try child.wait(); + switch (term) { + .Exited => |code| { + if (code != 0) { + out_code.* = @truncate(u8, code); + return error.ExitCodeFailure; + } + return stdout; + }, + .Signal, .Stopped, .Unknown => |code| { + out_code.* = @truncate(u8, code); + return error.ProcessTerminated; + }, + } +} + +pub fn execFromStep(self: *Build, argv: []const []const u8, src_step: ?*Step) ![]u8 { + assert(argv.len != 0); + + if (self.verbose) { + printCmd(null, argv); + } + + if (!std.process.can_spawn) { + if (src_step) |s| log.err("{s}...", .{s.name}); + log.err("Unable to spawn the following command: cannot spawn child process", .{}); + printCmd(null, argv); + std.os.abort(); + } + + var code: u8 = undefined; + return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) { + error.ExecNotSupported => { + if (src_step) |s| log.err("{s}...", .{s.name}); + log.err("Unable to spawn the following command: cannot spawn child process", .{}); + printCmd(null, argv); + std.os.abort(); + }, + error.FileNotFound => { + if (src_step) |s| log.err("{s}...", .{s.name}); + log.err("Unable to spawn the following command: file not found", .{}); + printCmd(null, argv); + std.os.exit(@truncate(u8, code)); + }, + error.ExitCodeFailure => { + if (src_step) |s| log.err("{s}...", .{s.name}); + if (self.prominent_compile_errors) { + log.err("The step exited with error code {d}", .{code}); + } else { + log.err("The following command exited with error code {d}:", .{code}); + printCmd(null, argv); + } + + std.os.exit(@truncate(u8, code)); + }, + error.ProcessTerminated => { + if (src_step) |s| log.err("{s}...", .{s.name}); + log.err("The following command terminated unexpectedly:", .{}); + printCmd(null, argv); + std.os.exit(@truncate(u8, code)); + }, + else => |e| return e, + }; +} + +pub fn exec(self: *Build, argv: []const []const u8) ![]u8 { + return self.execFromStep(argv, null); +} + +pub fn addSearchPrefix(self: *Build, search_prefix: []const u8) void { + self.search_prefixes.append(self.dupePath(search_prefix)) catch @panic("OOM"); +} + +pub fn getInstallPath(self: *Build, dir: InstallDir, dest_rel_path: []const u8) []const u8 { + assert(!fs.path.isAbsolute(dest_rel_path)); // Install paths must be relative to the prefix + const base_dir = switch (dir) { + .prefix => self.install_path, + .bin => self.exe_dir, + .lib => self.lib_dir, + .header => self.h_dir, + .custom => |path| self.pathJoin(&.{ self.install_path, path }), + }; + return fs.path.resolve( + self.allocator, + &[_][]const u8{ base_dir, dest_rel_path }, + ) catch @panic("OOM"); +} + +pub const Dependency = struct { + builder: *Build, + + pub fn artifact(d: *Dependency, name: []const u8) *CompileStep { + var found: ?*CompileStep = null; + for (d.builder.install_tls.step.dependencies.items) |dep_step| { + const inst = dep_step.cast(InstallArtifactStep) orelse continue; + if (mem.eql(u8, inst.artifact.name, name)) { + if (found != null) panic("artifact name '{s}' is ambiguous", .{name}); + found = inst.artifact; + } + } + return found orelse { + for (d.builder.install_tls.step.dependencies.items) |dep_step| { + const inst = dep_step.cast(InstallArtifactStep) orelse continue; + log.info("available artifact: '{s}'", .{inst.artifact.name}); + } + panic("unable to find artifact '{s}'", .{name}); + }; + } +}; + +pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency { + const build_runner = @import("root"); + const deps = build_runner.dependencies; + + inline for (@typeInfo(deps.imports).Struct.decls) |decl| { + if (mem.startsWith(u8, decl.name, b.dep_prefix) and + mem.endsWith(u8, decl.name, name) and + decl.name.len == b.dep_prefix.len + name.len) + { + const build_zig = @field(deps.imports, decl.name); + const build_root = @field(deps.build_root, decl.name); + return dependencyInner(b, name, build_root, build_zig, args); + } + } + + const full_path = b.pathFromRoot("build.zig.ini"); + std.debug.print("no dependency named '{s}' in '{s}'\n", .{ name, full_path }); + std.process.exit(1); +} + +fn dependencyInner( + b: *Build, + name: []const u8, + build_root: []const u8, + comptime build_zig: type, + args: anytype, +) *Dependency { + const sub_builder = b.createChild(name, build_root, args) catch @panic("unhandled error"); + sub_builder.runBuild(build_zig) catch @panic("unhandled error"); + + if (sub_builder.validateUserInputDidItFail()) { + std.debug.dumpCurrentStackTrace(@returnAddress()); + } + + const dep = b.allocator.create(Dependency) catch @panic("OOM"); + dep.* = .{ .builder = sub_builder }; + return dep; +} + +pub fn runBuild(b: *Build, build_zig: anytype) anyerror!void { + switch (@typeInfo(@typeInfo(@TypeOf(build_zig.build)).Fn.return_type.?)) { + .Void => build_zig.build(b), + .ErrorUnion => try build_zig.build(b), + else => @compileError("expected return type of build to be 'void' or '!void'"), + } +} + +test "builder.findProgram compiles" { + if (builtin.os.tag == .wasi) return error.SkipZigTest; + + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + + const host = try NativeTargetInfo.detect(.{}); + + const builder = try Build.create( + arena.allocator(), + "zig", + "zig-cache", + "zig-cache", + "zig-cache", + host, + ); + defer builder.destroy(); + _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null; +} + +pub const Pkg = struct { + name: []const u8, + source: FileSource, + dependencies: ?[]const Pkg = null, +}; + +/// 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 { + /// The step that generates the file + step: *Step, + + /// The path to the generated file. Must be either absolute or relative to the build root. + /// This value must be set in the `fn make()` of the `step` and must not be `null` afterwards. + path: ?[]const u8 = null, + + pub fn getPath(self: GeneratedFile) []const u8 { + return self.path orelse std.debug.panic( + "getPath() was called on a GeneratedFile that wasn't build yet. Is there a missing Step dependency on step '{s}'?", + .{self.step.name}, + ); + } +}; + +/// A file source is a reference to an existing or future file. +/// +pub const FileSource = union(enum) { + /// A plain file path, relative to build root or absolute. + path: []const u8, + + /// A file that is generated by an interface. Those files usually are + /// not available until built by a build step. + generated: *const GeneratedFile, + + /// Returns a new file source that will have a relative path to the build root guaranteed. + /// This should be preferred over setting `.path` directly as it documents that the files are in the project directory. + pub fn relative(path: []const u8) FileSource { + std.debug.assert(!std.fs.path.isAbsolute(path)); + return FileSource{ .path = path }; + } + + /// Returns a string that can be shown to represent the file source. + /// Either returns the path or `"generated"`. + pub fn getDisplayName(self: FileSource) []const u8 { + return switch (self) { + .path => self.path, + .generated => "generated", + }; + } + + /// Adds dependencies this file source implies to the given step. + pub fn addStepDependencies(self: FileSource, other_step: *Step) void { + switch (self) { + .path => {}, + .generated => |gen| other_step.dependOn(gen.step), + } + } + + /// Should only be called during make(), returns a path relative to the build root or absolute. + pub fn getPath(self: FileSource, builder: *Build) []const u8 { + const path = switch (self) { + .path => |p| builder.pathFromRoot(p), + .generated => |gen| gen.getPath(), + }; + return path; + } + + /// Duplicates the file source for a given builder. + pub fn dupe(self: FileSource, b: *Build) FileSource { + return switch (self) { + .path => |p| .{ .path = b.dupePath(p) }, + .generated => |gen| .{ .generated = gen }, + }; + } +}; + +/// Allocates a new string for assigning a value to a named macro. +/// If the value is omitted, it is set to 1. +/// `name` and `value` need not live longer than the function call. +pub fn constructCMacro(allocator: Allocator, name: []const u8, value: ?[]const u8) []const u8 { + var macro = allocator.alloc( + u8, + name.len + if (value) |value_slice| value_slice.len + 1 else 0, + ) catch |err| if (err == error.OutOfMemory) @panic("Out of memory") else unreachable; + mem.copy(u8, macro, name); + if (value) |value_slice| { + macro[name.len] = '='; + mem.copy(u8, macro[name.len + 1 ..], value_slice); + } + return macro; +} + +pub const VcpkgRoot = union(VcpkgRootStatus) { + unattempted: void, + not_found: void, + found: []const u8, +}; + +pub const VcpkgRootStatus = enum { + unattempted, + not_found, + found, +}; + +pub const InstallDir = union(enum) { + prefix: void, + lib: void, + bin: void, + header: void, + /// A path relative to the prefix + custom: []const u8, + + /// Duplicates the install directory including the path if set to custom. + pub fn dupe(self: InstallDir, builder: *Build) InstallDir { + if (self == .custom) { + // Written with this temporary to avoid RLS problems + const duped_path = builder.dupe(self.custom); + return .{ .custom = duped_path }; + } else { + return self; + } + } +}; + +pub const InstalledFile = struct { + dir: InstallDir, + path: []const u8, + + /// Duplicates the installed file path and directory. + pub fn dupe(self: InstalledFile, builder: *Build) InstalledFile { + return .{ + .dir = self.dir.dupe(builder), + .path = builder.dupe(self.path), + }; + } +}; + +pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 { + // TODO this logic can disappear if cpu model + features becomes part of the target triple + const all_features = cpu.arch.allFeaturesList(); + var populated_cpu_features = cpu.model.features; + populated_cpu_features.populateDependencies(all_features); + + if (populated_cpu_features.eql(cpu.features)) { + // The CPU name alone is sufficient. + return cpu.model.name; + } else { + var mcpu_buffer = ArrayList(u8).init(allocator); + try mcpu_buffer.appendSlice(cpu.model.name); + + for (all_features) |feature, i_usize| { + const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize); + const in_cpu_set = populated_cpu_features.isEnabled(i); + const in_actual_set = cpu.features.isEnabled(i); + if (in_cpu_set and !in_actual_set) { + try mcpu_buffer.writer().print("-{s}", .{feature.name}); + } else if (!in_cpu_set and in_actual_set) { + try mcpu_buffer.writer().print("+{s}", .{feature.name}); + } + } + + return try mcpu_buffer.toOwnedSlice(); + } +} + +test "dupePkg()" { + if (builtin.os.tag == .wasi) return error.SkipZigTest; + + var arena = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena.deinit(); + + const host = try NativeTargetInfo.detect(.{}); + + var builder = try Build.create( + arena.allocator(), + "test", + "test", + "test", + "test", + host, + ); + defer builder.destroy(); + + var pkg_dep = Pkg{ + .name = "pkg_dep", + .source = .{ .path = "/not/a/pkg_dep.zig" }, + }; + var pkg_top = Pkg{ + .name = "pkg_top", + .source = .{ .path = "/not/a/pkg_top.zig" }, + .dependencies = &[_]Pkg{pkg_dep}, + }; + const duped = builder.dupePkg(pkg_top); + + const original_deps = pkg_top.dependencies.?; + const dupe_deps = duped.dependencies.?; + + // probably the same top level package details + try std.testing.expectEqualStrings(pkg_top.name, duped.name); + + // probably the same dependencies + try std.testing.expectEqual(original_deps.len, dupe_deps.len); + try std.testing.expectEqual(original_deps[0].name, pkg_dep.name); + + // could segfault otherwise if pointers in duplicated package's fields are + // the same as those in stack allocated package's fields + try std.testing.expect(dupe_deps.ptr != original_deps.ptr); + try std.testing.expect(duped.name.ptr != pkg_top.name.ptr); + try std.testing.expect(duped.source.path.ptr != pkg_top.source.path.ptr); + try std.testing.expect(dupe_deps[0].name.ptr != pkg_dep.name.ptr); + try std.testing.expect(dupe_deps[0].source.path.ptr != pkg_dep.source.path.ptr); +} + +test { + _ = CheckFileStep; + _ = CheckObjectStep; + _ = EmulatableRunStep; + _ = FmtStep; + _ = InstallArtifactStep; + _ = InstallDirStep; + _ = InstallFileStep; + _ = InstallRawStep; + _ = CompileStep; + _ = LogStep; + _ = OptionsStep; + _ = RemoveDirStep; + _ = RunStep; + _ = TranslateCStep; + _ = WriteFileStep; +} diff --git a/lib/std/build/CheckFileStep.zig b/lib/std/Build/CheckFileStep.zig index 2c06ab9279..b08a797e84 100644 --- a/lib/std/build/CheckFileStep.zig +++ b/lib/std/Build/CheckFileStep.zig @@ -1,7 +1,5 @@ const std = @import("../std.zig"); -const build = std.build; -const Step = build.Step; -const Builder = build.Builder; +const Step = std.Build.Step; const fs = std.fs; const mem = std.mem; @@ -10,17 +8,17 @@ const CheckFileStep = @This(); pub const base_id = .check_file; step: Step, -builder: *Builder, +builder: *std.Build, expected_matches: []const []const u8, -source: build.FileSource, +source: std.Build.FileSource, max_bytes: usize = 20 * 1024 * 1024, pub fn create( - builder: *Builder, - source: build.FileSource, + builder: *std.Build, + source: std.Build.FileSource, expected_matches: []const []const u8, ) *CheckFileStep { - const self = builder.allocator.create(CheckFileStep) catch unreachable; + const self = builder.allocator.create(CheckFileStep) catch @panic("OOM"); self.* = CheckFileStep{ .builder = builder, .step = Step.init(.check_file, "CheckFile", builder.allocator, make), diff --git a/lib/std/build/CheckObjectStep.zig b/lib/std/Build/CheckObjectStep.zig index 4ef350b418..5cb096581f 100644 --- a/lib/std/build/CheckObjectStep.zig +++ b/lib/std/Build/CheckObjectStep.zig @@ -1,6 +1,5 @@ const std = @import("../std.zig"); const assert = std.debug.assert; -const build = std.build; const fs = std.fs; const macho = std.macho; const math = std.math; @@ -10,23 +9,22 @@ const testing = std.testing; const CheckObjectStep = @This(); const Allocator = mem.Allocator; -const Builder = build.Builder; -const Step = build.Step; -const EmulatableRunStep = build.EmulatableRunStep; +const Step = std.Build.Step; +const EmulatableRunStep = std.Build.EmulatableRunStep; pub const base_id = .check_object; step: Step, -builder: *Builder, -source: build.FileSource, +builder: *std.Build, +source: std.Build.FileSource, max_bytes: usize = 20 * 1024 * 1024, checks: std.ArrayList(Check), dump_symtab: bool = false, obj_format: std.Target.ObjectFormat, -pub fn create(builder: *Builder, source: build.FileSource, obj_format: std.Target.ObjectFormat) *CheckObjectStep { +pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std.Target.ObjectFormat) *CheckObjectStep { const gpa = builder.allocator; - const self = gpa.create(CheckObjectStep) catch unreachable; + const self = gpa.create(CheckObjectStep) catch @panic("OOM"); self.* = .{ .builder = builder, .step = Step.init(.check_file, "CheckObject", gpa, make), @@ -44,7 +42,7 @@ pub fn runAndCompare(self: *CheckObjectStep) *EmulatableRunStep { const dependencies_len = self.step.dependencies.items.len; assert(dependencies_len > 0); const exe_step = self.step.dependencies.items[dependencies_len - 1]; - const exe = exe_step.cast(std.build.LibExeObjStep).?; + const exe = exe_step.cast(std.Build.CompileStep).?; const emulatable_step = EmulatableRunStep.create(self.builder, "EmulatableRun", exe); emulatable_step.step.dependOn(&self.step); return emulatable_step; @@ -216,10 +214,10 @@ const ComputeCompareExpected = struct { }; const Check = struct { - builder: *Builder, + builder: *std.Build, actions: std.ArrayList(Action), - fn create(b: *Builder) Check { + fn create(b: *std.Build) Check { return .{ .builder = b, .actions = std.ArrayList(Action).init(b.allocator), @@ -230,14 +228,14 @@ const Check = struct { self.actions.append(.{ .tag = .match, .phrase = self.builder.dupe(phrase), - }) catch unreachable; + }) catch @panic("OOM"); } fn notPresent(self: *Check, phrase: []const u8) void { self.actions.append(.{ .tag = .not_present, .phrase = self.builder.dupe(phrase), - }) catch unreachable; + }) catch @panic("OOM"); } fn computeCmp(self: *Check, phrase: []const u8, expected: ComputeCompareExpected) void { @@ -245,7 +243,7 @@ const Check = struct { .tag = .compute_cmp, .phrase = self.builder.dupe(phrase), .expected = expected, - }) catch unreachable; + }) catch @panic("OOM"); } }; @@ -253,7 +251,7 @@ const Check = struct { pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void { var new_check = Check.create(self.builder); new_check.match(phrase); - self.checks.append(new_check) catch unreachable; + self.checks.append(new_check) catch @panic("OOM"); } /// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`. @@ -295,7 +293,7 @@ pub fn checkComputeCompare( ) void { var new_check = Check.create(self.builder); new_check.computeCmp(program, expected); - self.checks.append(new_check) catch unreachable; + self.checks.append(new_check) catch @panic("OOM"); } fn make(step: *Step) !void { diff --git a/lib/std/build/LibExeObjStep.zig b/lib/std/Build/CompileStep.zig index cb37b24885..12c73bdba6 100644 --- a/lib/std/build/LibExeObjStep.zig +++ b/lib/std/Build/CompileStep.zig @@ -9,41 +9,39 @@ const ArrayList = std.ArrayList; const StringHashMap = std.StringHashMap; const Sha256 = std.crypto.hash.sha2.Sha256; const Allocator = mem.Allocator; -const build = @import("../build.zig"); -const Step = build.Step; -const Builder = build.Builder; +const Step = std.Build.Step; const CrossTarget = std.zig.CrossTarget; const NativeTargetInfo = std.zig.system.NativeTargetInfo; -const FileSource = std.build.FileSource; -const PkgConfigPkg = Builder.PkgConfigPkg; -const PkgConfigError = Builder.PkgConfigError; -const ExecError = Builder.ExecError; -const Pkg = std.build.Pkg; -const VcpkgRoot = std.build.VcpkgRoot; -const InstallDir = std.build.InstallDir; -const InstallArtifactStep = std.build.InstallArtifactStep; -const GeneratedFile = std.build.GeneratedFile; -const InstallRawStep = std.build.InstallRawStep; -const EmulatableRunStep = std.build.EmulatableRunStep; -const CheckObjectStep = std.build.CheckObjectStep; -const RunStep = std.build.RunStep; -const OptionsStep = std.build.OptionsStep; -const ConfigHeaderStep = std.build.ConfigHeaderStep; -const LibExeObjStep = @This(); - -pub const base_id = .lib_exe_obj; +const FileSource = std.Build.FileSource; +const PkgConfigPkg = std.Build.PkgConfigPkg; +const PkgConfigError = std.Build.PkgConfigError; +const ExecError = std.Build.ExecError; +const Pkg = std.Build.Pkg; +const VcpkgRoot = std.Build.VcpkgRoot; +const InstallDir = std.Build.InstallDir; +const InstallArtifactStep = std.Build.InstallArtifactStep; +const GeneratedFile = std.Build.GeneratedFile; +const InstallRawStep = std.Build.InstallRawStep; +const EmulatableRunStep = std.Build.EmulatableRunStep; +const CheckObjectStep = std.Build.CheckObjectStep; +const RunStep = std.Build.RunStep; +const OptionsStep = std.Build.OptionsStep; +const ConfigHeaderStep = std.Build.ConfigHeaderStep; +const CompileStep = @This(); + +pub const base_id: Step.Id = .compile; step: Step, -builder: *Builder, +builder: *std.Build, name: []const u8, -target: CrossTarget = CrossTarget{}, +target: CrossTarget, target_info: NativeTargetInfo, +optimize: std.builtin.Mode, linker_script: ?FileSource = null, version_script: ?[]const u8 = null, out_filename: []const u8, linkage: ?Linkage = null, version: ?std.builtin.Version, -build_mode: std.builtin.Mode, kind: Kind, major_only_filename: ?[]const u8, name_only_filename: ?[]const u8, @@ -84,7 +82,7 @@ initial_memory: ?u64 = null, max_memory: ?u64 = null, shared_memory: bool = false, global_base: ?u64 = null, -c_std: Builder.CStd, +c_std: std.Build.CStd, override_lib_dir: ?[]const u8, main_pkg_path: ?[]const u8, exec_cmd_args: ?[]const ?[]const u8, @@ -108,7 +106,7 @@ object_src: []const u8, link_objects: ArrayList(LinkObject), include_dirs: ArrayList(IncludeDir), c_macros: ArrayList([]const u8), -installed_headers: ArrayList(*std.build.Step), +installed_headers: ArrayList(*Step), output_dir: ?[]const u8, is_linking_libc: bool = false, is_linking_libcpp: bool = false, @@ -226,7 +224,7 @@ pub const CSourceFile = struct { source: FileSource, args: []const []const u8, - pub fn dupe(self: CSourceFile, b: *Builder) CSourceFile { + pub fn dupe(self: CSourceFile, b: *std.Build) CSourceFile { return .{ .source = self.source.dupe(b), .args = b.dupeStrings(self.args), @@ -236,7 +234,7 @@ pub const CSourceFile = struct { pub const LinkObject = union(enum) { static_path: FileSource, - other_step: *LibExeObjStep, + other_step: *CompileStep, system_lib: SystemLib, assembly_file: FileSource, c_source_file: *CSourceFile, @@ -267,10 +265,20 @@ const FrameworkLinkInfo = struct { pub const IncludeDir = union(enum) { raw_path: []const u8, raw_path_system: []const u8, - other_step: *LibExeObjStep, + other_step: *CompileStep, config_header_step: *ConfigHeaderStep, }; +pub const Options = struct { + name: []const u8, + root_source_file: ?FileSource = null, + target: CrossTarget, + optimize: std.builtin.Mode, + kind: Kind, + linkage: ?Linkage = null, + version: ?std.builtin.Version = null, +}; + pub const Kind = enum { exe, lib, @@ -279,11 +287,6 @@ pub const Kind = enum { test_exe, }; -pub const SharedLibKind = union(enum) { - versioned: std.builtin.Version, - unversioned: void, -}; - pub const Linkage = enum { dynamic, static }; pub const EmitOption = union(enum) { @@ -292,7 +295,7 @@ pub const EmitOption = union(enum) { emit: void, emit_to: []const u8, - fn getArg(self: @This(), b: *Builder, arg_name: []const u8) ?[]const u8 { + fn getArg(self: @This(), b: *std.Build, arg_name: []const u8) ?[]const u8 { return switch (self) { .no_emit => b.fmt("-fno-{s}", .{arg_name}), .default => null, @@ -302,62 +305,29 @@ pub const EmitOption = union(enum) { } }; -pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource, kind: SharedLibKind) *LibExeObjStep { - return initExtraArgs(builder, name, root_src, .lib, .dynamic, switch (kind) { - .versioned => |ver| ver, - .unversioned => null, - }); -} - -pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { - return initExtraArgs(builder, name, root_src, .lib, .static, null); -} - -pub fn createObject(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { - return initExtraArgs(builder, name, root_src, .obj, null, null); -} - -pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { - return initExtraArgs(builder, name, root_src, .exe, null, null); -} - -pub fn createTest(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep { - return initExtraArgs(builder, name, root_src, .@"test", null, null); -} - -pub fn createTestExe(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep { - return initExtraArgs(builder, name, root_src, .test_exe, null, null); -} - -fn initExtraArgs( - builder: *Builder, - name_raw: []const u8, - root_src_raw: ?FileSource, - kind: Kind, - linkage: ?Linkage, - ver: ?std.builtin.Version, -) *LibExeObjStep { - const name = builder.dupe(name_raw); - const root_src: ?FileSource = if (root_src_raw) |rsrc| rsrc.dupe(builder) else null; +pub fn create(builder: *std.Build, options: Options) *CompileStep { + const name = builder.dupe(options.name); + const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(builder) 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}); } - const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = LibExeObjStep{ + const self = builder.allocator.create(CompileStep) catch @panic("OOM"); + self.* = CompileStep{ .strip = null, .unwind_tables = null, .builder = builder, .verbose_link = false, .verbose_cc = false, - .build_mode = std.builtin.Mode.Debug, - .linkage = linkage, - .kind = kind, + .optimize = options.optimize, + .target = options.target, + .linkage = options.linkage, + .kind = options.kind, .root_src = root_src, .name = name, .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator), .step = Step.init(base_id, name, builder.allocator, make), - .version = ver, + .version = options.version, .out_filename = undefined, .out_h_filename = builder.fmt("{s}.h", .{name}), .out_lib_filename = undefined, @@ -371,9 +341,9 @@ fn initExtraArgs( .lib_paths = ArrayList([]const u8).init(builder.allocator), .rpaths = ArrayList([]const u8).init(builder.allocator), .framework_dirs = ArrayList([]const u8).init(builder.allocator), - .installed_headers = ArrayList(*std.build.Step).init(builder.allocator), + .installed_headers = ArrayList(*Step).init(builder.allocator), .object_src = undefined, - .c_std = Builder.CStd.C99, + .c_std = std.Build.CStd.C99, .override_lib_dir = null, .main_pkg_path = null, .exec_cmd_args = null, @@ -394,17 +364,14 @@ fn initExtraArgs( .output_h_path_source = GeneratedFile{ .step = &self.step }, .output_pdb_path_source = GeneratedFile{ .step = &self.step }, - .target_info = undefined, // populated in computeOutFileNames + .target_info = NativeTargetInfo.detect(self.target) catch @panic("unhandled error"), }; self.computeOutFileNames(); if (root_src) |rs| rs.addStepDependencies(&self.step); return self; } -fn computeOutFileNames(self: *LibExeObjStep) void { - self.target_info = NativeTargetInfo.detect(self.target) catch - unreachable; - +fn computeOutFileNames(self: *CompileStep) void { const target = self.target_info.target; self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{ @@ -420,7 +387,7 @@ fn computeOutFileNames(self: *LibExeObjStep) void { .static => .Static, }) else null, .version = self.version, - }) catch unreachable; + }) catch @panic("OOM"); if (self.kind == .lib) { if (self.linkage != null and self.linkage.? == .static) { @@ -457,31 +424,26 @@ fn computeOutFileNames(self: *LibExeObjStep) void { } } -pub fn setTarget(self: *LibExeObjStep, target: CrossTarget) void { - self.target = target; - self.computeOutFileNames(); -} - -pub fn setOutputDir(self: *LibExeObjStep, dir: []const u8) void { +pub fn setOutputDir(self: *CompileStep, dir: []const u8) void { self.output_dir = self.builder.dupePath(dir); } -pub fn install(self: *LibExeObjStep) void { +pub fn install(self: *CompileStep) void { self.builder.installArtifact(self); } -pub fn installRaw(self: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { +pub fn installRaw(self: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { return self.builder.installRaw(self, dest_filename, options); } -pub fn installHeader(a: *LibExeObjStep, src_path: []const u8, dest_rel_path: []const u8) void { +pub fn installHeader(a: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void { const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path); a.builder.getInstallStep().dependOn(&install_file.step); - a.installed_headers.append(&install_file.step) catch unreachable; + a.installed_headers.append(&install_file.step) catch @panic("OOM"); } pub fn installHeadersDirectory( - a: *LibExeObjStep, + a: *CompileStep, src_dir_path: []const u8, dest_rel_path: []const u8, ) void { @@ -493,15 +455,15 @@ pub fn installHeadersDirectory( } pub fn installHeadersDirectoryOptions( - a: *LibExeObjStep, - options: std.build.InstallDirStep.Options, + a: *CompileStep, + options: std.Build.InstallDirStep.Options, ) void { const install_dir = a.builder.addInstallDirectory(options); a.builder.getInstallStep().dependOn(&install_dir.step); - a.installed_headers.append(&install_dir.step) catch unreachable; + a.installed_headers.append(&install_dir.step) catch @panic("OOM"); } -pub fn installLibraryHeaders(a: *LibExeObjStep, l: *LibExeObjStep) void { +pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void { assert(l.kind == .lib); const install_step = a.builder.getInstallStep(); // Copy each element from installed_headers, modifying the builder @@ -510,7 +472,7 @@ pub fn installLibraryHeaders(a: *LibExeObjStep, l: *LibExeObjStep) void { const step_copy = switch (step.id) { inline .install_file, .install_dir => |id| blk: { const T = id.Type(); - const ptr = a.builder.allocator.create(T) catch unreachable; + const ptr = a.builder.allocator.create(T) catch @panic("OOM"); ptr.* = step.cast(T).?.*; ptr.override_source_builder = ptr.builder; ptr.builder = a.builder; @@ -518,15 +480,15 @@ pub fn installLibraryHeaders(a: *LibExeObjStep, l: *LibExeObjStep) void { }, else => unreachable, }; - a.installed_headers.append(step_copy) catch unreachable; + a.installed_headers.append(step_copy) catch @panic("OOM"); install_step.dependOn(step_copy); } - a.installed_headers.appendSlice(l.installed_headers.items) catch unreachable; + a.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM"); } /// Creates a `RunStep` with an executable built with `addExecutable`. /// Add command line arguments with `addArg`. -pub fn run(exe: *LibExeObjStep) *RunStep { +pub fn run(exe: *CompileStep) *RunStep { assert(exe.kind == .exe or exe.kind == .test_exe); // It doesn't have to be native. We catch that if you actually try to run it. @@ -550,7 +512,7 @@ pub fn run(exe: *LibExeObjStep) *RunStep { /// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta. /// When a binary cannot be ran through emulation or the option is disabled, a warning /// will be printed and the binary will *NOT* be ran. -pub fn runEmulatable(exe: *LibExeObjStep) *EmulatableRunStep { +pub fn runEmulatable(exe: *CompileStep) *EmulatableRunStep { assert(exe.kind == .exe or exe.kind == .test_exe); const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe); @@ -560,33 +522,33 @@ pub fn runEmulatable(exe: *LibExeObjStep) *EmulatableRunStep { return run_step; } -pub fn checkObject(self: *LibExeObjStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep { +pub fn checkObject(self: *CompileStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep { return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format); } -pub fn setLinkerScriptPath(self: *LibExeObjStep, source: FileSource) void { +pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void { self.linker_script = source.dupe(self.builder); source.addStepDependencies(&self.step); } -pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void { - self.frameworks.put(self.builder.dupe(framework_name), .{}) catch unreachable; +pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void { + self.frameworks.put(self.builder.dupe(framework_name), .{}) catch @panic("OOM"); } -pub fn linkFrameworkNeeded(self: *LibExeObjStep, framework_name: []const u8) void { +pub fn linkFrameworkNeeded(self: *CompileStep, framework_name: []const u8) void { self.frameworks.put(self.builder.dupe(framework_name), .{ .needed = true, - }) catch unreachable; + }) catch @panic("OOM"); } -pub fn linkFrameworkWeak(self: *LibExeObjStep, framework_name: []const u8) void { +pub fn linkFrameworkWeak(self: *CompileStep, framework_name: []const u8) void { self.frameworks.put(self.builder.dupe(framework_name), .{ .weak = true, - }) catch unreachable; + }) catch @panic("OOM"); } /// Returns whether the library, executable, or object depends on a particular system library. -pub fn dependsOnSystemLibrary(self: LibExeObjStep, name: []const u8) bool { +pub fn dependsOnSystemLibrary(self: CompileStep, name: []const u8) bool { if (isLibCLibrary(name)) { return self.is_linking_libc; } @@ -602,49 +564,49 @@ pub fn dependsOnSystemLibrary(self: LibExeObjStep, name: []const u8) bool { return false; } -pub fn linkLibrary(self: *LibExeObjStep, lib: *LibExeObjStep) void { +pub fn linkLibrary(self: *CompileStep, lib: *CompileStep) void { assert(lib.kind == .lib); self.linkLibraryOrObject(lib); } -pub fn isDynamicLibrary(self: *LibExeObjStep) bool { +pub fn isDynamicLibrary(self: *CompileStep) bool { return self.kind == .lib and self.linkage == Linkage.dynamic; } -pub fn isStaticLibrary(self: *LibExeObjStep) bool { +pub fn isStaticLibrary(self: *CompileStep) bool { return self.kind == .lib and self.linkage != Linkage.dynamic; } -pub fn producesPdbFile(self: *LibExeObjStep) bool { +pub fn producesPdbFile(self: *CompileStep) bool { if (!self.target.isWindows() and !self.target.isUefi()) return false; if (self.target.getObjectFormat() == .c) return false; if (self.strip == true) return false; return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe; } -pub fn linkLibC(self: *LibExeObjStep) void { +pub fn linkLibC(self: *CompileStep) void { self.is_linking_libc = true; } -pub fn linkLibCpp(self: *LibExeObjStep) void { +pub fn linkLibCpp(self: *CompileStep) void { self.is_linking_libcpp = true; } /// If the value is omitted, it is set to 1. /// `name` and `value` need not live longer than the function call. -pub fn defineCMacro(self: *LibExeObjStep, name: []const u8, value: ?[]const u8) void { - const macro = std.build.constructCMacro(self.builder.allocator, name, value); - self.c_macros.append(macro) catch unreachable; +pub fn defineCMacro(self: *CompileStep, name: []const u8, value: ?[]const u8) void { + const macro = std.Build.constructCMacro(self.builder.allocator, name, value); + self.c_macros.append(macro) catch @panic("OOM"); } /// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. -pub fn defineCMacroRaw(self: *LibExeObjStep, name_and_value: []const u8) void { - self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable; +pub fn defineCMacroRaw(self: *CompileStep, name_and_value: []const u8) void { + self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM"); } /// This one has no integration with anything, it just puts -lname on the command line. /// Prefer to use `linkSystemLibrary` instead. -pub fn linkSystemLibraryName(self: *LibExeObjStep, name: []const u8) void { +pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void { self.link_objects.append(.{ .system_lib = .{ .name = self.builder.dupe(name), @@ -652,12 +614,12 @@ pub fn linkSystemLibraryName(self: *LibExeObjStep, name: []const u8) void { .weak = false, .use_pkg_config = .no, }, - }) catch unreachable; + }) catch @panic("OOM"); } /// This one has no integration with anything, it just puts -needed-lname on the command line. /// Prefer to use `linkSystemLibraryNeeded` instead. -pub fn linkSystemLibraryNeededName(self: *LibExeObjStep, name: []const u8) void { +pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void { self.link_objects.append(.{ .system_lib = .{ .name = self.builder.dupe(name), @@ -665,12 +627,12 @@ pub fn linkSystemLibraryNeededName(self: *LibExeObjStep, name: []const u8) void .weak = false, .use_pkg_config = .no, }, - }) catch unreachable; + }) catch @panic("OOM"); } /// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the /// command line. Prefer to use `linkSystemLibraryWeak` instead. -pub fn linkSystemLibraryWeakName(self: *LibExeObjStep, name: []const u8) void { +pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void { self.link_objects.append(.{ .system_lib = .{ .name = self.builder.dupe(name), @@ -678,12 +640,12 @@ pub fn linkSystemLibraryWeakName(self: *LibExeObjStep, name: []const u8) void { .weak = true, .use_pkg_config = .no, }, - }) catch unreachable; + }) catch @panic("OOM"); } /// This links against a system library, exclusively using pkg-config to find the library. /// Prefer to use `linkSystemLibrary` instead. -pub fn linkSystemLibraryPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void { +pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void { self.link_objects.append(.{ .system_lib = .{ .name = self.builder.dupe(lib_name), @@ -691,12 +653,12 @@ pub fn linkSystemLibraryPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8 .weak = false, .use_pkg_config = .force, }, - }) catch unreachable; + }) catch @panic("OOM"); } /// This links against a system library, exclusively using pkg-config to find the library. /// Prefer to use `linkSystemLibraryNeeded` instead. -pub fn linkSystemLibraryNeededPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void { +pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void { self.link_objects.append(.{ .system_lib = .{ .name = self.builder.dupe(lib_name), @@ -704,12 +666,12 @@ pub fn linkSystemLibraryNeededPkgConfigOnly(self: *LibExeObjStep, lib_name: []co .weak = false, .use_pkg_config = .force, }, - }) catch unreachable; + }) catch @panic("OOM"); } /// 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. -pub fn runPkgConfig(self: *LibExeObjStep, lib_name: []const u8) ![]const []const u8 { +pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u8 { const pkg_name = match: { // First we have to map the library name to pkg config name. Unfortunately, // there are several examples where this is not straightforward: @@ -803,19 +765,19 @@ pub fn runPkgConfig(self: *LibExeObjStep, lib_name: []const u8) ![]const []const return zig_args.toOwnedSlice(); } -pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void { +pub fn linkSystemLibrary(self: *CompileStep, name: []const u8) void { self.linkSystemLibraryInner(name, .{}); } -pub fn linkSystemLibraryNeeded(self: *LibExeObjStep, name: []const u8) void { +pub fn linkSystemLibraryNeeded(self: *CompileStep, name: []const u8) void { self.linkSystemLibraryInner(name, .{ .needed = true }); } -pub fn linkSystemLibraryWeak(self: *LibExeObjStep, name: []const u8) void { +pub fn linkSystemLibraryWeak(self: *CompileStep, name: []const u8) void { self.linkSystemLibraryInner(name, .{ .weak = true }); } -fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, opts: struct { +fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct { needed: bool = false, weak: bool = false, }) void { @@ -835,27 +797,27 @@ fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, opts: struct { .weak = opts.weak, .use_pkg_config = .yes, }, - }) catch unreachable; + }) catch @panic("OOM"); } -pub fn setNamePrefix(self: *LibExeObjStep, text: []const u8) void { +pub fn setNamePrefix(self: *CompileStep, text: []const u8) void { assert(self.kind == .@"test" or self.kind == .test_exe); self.name_prefix = self.builder.dupe(text); } -pub fn setFilter(self: *LibExeObjStep, text: ?[]const u8) void { +pub fn setFilter(self: *CompileStep, text: ?[]const u8) void { assert(self.kind == .@"test" or self.kind == .test_exe); self.filter = if (text) |t| self.builder.dupe(t) else null; } -pub fn setTestRunner(self: *LibExeObjStep, path: ?[]const u8) void { +pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void { assert(self.kind == .@"test" or self.kind == .test_exe); self.test_runner = if (path) |p| self.builder.dupePath(p) else null; } /// Handy when you have many C/C++ source files and want them all to have the same flags. -pub fn addCSourceFiles(self: *LibExeObjStep, files: []const []const u8, flags: []const []const u8) void { - const c_source_files = self.builder.allocator.create(CSourceFiles) catch unreachable; +pub fn addCSourceFiles(self: *CompileStep, files: []const []const u8, flags: []const []const u8) void { + const c_source_files = self.builder.allocator.create(CSourceFiles) catch @panic("OOM"); const files_copy = self.builder.dupeStrings(files); const flags_copy = self.builder.dupeStrings(flags); @@ -864,96 +826,92 @@ pub fn addCSourceFiles(self: *LibExeObjStep, files: []const []const u8, flags: [ .files = files_copy, .flags = flags_copy, }; - self.link_objects.append(.{ .c_source_files = c_source_files }) catch unreachable; + self.link_objects.append(.{ .c_source_files = c_source_files }) catch @panic("OOM"); } -pub fn addCSourceFile(self: *LibExeObjStep, file: []const u8, flags: []const []const u8) void { +pub fn addCSourceFile(self: *CompileStep, file: []const u8, flags: []const []const u8) void { self.addCSourceFileSource(.{ .args = flags, .source = .{ .path = file }, }); } -pub fn addCSourceFileSource(self: *LibExeObjStep, source: CSourceFile) void { - const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable; +pub fn addCSourceFileSource(self: *CompileStep, source: CSourceFile) void { + const c_source_file = self.builder.allocator.create(CSourceFile) catch @panic("OOM"); c_source_file.* = source.dupe(self.builder); - self.link_objects.append(.{ .c_source_file = c_source_file }) catch unreachable; + self.link_objects.append(.{ .c_source_file = c_source_file }) catch @panic("OOM"); source.source.addStepDependencies(&self.step); } -pub fn setVerboseLink(self: *LibExeObjStep, value: bool) void { +pub fn setVerboseLink(self: *CompileStep, value: bool) void { self.verbose_link = value; } -pub fn setVerboseCC(self: *LibExeObjStep, value: bool) void { +pub fn setVerboseCC(self: *CompileStep, value: bool) void { self.verbose_cc = value; } -pub fn setBuildMode(self: *LibExeObjStep, mode: std.builtin.Mode) void { - self.build_mode = mode; -} - -pub fn overrideZigLibDir(self: *LibExeObjStep, dir_path: []const u8) void { +pub fn overrideZigLibDir(self: *CompileStep, dir_path: []const u8) void { self.override_lib_dir = self.builder.dupePath(dir_path); } -pub fn setMainPkgPath(self: *LibExeObjStep, dir_path: []const u8) void { +pub fn setMainPkgPath(self: *CompileStep, dir_path: []const u8) void { self.main_pkg_path = self.builder.dupePath(dir_path); } -pub fn setLibCFile(self: *LibExeObjStep, libc_file: ?FileSource) void { +pub fn setLibCFile(self: *CompileStep, libc_file: ?FileSource) void { self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null; } /// Returns the generated executable, library or object file. /// To run an executable built with zig build, use `run`, or create an install step and invoke it. -pub fn getOutputSource(self: *LibExeObjStep) FileSource { +pub fn getOutputSource(self: *CompileStep) FileSource { return FileSource{ .generated = &self.output_path_source }; } /// Returns the generated import library. This function can only be called for libraries. -pub fn getOutputLibSource(self: *LibExeObjStep) FileSource { +pub fn getOutputLibSource(self: *CompileStep) FileSource { assert(self.kind == .lib); return FileSource{ .generated = &self.output_lib_path_source }; } /// Returns the generated header file. /// This function can only be called for libraries or object files which have `emit_h` set. -pub fn getOutputHSource(self: *LibExeObjStep) FileSource { +pub fn getOutputHSource(self: *CompileStep) FileSource { assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test"); assert(self.emit_h); return FileSource{ .generated = &self.output_h_path_source }; } /// Returns the generated PDB file. This function can only be called for Windows and UEFI. -pub fn getOutputPdbSource(self: *LibExeObjStep) FileSource { +pub fn getOutputPdbSource(self: *CompileStep) FileSource { // TODO: Is this right? Isn't PDB for *any* PE/COFF file? assert(self.target.isWindows() or self.target.isUefi()); return FileSource{ .generated = &self.output_pdb_path_source }; } -pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void { +pub fn addAssemblyFile(self: *CompileStep, path: []const u8) void { self.link_objects.append(.{ .assembly_file = .{ .path = self.builder.dupe(path) }, - }) catch unreachable; + }) catch @panic("OOM"); } -pub fn addAssemblyFileSource(self: *LibExeObjStep, source: FileSource) void { +pub fn addAssemblyFileSource(self: *CompileStep, source: FileSource) void { const source_duped = source.dupe(self.builder); - self.link_objects.append(.{ .assembly_file = source_duped }) catch unreachable; + self.link_objects.append(.{ .assembly_file = source_duped }) catch @panic("OOM"); source_duped.addStepDependencies(&self.step); } -pub fn addObjectFile(self: *LibExeObjStep, source_file: []const u8) void { +pub fn addObjectFile(self: *CompileStep, source_file: []const u8) void { self.addObjectFileSource(.{ .path = source_file }); } -pub fn addObjectFileSource(self: *LibExeObjStep, source: FileSource) void { - self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch unreachable; +pub fn addObjectFileSource(self: *CompileStep, source: FileSource) void { + self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch @panic("OOM"); source.addStepDependencies(&self.step); } -pub fn addObject(self: *LibExeObjStep, obj: *LibExeObjStep) void { +pub fn addObject(self: *CompileStep, obj: *CompileStep) void { assert(obj.kind == .obj); self.linkLibraryOrObject(obj); } @@ -963,41 +921,41 @@ pub const addIncludeDir = @compileError("deprecated; use addIncludePath"); pub const addLibPath = @compileError("deprecated, use addLibraryPath"); pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath"); -pub fn addSystemIncludePath(self: *LibExeObjStep, path: []const u8) void { - self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch unreachable; +pub fn addSystemIncludePath(self: *CompileStep, path: []const u8) void { + self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch @panic("OOM"); } -pub fn addIncludePath(self: *LibExeObjStep, path: []const u8) void { - self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch unreachable; +pub fn addIncludePath(self: *CompileStep, path: []const u8) void { + self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch @panic("OOM"); } -pub fn addConfigHeader(self: *LibExeObjStep, config_header: *ConfigHeaderStep) void { +pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) void { self.step.dependOn(&config_header.step); self.include_dirs.append(.{ .config_header_step = config_header }) catch @panic("OOM"); } -pub fn addLibraryPath(self: *LibExeObjStep, path: []const u8) void { - self.lib_paths.append(self.builder.dupe(path)) catch unreachable; +pub fn addLibraryPath(self: *CompileStep, path: []const u8) void { + self.lib_paths.append(self.builder.dupe(path)) catch @panic("OOM"); } -pub fn addRPath(self: *LibExeObjStep, path: []const u8) void { - self.rpaths.append(self.builder.dupe(path)) catch unreachable; +pub fn addRPath(self: *CompileStep, path: []const u8) void { + self.rpaths.append(self.builder.dupe(path)) catch @panic("OOM"); } -pub fn addFrameworkPath(self: *LibExeObjStep, dir_path: []const u8) void { - self.framework_dirs.append(self.builder.dupe(dir_path)) catch unreachable; +pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void { + self.framework_dirs.append(self.builder.dupe(dir_path)) catch @panic("OOM"); } -pub fn addPackage(self: *LibExeObjStep, package: Pkg) void { - self.packages.append(self.builder.dupePkg(package)) catch unreachable; +pub fn addPackage(self: *CompileStep, package: Pkg) void { + self.packages.append(self.builder.dupePkg(package)) catch @panic("OOM"); self.addRecursiveBuildDeps(package); } -pub fn addOptions(self: *LibExeObjStep, package_name: []const u8, options: *OptionsStep) void { +pub fn addOptions(self: *CompileStep, package_name: []const u8, options: *OptionsStep) void { self.addPackage(options.getPackage(package_name)); } -fn addRecursiveBuildDeps(self: *LibExeObjStep, package: Pkg) void { +fn addRecursiveBuildDeps(self: *CompileStep, package: Pkg) void { package.source.addStepDependencies(&self.step); if (package.dependencies) |deps| { for (deps) |dep| { @@ -1006,7 +964,7 @@ fn addRecursiveBuildDeps(self: *LibExeObjStep, package: Pkg) void { } } -pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void { +pub fn addPackagePath(self: *CompileStep, name: []const u8, pkg_index_path: []const u8) void { self.addPackage(Pkg{ .name = self.builder.dupe(name), .source = .{ .path = self.builder.dupe(pkg_index_path) }, @@ -1015,7 +973,7 @@ pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: [] /// If Vcpkg was found on the system, it will be added to include and lib /// paths for the specified target. -pub fn addVcpkgPaths(self: *LibExeObjStep, linkage: LibExeObjStep.Linkage) !void { +pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void { // Ideally in the Unattempted case we would call the function recursively // after findVcpkgRoot and have only one switch statement, but the compiler // cannot resolve the error set. @@ -1050,22 +1008,22 @@ pub fn addVcpkgPaths(self: *LibExeObjStep, linkage: LibExeObjStep.Linkage) !void } } -pub fn setExecCmd(self: *LibExeObjStep, args: []const ?[]const u8) void { +pub fn setExecCmd(self: *CompileStep, args: []const ?[]const u8) void { assert(self.kind == .@"test"); - const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch unreachable; + const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch @panic("OOM"); for (args) |arg, i| { duped_args[i] = if (arg) |a| self.builder.dupe(a) else null; } self.exec_cmd_args = duped_args; } -fn linkLibraryOrObject(self: *LibExeObjStep, other: *LibExeObjStep) void { +fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void { self.step.dependOn(&other.step); - self.link_objects.append(.{ .other_step = other }) catch unreachable; - self.include_dirs.append(.{ .other_step = other }) catch unreachable; + self.link_objects.append(.{ .other_step = other }) catch @panic("OOM"); + self.include_dirs.append(.{ .other_step = other }) catch @panic("OOM"); } -fn makePackageCmd(self: *LibExeObjStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void { +fn makePackageCmd(self: *CompileStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void { const builder = self.builder; try zig_args.append("--pkg-begin"); @@ -1082,7 +1040,7 @@ fn makePackageCmd(self: *LibExeObjStep, pkg: Pkg, zig_args: *ArrayList([]const u } fn make(step: *Step) !void { - const self = @fieldParentPtr(LibExeObjStep, "step", step); + const self = @fieldParentPtr(CompileStep, "step", step); const builder = self.builder; if (self.root_src == null and self.link_objects.items.len == 0) { @@ -1093,7 +1051,7 @@ fn make(step: *Step) !void { var zig_args = ArrayList([]const u8).init(builder.allocator); defer zig_args.deinit(); - zig_args.append(builder.zig_exe) catch unreachable; + try zig_args.append(builder.zig_exe); const cmd = switch (self.kind) { .lib => "build-lib", @@ -1102,7 +1060,7 @@ fn make(step: *Step) !void { .@"test" => "test", .test_exe => "test", }; - zig_args.append(cmd) catch unreachable; + try zig_args.append(cmd); if (builder.color != .auto) { try zig_args.append("--color"); @@ -1307,12 +1265,12 @@ fn make(step: *Step) !void { try zig_args.append("--debug-compile-errors"); } - if (builder.verbose_cimport) zig_args.append("--verbose-cimport") catch unreachable; - if (builder.verbose_air) zig_args.append("--verbose-air") catch unreachable; - if (builder.verbose_llvm_ir) zig_args.append("--verbose-llvm-ir") catch unreachable; - if (builder.verbose_link or self.verbose_link) zig_args.append("--verbose-link") catch unreachable; - if (builder.verbose_cc or self.verbose_cc) zig_args.append("--verbose-cc") catch unreachable; - if (builder.verbose_llvm_cpu_features) zig_args.append("--verbose-llvm-cpu-features") catch unreachable; + if (builder.verbose_cimport) try zig_args.append("--verbose-cimport"); + if (builder.verbose_air) try zig_args.append("--verbose-air"); + if (builder.verbose_llvm_ir) try zig_args.append("--verbose-llvm-ir"); + if (builder.verbose_link or self.verbose_link) try zig_args.append("--verbose-link"); + if (builder.verbose_cc or self.verbose_cc) try zig_args.append("--verbose-cc"); + if (builder.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features"); if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg); if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg); @@ -1376,9 +1334,9 @@ fn make(step: *Step) !void { try zig_args.append(libc_file); } - switch (self.build_mode) { + switch (self.optimize) { .Debug => {}, // Skip since it's the default. - else => zig_args.append(builder.fmt("-O{s}", .{@tagName(self.build_mode)})) catch unreachable, + else => try zig_args.append(builder.fmt("-O{s}", .{@tagName(self.optimize)})), } try zig_args.append("--cache-dir"); @@ -1387,8 +1345,8 @@ fn make(step: *Step) !void { try zig_args.append("--global-cache-dir"); try zig_args.append(builder.pathFromRoot(builder.global_cache_root)); - zig_args.append("--name") catch unreachable; - zig_args.append(self.name) catch unreachable; + try zig_args.append("--name"); + try zig_args.append(self.name); if (self.linkage) |some| switch (some) { .dynamic => try zig_args.append("-dynamic"), @@ -1396,8 +1354,8 @@ fn make(step: *Step) !void { }; if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) { if (self.version) |version| { - zig_args.append("--version") catch unreachable; - zig_args.append(builder.fmt("{}", .{version})) catch unreachable; + try zig_args.append("--version"); + try zig_args.append(builder.fmt("{}", .{version})); } if (self.target.isDarwin()) { @@ -1495,37 +1453,10 @@ fn make(step: *Step) !void { } if (!self.target.isNative()) { - try zig_args.append("-target"); - try zig_args.append(try self.target.zigTriple(builder.allocator)); - - // TODO this logic can disappear if cpu model + features becomes part of the target triple - const cross = self.target.toTarget(); - const all_features = cross.cpu.arch.allFeaturesList(); - var populated_cpu_features = cross.cpu.model.features; - populated_cpu_features.populateDependencies(all_features); - - if (populated_cpu_features.eql(cross.cpu.features)) { - // The CPU name alone is sufficient. - try zig_args.append("-mcpu"); - try zig_args.append(cross.cpu.model.name); - } else { - var mcpu_buffer = ArrayList(u8).init(builder.allocator); - - try mcpu_buffer.writer().print("-mcpu={s}", .{cross.cpu.model.name}); - - for (all_features) |feature, i_usize| { - const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize); - const in_cpu_set = populated_cpu_features.isEnabled(i); - const in_actual_set = cross.cpu.features.isEnabled(i); - if (in_cpu_set and !in_actual_set) { - try mcpu_buffer.writer().print("-{s}", .{feature.name}); - } else if (!in_cpu_set and in_actual_set) { - try mcpu_buffer.writer().print("+{s}", .{feature.name}); - } - } - - try zig_args.append(try mcpu_buffer.toOwnedSlice()); - } + try zig_args.appendSlice(&.{ + "-target", try self.target.zigTriple(builder.allocator), + "-mcpu", try std.Build.serializeCpu(builder.allocator, self.target.getCpu()), + }); if (self.target.dynamic_linker.get()) |dynamic_linker| { try zig_args.append("--dynamic-linker"); @@ -1720,13 +1651,13 @@ fn make(step: *Step) !void { const name = entry.key_ptr.*; const info = entry.value_ptr.*; if (info.needed) { - zig_args.append("-needed_framework") catch unreachable; + try zig_args.append("-needed_framework"); } else if (info.weak) { - zig_args.append("-weak_framework") catch unreachable; + try zig_args.append("-weak_framework"); } else { - zig_args.append("-framework") catch unreachable; + try zig_args.append("-framework"); } - zig_args.append(name) catch unreachable; + try zig_args.append(name); } } else { if (self.framework_dirs.items.len > 0) { @@ -1817,7 +1748,7 @@ fn make(step: *Step) !void { // Slow path for arguments that need to be escaped. We'll need to allocate and copy var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1); const writer = escaped.writer(); - writer.writeAll(arg[0..arg_idx]) catch unreachable; + try writer.writeAll(arg[0..arg_idx]); for (arg[arg_idx..]) |to_escape| { if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\'); try writer.writeByte(to_escape); @@ -1943,30 +1874,35 @@ fn findVcpkgRoot(allocator: Allocator) !?[]const u8 { return vcpkg_path; } -pub fn doAtomicSymLinks(allocator: Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void { +pub fn doAtomicSymLinks( + allocator: Allocator, + output_path: []const u8, + filename_major_only: []const u8, + filename_name_only: []const u8, +) !void { const out_dir = fs.path.dirname(output_path) orelse "."; const out_basename = fs.path.basename(output_path); // sym link for libfoo.so.1 to libfoo.so.1.2.3 - const major_only_path = fs.path.join( + const major_only_path = try fs.path.join( allocator, &[_][]const u8{ out_dir, filename_major_only }, - ) catch unreachable; + ); fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| { log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename }); return err; }; // sym link for libfoo.so to libfoo.so.1 - const name_only_path = fs.path.join( + const name_only_path = try fs.path.join( allocator, &[_][]const u8{ out_dir, filename_name_only }, - ) catch unreachable; + ); fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| { log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only }); return err; }; } -fn execPkgConfigList(self: *Builder, out_code: *u8) (PkgConfigError || ExecError)![]const PkgConfigPkg { +fn execPkgConfigList(self: *std.Build, out_code: *u8) (PkgConfigError || ExecError)![]const PkgConfigPkg { const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore); var list = ArrayList(PkgConfigPkg).init(self.allocator); errdefer list.deinit(); @@ -1982,7 +1918,7 @@ fn execPkgConfigList(self: *Builder, out_code: *u8) (PkgConfigError || ExecError return list.toOwnedSlice(); } -fn getPkgConfigList(self: *Builder) ![]const PkgConfigPkg { +fn getPkgConfigList(self: *std.Build) ![]const PkgConfigPkg { if (self.pkg_config_pkg_list) |res| { return res; } @@ -2012,12 +1948,15 @@ test "addPackage" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); - var builder = try Builder.create( + const host = try NativeTargetInfo.detect(.{}); + + var builder = try std.Build.create( arena.allocator(), "test", "test", "test", "test", + host, ); defer builder.destroy(); @@ -2031,7 +1970,10 @@ test "addPackage" { .dependencies = &[_]Pkg{pkg_dep}, }; - var exe = builder.addExecutable("not_an_executable", "/not/an/executable.zig"); + var exe = builder.addExecutable(.{ + .name = "not_an_executable", + .root_source_file = .{ .path = "/not/an/executable.zig" }, + }); exe.addPackage(pkg_top); try std.testing.expectEqual(@as(usize, 1), exe.packages.items.len); @@ -2070,7 +2012,7 @@ const TransitiveDeps = struct { } } - fn addInner(td: *TransitiveDeps, other: *LibExeObjStep, dyn: bool) !void { + fn addInner(td: *TransitiveDeps, other: *CompileStep, 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; diff --git a/lib/std/build/ConfigHeaderStep.zig b/lib/std/Build/ConfigHeaderStep.zig index 400c06525e..58a78b939d 100644 --- a/lib/std/build/ConfigHeaderStep.zig +++ b/lib/std/Build/ConfigHeaderStep.zig @@ -1,7 +1,6 @@ const std = @import("../std.zig"); const ConfigHeaderStep = @This(); -const Step = std.build.Step; -const Builder = std.build.Builder; +const Step = std.Build.Step; pub const base_id: Step.Id = .config_header; @@ -24,15 +23,15 @@ pub const Value = union(enum) { }; step: Step, -builder: *Builder, -source: std.build.FileSource, +builder: *std.Build, +source: std.Build.FileSource, style: Style, values: std.StringHashMap(Value), max_bytes: usize = 2 * 1024 * 1024, output_dir: []const u8, output_basename: []const u8, -pub fn create(builder: *Builder, source: std.build.FileSource, style: Style) *ConfigHeaderStep { +pub fn create(builder: *std.Build, source: std.Build.FileSource, style: Style) *ConfigHeaderStep { const self = builder.allocator.create(ConfigHeaderStep) catch @panic("OOM"); const name = builder.fmt("configure header {s}", .{source.getDisplayName()}); self.* = .{ @@ -62,39 +61,51 @@ pub fn addValues(self: *ConfigHeaderStep, values: anytype) void { fn addValuesInner(self: *ConfigHeaderStep, values: anytype) !void { inline for (@typeInfo(@TypeOf(values)).Struct.fields) |field| { - switch (@typeInfo(field.type)) { - .Null => { - try self.values.put(field.name, .undef); - }, - .Void => { - try self.values.put(field.name, .defined); - }, - .Bool => { - try self.values.put(field.name, .{ .boolean = @field(values, field.name) }); - }, - .ComptimeInt => { - try self.values.put(field.name, .{ .int = @field(values, field.name) }); - }, - .EnumLiteral => { - try self.values.put(field.name, .{ .ident = @tagName(@field(values, field.name)) }); - }, - .Pointer => |ptr| { - switch (@typeInfo(ptr.child)) { - .Array => |array| { - if (ptr.size == .One and array.child == u8) { - try self.values.put(field.name, .{ .string = @field(values, field.name) }); - continue; - } - }, - else => {}, - } + try putValue(self, field.name, field.type, @field(values, field.name)); + } +} - @compileError("unsupported ConfigHeaderStep value type: " ++ - @typeName(field.type)); - }, - else => @compileError("unsupported ConfigHeaderStep value type: " ++ - @typeName(field.type)), - } +fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v: T) !void { + switch (@typeInfo(T)) { + .Null => { + try self.values.put(field_name, .undef); + }, + .Void => { + try self.values.put(field_name, .defined); + }, + .Bool => { + try self.values.put(field_name, .{ .boolean = v }); + }, + .Int => { + try self.values.put(field_name, .{ .int = v }); + }, + .ComptimeInt => { + try self.values.put(field_name, .{ .int = v }); + }, + .EnumLiteral => { + try self.values.put(field_name, .{ .ident = @tagName(v) }); + }, + .Optional => { + if (v) |x| { + return putValue(self, field_name, @TypeOf(x), x); + } else { + try self.values.put(field_name, .undef); + } + }, + .Pointer => |ptr| { + switch (@typeInfo(ptr.child)) { + .Array => |array| { + if (ptr.size == .One and array.child == u8) { + try self.values.put(field_name, .{ .string = v }); + return; + } + }, + else => {}, + } + + @compileError("unsupported ConfigHeaderStep value type: " ++ @typeName(T)); + }, + else => @compileError("unsupported ConfigHeaderStep value type: " ++ @typeName(T)), } } diff --git a/lib/std/build/EmulatableRunStep.zig b/lib/std/Build/EmulatableRunStep.zig index 52ce8edfac..5517f7f9aa 100644 --- a/lib/std/build/EmulatableRunStep.zig +++ b/lib/std/Build/EmulatableRunStep.zig @@ -5,11 +5,9 @@ //! without having to verify if it's possible to be ran against. const std = @import("../std.zig"); -const build = std.build; -const Step = std.build.Step; -const Builder = std.build.Builder; -const LibExeObjStep = std.build.LibExeObjStep; -const RunStep = std.build.RunStep; +const Step = std.Build.Step; +const CompileStep = std.Build.CompileStep; +const RunStep = std.Build.RunStep; const fs = std.fs; const process = std.process; @@ -22,10 +20,10 @@ pub const base_id = .emulatable_run; const max_stdout_size = 1 * 1024 * 1024; // 1 MiB step: Step, -builder: *Builder, +builder: *std.Build, /// The artifact (executable) to be run by this step -exe: *LibExeObjStep, +exe: *CompileStep, /// Set this to `null` to ignore the exit code for the purpose of determining a successful execution expected_exit_code: ?u8 = 0, @@ -47,9 +45,9 @@ hide_foreign_binaries_warning: bool, /// binary through emulation when any of the emulation options such as `enable_rosetta` are set to true. /// When set to false, and the binary is foreign, running the executable is skipped. /// Asserts given artifact is an executable. -pub fn create(builder: *Builder, name: []const u8, artifact: *LibExeObjStep) *EmulatableRunStep { +pub fn create(builder: *std.Build, name: []const u8, artifact: *CompileStep) *EmulatableRunStep { std.debug.assert(artifact.kind == .exe or artifact.kind == .test_exe); - const self = builder.allocator.create(EmulatableRunStep) catch unreachable; + const self = builder.allocator.create(EmulatableRunStep) catch @panic("OOM"); const option_name = "hide-foreign-warnings"; const hide_warnings = if (builder.available_options_map.get(option_name) == null) warn: { @@ -156,9 +154,9 @@ fn warnAboutForeignBinaries(step: *EmulatableRunStep) void { const builder = step.builder; const artifact = step.exe; - const host_name = builder.host.target.zigTriple(builder.allocator) catch unreachable; - const foreign_name = artifact.target.zigTriple(builder.allocator) catch unreachable; - const target_info = std.zig.system.NativeTargetInfo.detect(artifact.target) catch unreachable; + const host_name = builder.host.target.zigTriple(builder.allocator) catch @panic("unhandled error"); + const foreign_name = artifact.target.zigTriple(builder.allocator) catch @panic("unhandled error"); + const target_info = std.zig.system.NativeTargetInfo.detect(artifact.target) catch @panic("unhandled error"); const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc; switch (builder.host.getExternalExecutor(target_info, .{ .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null, diff --git a/lib/std/build/FmtStep.zig b/lib/std/Build/FmtStep.zig index 62923623f2..6404d22f13 100644 --- a/lib/std/build/FmtStep.zig +++ b/lib/std/Build/FmtStep.zig @@ -1,25 +1,20 @@ const std = @import("../std.zig"); -const build = @import("../build.zig"); -const Step = build.Step; -const Builder = build.Builder; -const BufMap = std.BufMap; -const mem = std.mem; - +const Step = std.Build.Step; const FmtStep = @This(); pub const base_id = .fmt; step: Step, -builder: *Builder, +builder: *std.Build, argv: [][]const u8, -pub fn create(builder: *Builder, paths: []const []const u8) *FmtStep { - const self = builder.allocator.create(FmtStep) catch unreachable; +pub fn create(builder: *std.Build, paths: []const []const u8) *FmtStep { + const self = builder.allocator.create(FmtStep) catch @panic("OOM"); const name = "zig fmt"; self.* = FmtStep{ .step = Step.init(.fmt, name, builder.allocator, make), .builder = builder, - .argv = builder.allocator.alloc([]u8, paths.len + 2) catch unreachable, + .argv = builder.allocator.alloc([]u8, paths.len + 2) catch @panic("OOM"), }; self.argv[0] = builder.zig_exe; diff --git a/lib/std/build/InstallArtifactStep.zig b/lib/std/Build/InstallArtifactStep.zig index 537b8c8fd9..8ee739a41c 100644 --- a/lib/std/build/InstallArtifactStep.zig +++ b/lib/std/Build/InstallArtifactStep.zig @@ -1,26 +1,23 @@ const std = @import("../std.zig"); -const build = @import("../build.zig"); -const Step = build.Step; -const Builder = build.Builder; -const LibExeObjStep = std.build.LibExeObjStep; -const InstallDir = std.build.InstallDir; +const Step = std.Build.Step; +const CompileStep = std.Build.CompileStep; +const InstallDir = std.Build.InstallDir; +const InstallArtifactStep = @This(); pub const base_id = .install_artifact; step: Step, -builder: *Builder, -artifact: *LibExeObjStep, +builder: *std.Build, +artifact: *CompileStep, dest_dir: InstallDir, pdb_dir: ?InstallDir, h_dir: ?InstallDir, -const Self = @This(); - -pub fn create(builder: *Builder, artifact: *LibExeObjStep) *Self { +pub fn create(builder: *std.Build, artifact: *CompileStep) *InstallArtifactStep { if (artifact.install_step) |s| return s; - const self = builder.allocator.create(Self) catch unreachable; - self.* = Self{ + const self = builder.allocator.create(InstallArtifactStep) catch @panic("OOM"); + self.* = InstallArtifactStep{ .builder = builder, .step = Step.init(.install_artifact, builder.fmt("install {s}", .{artifact.step.name}), builder.allocator, make), .artifact = artifact, @@ -64,13 +61,13 @@ pub fn create(builder: *Builder, artifact: *LibExeObjStep) *Self { } fn make(step: *Step) !void { - const self = @fieldParentPtr(Self, "step", step); + const self = @fieldParentPtr(InstallArtifactStep, "step", step); const builder = self.builder; const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename); try builder.updateFile(self.artifact.getOutputSource().getPath(builder), full_dest_path); if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) { - try LibExeObjStep.doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?); + try CompileStep.doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?); } if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) { const full_implib_path = builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename); diff --git a/lib/std/build/InstallDirStep.zig b/lib/std/Build/InstallDirStep.zig index 0a41e1aaef..41dbb3e35a 100644 --- a/lib/std/build/InstallDirStep.zig +++ b/lib/std/Build/InstallDirStep.zig @@ -1,19 +1,17 @@ const std = @import("../std.zig"); const mem = std.mem; const fs = std.fs; -const build = @import("../build.zig"); -const Step = build.Step; -const Builder = build.Builder; -const InstallDir = std.build.InstallDir; +const Step = std.Build.Step; +const InstallDir = std.Build.InstallDir; const InstallDirStep = @This(); const log = std.log; step: Step, -builder: *Builder, +builder: *std.Build, options: Options, /// This is used by the build system when a file being installed comes from one /// package but is being installed by another. -override_source_builder: ?*Builder = null, +override_source_builder: ?*std.Build = null, pub const base_id = .install_dir; @@ -31,7 +29,7 @@ pub const Options = struct { /// `@import("test.zig")` would be a compile error. blank_extensions: []const []const u8 = &.{}, - fn dupe(self: Options, b: *Builder) Options { + fn dupe(self: Options, b: *std.Build) Options { return .{ .source_dir = b.dupe(self.source_dir), .install_dir = self.install_dir.dupe(b), @@ -43,7 +41,7 @@ pub const Options = struct { }; pub fn init( - builder: *Builder, + builder: *std.Build, options: Options, ) InstallDirStep { builder.pushInstalledFile(options.install_dir, options.install_subdir); diff --git a/lib/std/build/InstallFileStep.zig b/lib/std/Build/InstallFileStep.zig index 37203e64c5..8c8d8ad2d4 100644 --- a/lib/std/build/InstallFileStep.zig +++ b/lib/std/Build/InstallFileStep.zig @@ -1,24 +1,22 @@ const std = @import("../std.zig"); -const build = @import("../build.zig"); -const Step = build.Step; -const Builder = build.Builder; -const FileSource = std.build.FileSource; -const InstallDir = std.build.InstallDir; +const Step = std.Build.Step; +const FileSource = std.Build.FileSource; +const InstallDir = std.Build.InstallDir; const InstallFileStep = @This(); pub const base_id = .install_file; step: Step, -builder: *Builder, +builder: *std.Build, source: FileSource, dir: InstallDir, dest_rel_path: []const u8, /// This is used by the build system when a file being installed comes from one /// package but is being installed by another. -override_source_builder: ?*Builder = null, +override_source_builder: ?*std.Build = null, pub fn init( - builder: *Builder, + builder: *std.Build, source: FileSource, dir: InstallDir, dest_rel_path: []const u8, diff --git a/lib/std/build/InstallRawStep.zig b/lib/std/Build/InstallRawStep.zig index e8266dff5a..014c44f287 100644 --- a/lib/std/build/InstallRawStep.zig +++ b/lib/std/Build/InstallRawStep.zig @@ -7,11 +7,10 @@ const InstallRawStep = @This(); const Allocator = std.mem.Allocator; const ArenaAllocator = std.heap.ArenaAllocator; const ArrayListUnmanaged = std.ArrayListUnmanaged; -const Builder = std.build.Builder; const File = std.fs.File; -const InstallDir = std.build.InstallDir; -const LibExeObjStep = std.build.LibExeObjStep; -const Step = std.build.Step; +const InstallDir = std.Build.InstallDir; +const CompileStep = std.Build.CompileStep; +const Step = std.Build.Step; const elf = std.elf; const fs = std.fs; const io = std.io; @@ -25,12 +24,12 @@ pub const RawFormat = enum { }; step: Step, -builder: *Builder, -artifact: *LibExeObjStep, +builder: *std.Build, +artifact: *CompileStep, dest_dir: InstallDir, dest_filename: []const u8, options: CreateOptions, -output_file: std.build.GeneratedFile, +output_file: std.Build.GeneratedFile, pub const CreateOptions = struct { format: ?RawFormat = null, @@ -39,8 +38,13 @@ pub const CreateOptions = struct { pad_to: ?u64 = null, }; -pub fn create(builder: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: CreateOptions) *InstallRawStep { - const self = builder.allocator.create(InstallRawStep) catch unreachable; +pub fn create( + builder: *std.Build, + artifact: *CompileStep, + dest_filename: []const u8, + options: CreateOptions, +) *InstallRawStep { + const self = builder.allocator.create(InstallRawStep) catch @panic("OOM"); self.* = InstallRawStep{ .step = Step.init(.install_raw, builder.fmt("install raw binary {s}", .{artifact.step.name}), builder.allocator, make), .builder = builder, @@ -53,7 +57,7 @@ pub fn create(builder: *Builder, artifact: *LibExeObjStep, dest_filename: []cons }, .dest_filename = dest_filename, .options = options, - .output_file = std.build.GeneratedFile{ .step = &self.step }, + .output_file = std.Build.GeneratedFile{ .step = &self.step }, }; self.step.dependOn(&artifact.step); @@ -61,8 +65,8 @@ pub fn create(builder: *Builder, artifact: *LibExeObjStep, dest_filename: []cons return self; } -pub fn getOutputSource(self: *const InstallRawStep) std.build.FileSource { - return std.build.FileSource{ .generated = &self.output_file }; +pub fn getOutputSource(self: *const InstallRawStep) std.Build.FileSource { + return std.Build.FileSource{ .generated = &self.output_file }; } fn make(step: *Step) !void { @@ -78,7 +82,7 @@ fn make(step: *Step) !void { const full_dest_path = b.getInstallPath(self.dest_dir, self.dest_filename); self.output_file.path = full_dest_path; - fs.cwd().makePath(b.getInstallPath(self.dest_dir, "")) catch unreachable; + try fs.cwd().makePath(b.getInstallPath(self.dest_dir, "")); var argv_list = std.ArrayList([]const u8).init(b.allocator); try argv_list.appendSlice(&.{ b.zig_exe, "objcopy" }); diff --git a/lib/std/build/LogStep.zig b/lib/std/Build/LogStep.zig index fd937b00f9..6d51df8cbd 100644 --- a/lib/std/build/LogStep.zig +++ b/lib/std/Build/LogStep.zig @@ -1,17 +1,15 @@ const std = @import("../std.zig"); const log = std.log; -const build = @import("../build.zig"); -const Step = build.Step; -const Builder = build.Builder; +const Step = std.Build.Step; const LogStep = @This(); pub const base_id = .log; step: Step, -builder: *Builder, +builder: *std.Build, data: []const u8, -pub fn init(builder: *Builder, data: []const u8) LogStep { +pub fn init(builder: *std.Build, data: []const u8) LogStep { return LogStep{ .builder = builder, .step = Step.init(.log, builder.fmt("log {s}", .{data}), builder.allocator, make), diff --git a/lib/std/build/OptionsStep.zig b/lib/std/Build/OptionsStep.zig index fb06cc2179..a353737512 100644 --- a/lib/std/build/OptionsStep.zig +++ b/lib/std/Build/OptionsStep.zig @@ -1,12 +1,10 @@ const std = @import("../std.zig"); const builtin = @import("builtin"); -const build = std.build; const fs = std.fs; -const Step = build.Step; -const Builder = build.Builder; -const GeneratedFile = build.GeneratedFile; -const LibExeObjStep = build.LibExeObjStep; -const FileSource = build.FileSource; +const Step = std.Build.Step; +const GeneratedFile = std.Build.GeneratedFile; +const CompileStep = std.Build.CompileStep; +const FileSource = std.Build.FileSource; const OptionsStep = @This(); @@ -14,14 +12,14 @@ pub const base_id = .options; step: Step, generated_file: GeneratedFile, -builder: *Builder, +builder: *std.Build, contents: std.ArrayList(u8), artifact_args: std.ArrayList(OptionArtifactArg), file_source_args: std.ArrayList(OptionFileSourceArg), -pub fn create(builder: *Builder) *OptionsStep { - const self = builder.allocator.create(OptionsStep) catch unreachable; +pub fn create(builder: *std.Build) *OptionsStep { + const self = builder.allocator.create(OptionsStep) catch @panic("OOM"); self.* = .{ .builder = builder, .step = Step.init(.options, "options", builder.allocator, make), @@ -36,44 +34,48 @@ pub fn create(builder: *Builder) *OptionsStep { } pub fn addOption(self: *OptionsStep, comptime T: type, name: []const u8, value: T) void { + return addOptionFallible(self, T, name, value) catch @panic("unhandled error"); +} + +fn addOptionFallible(self: *OptionsStep, comptime T: type, name: []const u8, value: T) !void { const out = self.contents.writer(); switch (T) { []const []const u8 => { - out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)}) catch unreachable; + try out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)}); for (value) |slice| { - out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)}) catch unreachable; + try out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)}); } - out.writeAll("};\n") catch unreachable; + try out.writeAll("};\n"); return; }, [:0]const u8 => { - out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable; + try out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }); return; }, []const u8 => { - out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable; + try out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }); return; }, ?[:0]const u8 => { - out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable; + try out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)}); if (value) |payload| { - out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable; + try out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}); } else { - out.writeAll("null;\n") catch unreachable; + try out.writeAll("null;\n"); } return; }, ?[]const u8 => { - out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable; + try out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)}); if (value) |payload| { - out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable; + try out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}); } else { - out.writeAll("null;\n") catch unreachable; + try out.writeAll("null;\n"); } return; }, std.builtin.Version => { - out.print( + try out.print( \\pub const {}: @import("std").builtin.Version = .{{ \\ .major = {d}, \\ .minor = {d}, @@ -86,11 +88,11 @@ pub fn addOption(self: *OptionsStep, comptime T: type, name: []const u8, value: value.major, value.minor, value.patch, - }) catch unreachable; + }); return; }, std.SemanticVersion => { - out.print( + try out.print( \\pub const {}: @import("std").SemanticVersion = .{{ \\ .major = {d}, \\ .minor = {d}, @@ -102,38 +104,38 @@ pub fn addOption(self: *OptionsStep, comptime T: type, name: []const u8, value: value.major, value.minor, value.patch, - }) catch unreachable; + }); if (value.pre) |some| { - out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable; + try out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)}); } if (value.build) |some| { - out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable; + try out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)}); } - out.writeAll("};\n") catch unreachable; + try out.writeAll("};\n"); return; }, else => {}, } switch (@typeInfo(T)) { .Enum => |enum_info| { - out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))}) catch unreachable; + try out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))}); inline for (enum_info.fields) |field| { - out.print(" {},\n", .{std.zig.fmtId(field.name)}) catch unreachable; + try out.print(" {},\n", .{std.zig.fmtId(field.name)}); } - out.writeAll("};\n") catch unreachable; - out.print("pub const {}: {s} = {s}.{s};\n", .{ + try out.writeAll("};\n"); + try out.print("pub const {}: {s} = {s}.{s};\n", .{ std.zig.fmtId(name), std.zig.fmtId(@typeName(T)), std.zig.fmtId(@typeName(T)), std.zig.fmtId(@tagName(value)), - }) catch unreachable; + }); return; }, else => {}, } - out.print("pub const {}: {s} = ", .{ std.zig.fmtId(name), @typeName(T) }) catch unreachable; - printLiteral(out, value, 0) catch unreachable; - out.writeAll(";\n") catch unreachable; + try out.print("pub const {}: {s} = ", .{ std.zig.fmtId(name), @typeName(T) }); + try printLiteral(out, value, 0); + try out.writeAll(";\n"); } // TODO: non-recursive? @@ -191,18 +193,18 @@ pub fn addOptionFileSource( self.file_source_args.append(.{ .name = name, .source = source.dupe(self.builder), - }) catch unreachable; + }) catch @panic("OOM"); source.addStepDependencies(&self.step); } /// The value is the path in the cache dir. /// Adds a dependency automatically. -pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *LibExeObjStep) void { - self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch unreachable; +pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *CompileStep) void { + self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch @panic("OOM"); self.step.dependOn(&artifact.step); } -pub fn getPackage(self: *OptionsStep, package_name: []const u8) build.Pkg { +pub fn getPackage(self: *OptionsStep, package_name: []const u8) std.Build.Pkg { return .{ .name = package_name, .source = self.getSource() }; } @@ -268,7 +270,7 @@ fn hashContentsToFileName(self: *OptionsStep) [64]u8 { const OptionArtifactArg = struct { name: []const u8, - artifact: *LibExeObjStep, + artifact: *CompileStep, }; const OptionFileSourceArg = struct { @@ -281,12 +283,16 @@ test "OptionsStep" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); - var builder = try Builder.create( + + const host = try std.zig.system.NativeTargetInfo.detect(.{}); + + var builder = try std.Build.create( arena.allocator(), "test", "test", "test", "test", + host, ); defer builder.destroy(); diff --git a/lib/std/build/RemoveDirStep.zig b/lib/std/Build/RemoveDirStep.zig index 959414e54f..f3b71dcec1 100644 --- a/lib/std/build/RemoveDirStep.zig +++ b/lib/std/Build/RemoveDirStep.zig @@ -1,18 +1,16 @@ const std = @import("../std.zig"); const log = std.log; const fs = std.fs; -const build = @import("../build.zig"); -const Step = build.Step; -const Builder = build.Builder; +const Step = std.Build.Step; const RemoveDirStep = @This(); pub const base_id = .remove_dir; step: Step, -builder: *Builder, +builder: *std.Build, dir_path: []const u8, -pub fn init(builder: *Builder, dir_path: []const u8) RemoveDirStep { +pub fn init(builder: *std.Build, dir_path: []const u8) RemoveDirStep { return RemoveDirStep{ .builder = builder, .step = Step.init(.remove_dir, builder.fmt("RemoveDir {s}", .{dir_path}), builder.allocator, make), diff --git a/lib/std/build/RunStep.zig b/lib/std/Build/RunStep.zig index 5183a328cd..07f2363623 100644 --- a/lib/std/build/RunStep.zig +++ b/lib/std/Build/RunStep.zig @@ -1,17 +1,15 @@ const std = @import("../std.zig"); const builtin = @import("builtin"); -const build = std.build; -const Step = build.Step; -const Builder = build.Builder; -const LibExeObjStep = build.LibExeObjStep; -const WriteFileStep = build.WriteFileStep; +const Step = std.Build.Step; +const CompileStep = std.Build.CompileStep; +const WriteFileStep = std.Build.WriteFileStep; const fs = std.fs; const mem = std.mem; const process = std.process; const ArrayList = std.ArrayList; const EnvMap = process.EnvMap; const Allocator = mem.Allocator; -const ExecError = build.Builder.ExecError; +const ExecError = std.Build.ExecError; const max_stdout_size = 1 * 1024 * 1024; // 1 MiB @@ -20,7 +18,7 @@ const RunStep = @This(); pub const base_id: Step.Id = .run; step: Step, -builder: *Builder, +builder: *std.Build, /// See also addArg and addArgs to modifying this directly argv: ArrayList(Arg), @@ -50,13 +48,13 @@ pub const StdIoAction = union(enum) { }; pub const Arg = union(enum) { - artifact: *LibExeObjStep, - file_source: build.FileSource, + artifact: *CompileStep, + file_source: std.Build.FileSource, bytes: []u8, }; -pub fn create(builder: *Builder, name: []const u8) *RunStep { - const self = builder.allocator.create(RunStep) catch unreachable; +pub fn create(builder: *std.Build, name: []const u8) *RunStep { + const self = builder.allocator.create(RunStep) catch @panic("OOM"); self.* = RunStep{ .builder = builder, .step = Step.init(base_id, name, builder.allocator, make), @@ -68,20 +66,20 @@ pub fn create(builder: *Builder, name: []const u8) *RunStep { return self; } -pub fn addArtifactArg(self: *RunStep, artifact: *LibExeObjStep) void { - self.argv.append(Arg{ .artifact = artifact }) catch unreachable; +pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void { + self.argv.append(Arg{ .artifact = artifact }) catch @panic("OOM"); self.step.dependOn(&artifact.step); } -pub fn addFileSourceArg(self: *RunStep, file_source: build.FileSource) void { +pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void { self.argv.append(Arg{ .file_source = file_source.dupe(self.builder), - }) catch unreachable; + }) catch @panic("OOM"); file_source.addStepDependencies(&self.step); } pub fn addArg(self: *RunStep, arg: []const u8) void { - self.argv.append(Arg{ .bytes = self.builder.dupe(arg) }) catch unreachable; + self.argv.append(Arg{ .bytes = self.builder.dupe(arg) }) catch @panic("OOM"); } pub fn addArgs(self: *RunStep, args: []const []const u8) void { @@ -91,7 +89,7 @@ pub fn addArgs(self: *RunStep, args: []const []const u8) void { } pub fn clearEnvironment(self: *RunStep) void { - const new_env_map = self.builder.allocator.create(EnvMap) catch unreachable; + const new_env_map = self.builder.allocator.create(EnvMap) catch @panic("OOM"); new_env_map.* = EnvMap.init(self.builder.allocator); self.env_map = new_env_map; } @@ -101,7 +99,7 @@ pub fn addPathDir(self: *RunStep, search_path: []const u8) void { } /// For internal use only, users of `RunStep` should use `addPathDir` directly. -pub fn addPathDirInternal(step: *Step, builder: *Builder, search_path: []const u8) void { +pub fn addPathDirInternal(step: *Step, builder: *std.Build, search_path: []const u8) void { const env_map = getEnvMapInternal(step, builder.allocator); const key = "PATH"; @@ -109,9 +107,9 @@ pub fn addPathDirInternal(step: *Step, builder: *Builder, search_path: []const u if (prev_path) |pp| { const new_path = builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path }); - env_map.put(key, new_path) catch unreachable; + env_map.put(key, new_path) catch @panic("OOM"); } else { - env_map.put(key, builder.dupePath(search_path)) catch unreachable; + env_map.put(key, builder.dupePath(search_path)) catch @panic("OOM"); } } @@ -122,12 +120,12 @@ pub fn getEnvMap(self: *RunStep) *EnvMap { fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap { const maybe_env_map = switch (step.id) { .run => step.cast(RunStep).?.env_map, - .emulatable_run => step.cast(build.EmulatableRunStep).?.env_map, + .emulatable_run => step.cast(std.Build.EmulatableRunStep).?.env_map, else => unreachable, }; return maybe_env_map orelse { - const env_map = allocator.create(EnvMap) catch unreachable; - env_map.* = process.getEnvMap(allocator) catch unreachable; + const env_map = allocator.create(EnvMap) catch @panic("OOM"); + env_map.* = process.getEnvMap(allocator) catch @panic("unhandled error"); switch (step.id) { .run => step.cast(RunStep).?.env_map = env_map, .emulatable_run => step.cast(RunStep).?.env_map = env_map, @@ -142,7 +140,7 @@ pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8 env_map.put( self.builder.dupe(key), self.builder.dupe(value), - ) catch unreachable; + ) catch @panic("unhandled error"); } pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void { @@ -195,7 +193,7 @@ fn make(step: *Step) !void { pub fn runCommand( argv: []const []const u8, - builder: *Builder, + builder: *std.Build, expected_exit_code: ?u8, stdout_action: StdIoAction, stderr_action: StdIoAction, @@ -236,7 +234,7 @@ pub fn runCommand( switch (stdout_action) { .expect_exact, .expect_matches => { - stdout = child.stdout.?.reader().readAllAlloc(builder.allocator, max_stdout_size) catch unreachable; + stdout = try child.stdout.?.reader().readAllAlloc(builder.allocator, max_stdout_size); }, .inherit, .ignore => {}, } @@ -246,7 +244,7 @@ pub fn runCommand( switch (stderr_action) { .expect_exact, .expect_matches => { - stderr = child.stderr.?.reader().readAllAlloc(builder.allocator, max_stdout_size) catch unreachable; + stderr = try child.stderr.?.reader().readAllAlloc(builder.allocator, max_stdout_size); }, .inherit, .ignore => {}, } @@ -357,13 +355,13 @@ fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { std.debug.print("\n", .{}); } -fn addPathForDynLibs(self: *RunStep, artifact: *LibExeObjStep) void { +fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void { addPathForDynLibsInternal(&self.step, self.builder, artifact); } /// This should only be used for internal usage, this is called automatically /// for the user. -pub fn addPathForDynLibsInternal(step: *Step, builder: *Builder, artifact: *LibExeObjStep) void { +pub fn addPathForDynLibsInternal(step: *Step, builder: *std.Build, artifact: *CompileStep) void { for (artifact.link_objects.items) |link_object| { switch (link_object) { .other_step => |other| { diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig new file mode 100644 index 0000000000..ff0ceb2a51 --- /dev/null +++ b/lib/std/Build/Step.zig @@ -0,0 +1,97 @@ +id: Id, +name: []const u8, +makeFn: *const fn (self: *Step) anyerror!void, +dependencies: std.ArrayList(*Step), +loop_flag: bool, +done_flag: bool, + +pub const Id = enum { + top_level, + compile, + install_artifact, + install_file, + install_dir, + log, + remove_dir, + fmt, + translate_c, + write_file, + run, + emulatable_run, + check_file, + check_object, + config_header, + install_raw, + options, + custom, + + pub fn Type(comptime id: Id) type { + return switch (id) { + .top_level => Build.TopLevelStep, + .compile => Build.CompileStep, + .install_artifact => Build.InstallArtifactStep, + .install_file => Build.InstallFileStep, + .install_dir => Build.InstallDirStep, + .log => Build.LogStep, + .remove_dir => Build.RemoveDirStep, + .fmt => Build.FmtStep, + .translate_c => Build.TranslateCStep, + .write_file => Build.WriteFileStep, + .run => Build.RunStep, + .emulatable_run => Build.EmulatableRunStep, + .check_file => Build.CheckFileStep, + .check_object => Build.CheckObjectStep, + .config_header => Build.ConfigHeaderStep, + .install_raw => Build.InstallRawStep, + .options => Build.OptionsStep, + .custom => @compileError("no type available for custom step"), + }; + } +}; + +pub fn init( + id: Id, + name: []const u8, + allocator: Allocator, + makeFn: *const fn (self: *Step) anyerror!void, +) Step { + return Step{ + .id = id, + .name = allocator.dupe(u8, name) catch @panic("OOM"), + .makeFn = makeFn, + .dependencies = std.ArrayList(*Step).init(allocator), + .loop_flag = false, + .done_flag = false, + }; +} + +pub fn initNoOp(id: Id, name: []const u8, allocator: Allocator) Step { + return init(id, name, allocator, makeNoOp); +} + +pub fn make(self: *Step) !void { + if (self.done_flag) return; + + try self.makeFn(self); + self.done_flag = true; +} + +pub fn dependOn(self: *Step, other: *Step) void { + self.dependencies.append(other) catch @panic("OOM"); +} + +fn makeNoOp(self: *Step) anyerror!void { + _ = self; +} + +pub fn cast(step: *Step, comptime T: type) ?*T { + if (step.id == T.base_id) { + return @fieldParentPtr(T, "step", step); + } + return null; +} + +const Step = @This(); +const std = @import("../std.zig"); +const Build = std.Build; +const Allocator = std.mem.Allocator; diff --git a/lib/std/build/TranslateCStep.zig b/lib/std/Build/TranslateCStep.zig index 1f9bee463c..d9874142d8 100644 --- a/lib/std/build/TranslateCStep.zig +++ b/lib/std/Build/TranslateCStep.zig @@ -1,9 +1,7 @@ const std = @import("../std.zig"); -const build = std.build; -const Step = build.Step; -const Builder = build.Builder; -const LibExeObjStep = build.LibExeObjStep; -const CheckFileStep = build.CheckFileStep; +const Step = std.Build.Step; +const CompileStep = std.Build.CompileStep; +const CheckFileStep = std.Build.CheckFileStep; const fs = std.fs; const mem = std.mem; const CrossTarget = std.zig.CrossTarget; @@ -13,17 +11,25 @@ const TranslateCStep = @This(); pub const base_id = .translate_c; step: Step, -builder: *Builder, -source: build.FileSource, +builder: *std.Build, +source: std.Build.FileSource, include_dirs: std.ArrayList([]const u8), c_macros: std.ArrayList([]const u8), output_dir: ?[]const u8, out_basename: []const u8, -target: CrossTarget = CrossTarget{}, -output_file: build.GeneratedFile, - -pub fn create(builder: *Builder, source: build.FileSource) *TranslateCStep { - const self = builder.allocator.create(TranslateCStep) catch unreachable; +target: CrossTarget, +optimize: std.builtin.OptimizeMode, +output_file: std.Build.GeneratedFile, + +pub const Options = struct { + source_file: std.Build.FileSource, + target: CrossTarget, + optimize: std.builtin.OptimizeMode, +}; + +pub fn create(builder: *std.Build, options: Options) *TranslateCStep { + const self = builder.allocator.create(TranslateCStep) catch @panic("OOM"); + const source = options.source_file.dupe(builder); self.* = TranslateCStep{ .step = Step.init(.translate_c, "translate-c", builder.allocator, make), .builder = builder, @@ -32,23 +38,36 @@ pub fn create(builder: *Builder, source: build.FileSource) *TranslateCStep { .c_macros = std.ArrayList([]const u8).init(builder.allocator), .output_dir = null, .out_basename = undefined, - .output_file = build.GeneratedFile{ .step = &self.step }, + .target = options.target, + .optimize = options.optimize, + .output_file = std.Build.GeneratedFile{ .step = &self.step }, }; source.addStepDependencies(&self.step); return self; } -pub fn setTarget(self: *TranslateCStep, target: CrossTarget) void { - self.target = target; -} +pub const AddExecutableOptions = struct { + name: ?[]const u8 = null, + version: ?std.builtin.Version = null, + target: ?CrossTarget = null, + optimize: ?std.builtin.Mode = null, + linkage: ?CompileStep.Linkage = null, +}; /// Creates a step to build an executable from the translated source. -pub fn addExecutable(self: *TranslateCStep) *LibExeObjStep { - return self.builder.addExecutableSource("translated_c", build.FileSource{ .generated = &self.output_file }); +pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *CompileStep { + return self.builder.addExecutable(.{ + .root_source_file = .{ .generated = &self.output_file }, + .name = options.name orelse "translated_c", + .version = options.version, + .target = options.target orelse self.target, + .optimize = options.optimize orelse self.optimize, + .linkage = options.linkage, + }); } pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void { - self.include_dirs.append(self.builder.dupePath(include_dir)) catch unreachable; + self.include_dirs.append(self.builder.dupePath(include_dir)) catch @panic("OOM"); } pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep { @@ -58,13 +77,13 @@ pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) /// If the value is omitted, it is set to 1. /// `name` and `value` need not live longer than the function call. pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void { - const macro = build.constructCMacro(self.builder.allocator, name, value); - self.c_macros.append(macro) catch unreachable; + const macro = std.Build.constructCMacro(self.builder.allocator, name, value); + self.c_macros.append(macro) catch @panic("OOM"); } /// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void { - self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable; + self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM"); } fn make(step: *Step) !void { @@ -82,6 +101,11 @@ fn make(step: *Step) !void { try argv_list.append(try self.target.zigTriple(self.builder.allocator)); } + switch (self.optimize) { + .Debug => {}, // Skip since it's the default. + else => try argv_list.append(self.builder.fmt("-O{s}", .{@tagName(self.optimize)})), + } + for (self.include_dirs.items) |include_dir| { try argv_list.append("-I"); try argv_list.append(include_dir); @@ -105,8 +129,8 @@ fn make(step: *Step) !void { self.output_dir = fs.path.dirname(output_path).?; } - self.output_file.path = fs.path.join( + self.output_file.path = try fs.path.join( self.builder.allocator, &[_][]const u8{ self.output_dir.?, self.out_basename }, - ) catch unreachable; + ); } diff --git a/lib/std/build/WriteFileStep.zig b/lib/std/Build/WriteFileStep.zig index 4faae8f74e..9e8fcdc203 100644 --- a/lib/std/build/WriteFileStep.zig +++ b/lib/std/Build/WriteFileStep.zig @@ -1,7 +1,5 @@ const std = @import("../std.zig"); -const build = @import("../build.zig"); -const Step = build.Step; -const Builder = build.Builder; +const Step = std.Build.Step; const fs = std.fs; const ArrayList = std.ArrayList; @@ -10,17 +8,17 @@ const WriteFileStep = @This(); pub const base_id = .write_file; step: Step, -builder: *Builder, +builder: *std.Build, output_dir: []const u8, files: std.TailQueue(File), pub const File = struct { - source: build.GeneratedFile, + source: std.Build.GeneratedFile, basename: []const u8, bytes: []const u8, }; -pub fn init(builder: *Builder) WriteFileStep { +pub fn init(builder: *std.Build) WriteFileStep { return WriteFileStep{ .builder = builder, .step = Step.init(.write_file, "writefile", builder.allocator, make), @@ -30,10 +28,10 @@ pub fn init(builder: *Builder) WriteFileStep { } pub fn add(self: *WriteFileStep, basename: []const u8, bytes: []const u8) void { - const node = self.builder.allocator.create(std.TailQueue(File).Node) catch unreachable; + const node = self.builder.allocator.create(std.TailQueue(File).Node) catch @panic("unhandled error"); node.* = .{ .data = .{ - .source = build.GeneratedFile{ .step = &self.step }, + .source = std.Build.GeneratedFile{ .step = &self.step }, .basename = self.builder.dupePath(basename), .bytes = self.builder.dupe(bytes), }, @@ -43,11 +41,11 @@ pub fn add(self: *WriteFileStep, basename: []const u8, bytes: []const u8) void { } /// Gets a file source for the given basename. If the file does not exist, returns `null`. -pub fn getFileSource(step: *WriteFileStep, basename: []const u8) ?build.FileSource { +pub fn getFileSource(step: *WriteFileStep, basename: []const u8) ?std.Build.FileSource { var it = step.files.first; while (it) |node| : (it = node.next) { if (std.mem.eql(u8, node.data.basename, basename)) - return build.FileSource{ .generated = &node.data.source }; + return std.Build.FileSource{ .generated = &node.data.source }; } return null; } @@ -108,10 +106,10 @@ fn make(step: *Step) !void { }); return err; }; - node.data.source.path = fs.path.join( + node.data.source.path = try fs.path.join( self.builder.allocator, &[_][]const u8{ self.output_dir, node.data.basename }, - ) catch unreachable; + ); } } } diff --git a/lib/std/build.zig b/lib/std/build.zig deleted file mode 100644 index 8137b76846..0000000000 --- a/lib/std/build.zig +++ /dev/null @@ -1,1781 +0,0 @@ -const std = @import("std.zig"); -const builtin = @import("builtin"); -const io = std.io; -const fs = std.fs; -const mem = std.mem; -const debug = std.debug; -const panic = std.debug.panic; -const assert = debug.assert; -const log = std.log; -const ArrayList = std.ArrayList; -const StringHashMap = std.StringHashMap; -const Allocator = mem.Allocator; -const process = std.process; -const EnvMap = std.process.EnvMap; -const fmt_lib = std.fmt; -const File = std.fs.File; -const CrossTarget = std.zig.CrossTarget; -const NativeTargetInfo = std.zig.system.NativeTargetInfo; -const Sha256 = std.crypto.hash.sha2.Sha256; -const ThisModule = @This(); - -pub const CheckFileStep = @import("build/CheckFileStep.zig"); -pub const CheckObjectStep = @import("build/CheckObjectStep.zig"); -pub const ConfigHeaderStep = @import("build/ConfigHeaderStep.zig"); -pub const EmulatableRunStep = @import("build/EmulatableRunStep.zig"); -pub const FmtStep = @import("build/FmtStep.zig"); -pub const InstallArtifactStep = @import("build/InstallArtifactStep.zig"); -pub const InstallDirStep = @import("build/InstallDirStep.zig"); -pub const InstallFileStep = @import("build/InstallFileStep.zig"); -pub const InstallRawStep = @import("build/InstallRawStep.zig"); -pub const LibExeObjStep = @import("build/LibExeObjStep.zig"); -pub const LogStep = @import("build/LogStep.zig"); -pub const OptionsStep = @import("build/OptionsStep.zig"); -pub const RemoveDirStep = @import("build/RemoveDirStep.zig"); -pub const RunStep = @import("build/RunStep.zig"); -pub const TranslateCStep = @import("build/TranslateCStep.zig"); -pub const WriteFileStep = @import("build/WriteFileStep.zig"); - -pub const Builder = struct { - install_tls: TopLevelStep, - uninstall_tls: TopLevelStep, - allocator: Allocator, - user_input_options: UserInputOptionsMap, - available_options_map: AvailableOptionsMap, - available_options_list: ArrayList(AvailableOption), - verbose: bool, - verbose_link: bool, - verbose_cc: bool, - verbose_air: bool, - verbose_llvm_ir: bool, - verbose_cimport: bool, - verbose_llvm_cpu_features: bool, - /// The purpose of executing the command is for a human to read compile errors from the terminal - prominent_compile_errors: bool, - color: enum { auto, on, off } = .auto, - reference_trace: ?u32 = null, - invalid_user_input: bool, - zig_exe: []const u8, - default_step: *Step, - env_map: *EnvMap, - top_level_steps: ArrayList(*TopLevelStep), - install_prefix: []const u8, - dest_dir: ?[]const u8, - lib_dir: []const u8, - exe_dir: []const u8, - h_dir: []const u8, - install_path: []const u8, - sysroot: ?[]const u8 = null, - search_prefixes: ArrayList([]const u8), - libc_file: ?[]const u8 = null, - installed_files: ArrayList(InstalledFile), - /// Path to the directory containing build.zig. - build_root: []const u8, - cache_root: []const u8, - global_cache_root: []const u8, - release_mode: ?std.builtin.Mode, - is_release: bool, - /// zig lib dir - override_lib_dir: ?[]const u8, - vcpkg_root: VcpkgRoot = .unattempted, - pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null, - args: ?[][]const u8 = null, - debug_log_scopes: []const []const u8 = &.{}, - debug_compile_errors: bool = false, - - /// Experimental. Use system Darling installation to run cross compiled macOS build artifacts. - enable_darling: bool = false, - /// Use system QEMU installation to run cross compiled foreign architecture build artifacts. - enable_qemu: bool = false, - /// Darwin. Use Rosetta to run x86_64 macOS build artifacts on arm64 macOS. - enable_rosetta: bool = false, - /// Use system Wasmtime installation to run cross compiled wasm/wasi build artifacts. - enable_wasmtime: bool = false, - /// Use system Wine installation to run cross compiled Windows build artifacts. - enable_wine: bool = false, - /// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc, - /// this will be the directory $glibc-build-dir/install/glibcs - /// Given the example of the aarch64 target, this is the directory - /// that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`. - glibc_runtimes_dir: ?[]const u8 = null, - - /// Information about the native target. Computed before build() is invoked. - host: NativeTargetInfo, - - dep_prefix: []const u8 = "", - - pub const ExecError = error{ - ReadFailure, - ExitCodeFailure, - ProcessTerminated, - ExecNotSupported, - } || std.ChildProcess.SpawnError; - - pub const PkgConfigError = error{ - PkgConfigCrashed, - PkgConfigFailed, - PkgConfigNotInstalled, - PkgConfigInvalidOutput, - }; - - pub const PkgConfigPkg = struct { - name: []const u8, - desc: []const u8, - }; - - pub const CStd = enum { - C89, - C99, - C11, - }; - - const UserInputOptionsMap = StringHashMap(UserInputOption); - const AvailableOptionsMap = StringHashMap(AvailableOption); - - const AvailableOption = struct { - name: []const u8, - type_id: TypeId, - description: []const u8, - /// If the `type_id` is `enum` this provides the list of enum options - enum_options: ?[]const []const u8, - }; - - const UserInputOption = struct { - name: []const u8, - value: UserValue, - used: bool, - }; - - const UserValue = union(enum) { - flag: void, - scalar: []const u8, - list: ArrayList([]const u8), - }; - - const TypeId = enum { - bool, - int, - float, - @"enum", - string, - list, - }; - - const TopLevelStep = struct { - pub const base_id = .top_level; - - step: Step, - description: []const u8, - }; - - pub const DirList = struct { - lib_dir: ?[]const u8 = null, - exe_dir: ?[]const u8 = null, - include_dir: ?[]const u8 = null, - }; - - pub fn create( - allocator: Allocator, - zig_exe: []const u8, - build_root: []const u8, - cache_root: []const u8, - global_cache_root: []const u8, - ) !*Builder { - const env_map = try allocator.create(EnvMap); - env_map.* = try process.getEnvMap(allocator); - - const host = try NativeTargetInfo.detect(.{}); - - const self = try allocator.create(Builder); - self.* = Builder{ - .zig_exe = zig_exe, - .build_root = build_root, - .cache_root = try fs.path.relative(allocator, build_root, cache_root), - .global_cache_root = global_cache_root, - .verbose = false, - .verbose_link = false, - .verbose_cc = false, - .verbose_air = false, - .verbose_llvm_ir = false, - .verbose_cimport = false, - .verbose_llvm_cpu_features = false, - .prominent_compile_errors = false, - .invalid_user_input = false, - .allocator = allocator, - .user_input_options = UserInputOptionsMap.init(allocator), - .available_options_map = AvailableOptionsMap.init(allocator), - .available_options_list = ArrayList(AvailableOption).init(allocator), - .top_level_steps = ArrayList(*TopLevelStep).init(allocator), - .default_step = undefined, - .env_map = env_map, - .search_prefixes = ArrayList([]const u8).init(allocator), - .install_prefix = undefined, - .lib_dir = undefined, - .exe_dir = undefined, - .h_dir = undefined, - .dest_dir = env_map.get("DESTDIR"), - .installed_files = ArrayList(InstalledFile).init(allocator), - .install_tls = TopLevelStep{ - .step = Step.initNoOp(.top_level, "install", allocator), - .description = "Copy build artifacts to prefix path", - }, - .uninstall_tls = TopLevelStep{ - .step = Step.init(.top_level, "uninstall", allocator, makeUninstall), - .description = "Remove build artifacts from prefix path", - }, - .release_mode = null, - .is_release = false, - .override_lib_dir = null, - .install_path = undefined, - .args = null, - .host = host, - }; - try self.top_level_steps.append(&self.install_tls); - try self.top_level_steps.append(&self.uninstall_tls); - self.default_step = &self.install_tls.step; - return self; - } - - fn createChild( - parent: *Builder, - dep_name: []const u8, - build_root: []const u8, - args: anytype, - ) !*Builder { - const child = try createChildOnly(parent, dep_name, build_root); - try applyArgs(child, args); - return child; - } - - fn createChildOnly(parent: *Builder, dep_name: []const u8, build_root: []const u8) !*Builder { - const allocator = parent.allocator; - const child = try allocator.create(Builder); - child.* = .{ - .allocator = allocator, - .install_tls = .{ - .step = Step.initNoOp(.top_level, "install", allocator), - .description = "Copy build artifacts to prefix path", - }, - .uninstall_tls = .{ - .step = Step.init(.top_level, "uninstall", allocator, makeUninstall), - .description = "Remove build artifacts from prefix path", - }, - .user_input_options = UserInputOptionsMap.init(allocator), - .available_options_map = AvailableOptionsMap.init(allocator), - .available_options_list = ArrayList(AvailableOption).init(allocator), - .verbose = parent.verbose, - .verbose_link = parent.verbose_link, - .verbose_cc = parent.verbose_cc, - .verbose_air = parent.verbose_air, - .verbose_llvm_ir = parent.verbose_llvm_ir, - .verbose_cimport = parent.verbose_cimport, - .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features, - .prominent_compile_errors = parent.prominent_compile_errors, - .color = parent.color, - .reference_trace = parent.reference_trace, - .invalid_user_input = false, - .zig_exe = parent.zig_exe, - .default_step = undefined, - .env_map = parent.env_map, - .top_level_steps = ArrayList(*TopLevelStep).init(allocator), - .install_prefix = undefined, - .dest_dir = parent.dest_dir, - .lib_dir = parent.lib_dir, - .exe_dir = parent.exe_dir, - .h_dir = parent.h_dir, - .install_path = parent.install_path, - .sysroot = parent.sysroot, - .search_prefixes = ArrayList([]const u8).init(allocator), - .libc_file = parent.libc_file, - .installed_files = ArrayList(InstalledFile).init(allocator), - .build_root = build_root, - .cache_root = parent.cache_root, - .global_cache_root = parent.global_cache_root, - .release_mode = parent.release_mode, - .is_release = parent.is_release, - .override_lib_dir = parent.override_lib_dir, - .debug_log_scopes = parent.debug_log_scopes, - .debug_compile_errors = parent.debug_compile_errors, - .enable_darling = parent.enable_darling, - .enable_qemu = parent.enable_qemu, - .enable_rosetta = parent.enable_rosetta, - .enable_wasmtime = parent.enable_wasmtime, - .enable_wine = parent.enable_wine, - .glibc_runtimes_dir = parent.glibc_runtimes_dir, - .host = parent.host, - .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }), - }; - try child.top_level_steps.append(&child.install_tls); - try child.top_level_steps.append(&child.uninstall_tls); - child.default_step = &child.install_tls.step; - return child; - } - - fn applyArgs(b: *Builder, args: anytype) !void { - // TODO this function is the way that a build.zig file communicates - // options to its dependencies. It is the programmatic way to give - // command line arguments to a build.zig script. - _ = args; - const Hasher = std.crypto.auth.siphash.SipHash128(1, 3); - // Random bytes to make unique. Refresh this with new random bytes when - // implementation is modified in a non-backwards-compatible way. - var hash = Hasher.init("ZaEsvQ5ClaA2IdH9"); - hash.update(b.dep_prefix); - // TODO additionally update the hash with `args`. - - var digest: [16]u8 = undefined; - hash.final(&digest); - var hash_basename: [digest.len * 2]u8 = undefined; - _ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch - unreachable; - - const install_prefix = b.pathJoin(&.{ b.cache_root, "i", &hash_basename }); - b.resolveInstallPrefix(install_prefix, .{}); - } - - pub fn destroy(self: *Builder) void { - self.env_map.deinit(); - self.top_level_steps.deinit(); - self.allocator.destroy(self); - } - - /// This function is intended to be called by lib/build_runner.zig, not a build.zig file. - pub fn resolveInstallPrefix(self: *Builder, install_prefix: ?[]const u8, dir_list: DirList) void { - if (self.dest_dir) |dest_dir| { - self.install_prefix = install_prefix orelse "/usr"; - self.install_path = self.pathJoin(&.{ dest_dir, self.install_prefix }); - } else { - self.install_prefix = install_prefix orelse - (self.pathJoin(&.{ self.build_root, "zig-out" })); - self.install_path = self.install_prefix; - } - - var lib_list = [_][]const u8{ self.install_path, "lib" }; - var exe_list = [_][]const u8{ self.install_path, "bin" }; - var h_list = [_][]const u8{ self.install_path, "include" }; - - if (dir_list.lib_dir) |dir| { - if (std.fs.path.isAbsolute(dir)) lib_list[0] = self.dest_dir orelse ""; - lib_list[1] = dir; - } - - if (dir_list.exe_dir) |dir| { - if (std.fs.path.isAbsolute(dir)) exe_list[0] = self.dest_dir orelse ""; - exe_list[1] = dir; - } - - if (dir_list.include_dir) |dir| { - if (std.fs.path.isAbsolute(dir)) h_list[0] = self.dest_dir orelse ""; - h_list[1] = dir; - } - - self.lib_dir = self.pathJoin(&lib_list); - self.exe_dir = self.pathJoin(&exe_list); - self.h_dir = self.pathJoin(&h_list); - } - - fn convertOptionalPathToFileSource(path: ?[]const u8) ?FileSource { - return if (path) |p| - FileSource{ .path = p } - else - null; - } - - pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { - return addExecutableSource(self, name, convertOptionalPathToFileSource(root_src)); - } - - pub fn addExecutableSource(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { - return LibExeObjStep.createExecutable(builder, name, root_src); - } - - pub fn addOptions(self: *Builder) *OptionsStep { - return OptionsStep.create(self); - } - - pub fn addObject(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { - return addObjectSource(self, name, convertOptionalPathToFileSource(root_src)); - } - - pub fn addObjectSource(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { - return LibExeObjStep.createObject(builder, name, root_src); - } - - pub fn addSharedLibrary( - self: *Builder, - name: []const u8, - root_src: ?[]const u8, - kind: LibExeObjStep.SharedLibKind, - ) *LibExeObjStep { - return addSharedLibrarySource(self, name, convertOptionalPathToFileSource(root_src), kind); - } - - pub fn addSharedLibrarySource( - self: *Builder, - name: []const u8, - root_src: ?FileSource, - kind: LibExeObjStep.SharedLibKind, - ) *LibExeObjStep { - return LibExeObjStep.createSharedLibrary(self, name, root_src, kind); - } - - pub fn addStaticLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { - return addStaticLibrarySource(self, name, convertOptionalPathToFileSource(root_src)); - } - - pub fn addStaticLibrarySource(self: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { - return LibExeObjStep.createStaticLibrary(self, name, root_src); - } - - pub fn addTest(self: *Builder, root_src: []const u8) *LibExeObjStep { - return LibExeObjStep.createTest(self, "test", .{ .path = root_src }); - } - - pub fn addTestSource(self: *Builder, root_src: FileSource) *LibExeObjStep { - return LibExeObjStep.createTest(self, "test", root_src.dupe(self)); - } - - pub fn addTestExe(self: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep { - return LibExeObjStep.createTestExe(self, name, .{ .path = root_src }); - } - - pub fn addTestExeSource(self: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep { - return LibExeObjStep.createTestExe(self, name, root_src.dupe(self)); - } - - pub fn addAssemble(self: *Builder, name: []const u8, src: []const u8) *LibExeObjStep { - return addAssembleSource(self, name, .{ .path = src }); - } - - pub fn addAssembleSource(self: *Builder, name: []const u8, src: FileSource) *LibExeObjStep { - const obj_step = LibExeObjStep.createObject(self, name, null); - obj_step.addAssemblyFileSource(src.dupe(self)); - return obj_step; - } - - /// Initializes a RunStep with argv, which must at least have the path to the - /// executable. More command line arguments can be added with `addArg`, - /// `addArgs`, and `addArtifactArg`. - /// Be careful using this function, as it introduces a system dependency. - /// To run an executable built with zig build, see `LibExeObjStep.run`. - pub fn addSystemCommand(self: *Builder, argv: []const []const u8) *RunStep { - assert(argv.len >= 1); - const run_step = RunStep.create(self, self.fmt("run {s}", .{argv[0]})); - run_step.addArgs(argv); - return run_step; - } - - pub fn addConfigHeader( - b: *Builder, - source: FileSource, - style: ConfigHeaderStep.Style, - values: anytype, - ) *ConfigHeaderStep { - const config_header_step = ConfigHeaderStep.create(b, source, style); - config_header_step.addValues(values); - return config_header_step; - } - - /// Allocator.dupe without the need to handle out of memory. - pub fn dupe(self: *Builder, bytes: []const u8) []u8 { - return self.allocator.dupe(u8, bytes) catch unreachable; - } - - /// Duplicates an array of strings without the need to handle out of memory. - pub fn dupeStrings(self: *Builder, strings: []const []const u8) [][]u8 { - const array = self.allocator.alloc([]u8, strings.len) catch unreachable; - for (strings) |s, i| { - array[i] = self.dupe(s); - } - return array; - } - - /// Duplicates a path and converts all slashes to the OS's canonical path separator. - pub fn dupePath(self: *Builder, bytes: []const u8) []u8 { - const the_copy = self.dupe(bytes); - for (the_copy) |*byte| { - switch (byte.*) { - '/', '\\' => byte.* = fs.path.sep, - else => {}, - } - } - return the_copy; - } - - /// Duplicates a package recursively. - pub fn dupePkg(self: *Builder, package: Pkg) Pkg { - var the_copy = Pkg{ - .name = self.dupe(package.name), - .source = package.source.dupe(self), - }; - - if (package.dependencies) |dependencies| { - const new_dependencies = self.allocator.alloc(Pkg, dependencies.len) catch unreachable; - the_copy.dependencies = new_dependencies; - - for (dependencies) |dep_package, i| { - new_dependencies[i] = self.dupePkg(dep_package); - } - } - return the_copy; - } - - pub fn addWriteFile(self: *Builder, file_path: []const u8, data: []const u8) *WriteFileStep { - const write_file_step = self.addWriteFiles(); - write_file_step.add(file_path, data); - return write_file_step; - } - - pub fn addWriteFiles(self: *Builder) *WriteFileStep { - const write_file_step = self.allocator.create(WriteFileStep) catch unreachable; - write_file_step.* = WriteFileStep.init(self); - return write_file_step; - } - - pub fn addLog(self: *Builder, comptime format: []const u8, args: anytype) *LogStep { - const data = self.fmt(format, args); - const log_step = self.allocator.create(LogStep) catch unreachable; - log_step.* = LogStep.init(self, data); - return log_step; - } - - pub fn addRemoveDirTree(self: *Builder, dir_path: []const u8) *RemoveDirStep { - const remove_dir_step = self.allocator.create(RemoveDirStep) catch unreachable; - remove_dir_step.* = RemoveDirStep.init(self, dir_path); - return remove_dir_step; - } - - pub fn addFmt(self: *Builder, paths: []const []const u8) *FmtStep { - return FmtStep.create(self, paths); - } - - pub fn addTranslateC(self: *Builder, source: FileSource) *TranslateCStep { - return TranslateCStep.create(self, source.dupe(self)); - } - - pub fn version(self: *const Builder, major: u32, minor: u32, patch: u32) LibExeObjStep.SharedLibKind { - _ = self; - return .{ - .versioned = .{ - .major = major, - .minor = minor, - .patch = patch, - }, - }; - } - - pub fn make(self: *Builder, step_names: []const []const u8) !void { - try self.makePath(self.cache_root); - - var wanted_steps = ArrayList(*Step).init(self.allocator); - defer wanted_steps.deinit(); - - if (step_names.len == 0) { - try wanted_steps.append(self.default_step); - } else { - for (step_names) |step_name| { - const s = try self.getTopLevelStepByName(step_name); - try wanted_steps.append(s); - } - } - - for (wanted_steps.items) |s| { - try self.makeOneStep(s); - } - } - - pub fn getInstallStep(self: *Builder) *Step { - return &self.install_tls.step; - } - - pub fn getUninstallStep(self: *Builder) *Step { - return &self.uninstall_tls.step; - } - - fn makeUninstall(uninstall_step: *Step) anyerror!void { - const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step); - const self = @fieldParentPtr(Builder, "uninstall_tls", uninstall_tls); - - for (self.installed_files.items) |installed_file| { - const full_path = self.getInstallPath(installed_file.dir, installed_file.path); - if (self.verbose) { - log.info("rm {s}", .{full_path}); - } - fs.cwd().deleteTree(full_path) catch {}; - } - - // TODO remove empty directories - } - - fn makeOneStep(self: *Builder, s: *Step) anyerror!void { - if (s.loop_flag) { - log.err("Dependency loop detected:\n {s}", .{s.name}); - return error.DependencyLoopDetected; - } - s.loop_flag = true; - - for (s.dependencies.items) |dep| { - self.makeOneStep(dep) catch |err| { - if (err == error.DependencyLoopDetected) { - log.err(" {s}", .{s.name}); - } - return err; - }; - } - - s.loop_flag = false; - - try s.make(); - } - - fn getTopLevelStepByName(self: *Builder, name: []const u8) !*Step { - for (self.top_level_steps.items) |top_level_step| { - if (mem.eql(u8, top_level_step.step.name, name)) { - return &top_level_step.step; - } - } - log.err("Cannot run step '{s}' because it does not exist", .{name}); - return error.InvalidStepName; - } - - pub fn option(self: *Builder, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T { - const name = self.dupe(name_raw); - const description = self.dupe(description_raw); - const type_id = comptime typeToEnum(T); - const enum_options = if (type_id == .@"enum") blk: { - const fields = comptime std.meta.fields(T); - var options = ArrayList([]const u8).initCapacity(self.allocator, fields.len) catch unreachable; - - inline for (fields) |field| { - options.appendAssumeCapacity(field.name); - } - - break :blk options.toOwnedSlice() catch unreachable; - } else null; - const available_option = AvailableOption{ - .name = name, - .type_id = type_id, - .description = description, - .enum_options = enum_options, - }; - if ((self.available_options_map.fetchPut(name, available_option) catch unreachable) != null) { - panic("Option '{s}' declared twice", .{name}); - } - self.available_options_list.append(available_option) catch unreachable; - - const option_ptr = self.user_input_options.getPtr(name) orelse return null; - option_ptr.used = true; - switch (type_id) { - .bool => switch (option_ptr.value) { - .flag => return true, - .scalar => |s| { - if (mem.eql(u8, s, "true")) { - return true; - } else if (mem.eql(u8, s, "false")) { - return false; - } else { - log.err("Expected -D{s} to be a boolean, but received '{s}'\n", .{ name, s }); - self.markInvalidUserInput(); - return null; - } - }, - .list => { - log.err("Expected -D{s} to be a boolean, but received a list.\n", .{name}); - self.markInvalidUserInput(); - return null; - }, - }, - .int => switch (option_ptr.value) { - .flag => { - log.err("Expected -D{s} to be an integer, but received a boolean.\n", .{name}); - self.markInvalidUserInput(); - return null; - }, - .scalar => |s| { - const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) { - error.Overflow => { - log.err("-D{s} value {s} cannot fit into type {s}.\n", .{ name, s, @typeName(T) }); - self.markInvalidUserInput(); - return null; - }, - else => { - log.err("Expected -D{s} to be an integer of type {s}.\n", .{ name, @typeName(T) }); - self.markInvalidUserInput(); - return null; - }, - }; - return n; - }, - .list => { - log.err("Expected -D{s} to be an integer, but received a list.\n", .{name}); - self.markInvalidUserInput(); - return null; - }, - }, - .float => switch (option_ptr.value) { - .flag => { - log.err("Expected -D{s} to be a float, but received a boolean.\n", .{name}); - self.markInvalidUserInput(); - return null; - }, - .scalar => |s| { - const n = std.fmt.parseFloat(T, s) catch { - log.err("Expected -D{s} to be a float of type {s}.\n", .{ name, @typeName(T) }); - self.markInvalidUserInput(); - return null; - }; - return n; - }, - .list => { - log.err("Expected -D{s} to be a float, but received a list.\n", .{name}); - self.markInvalidUserInput(); - return null; - }, - }, - .@"enum" => switch (option_ptr.value) { - .flag => { - log.err("Expected -D{s} to be a string, but received a boolean.\n", .{name}); - self.markInvalidUserInput(); - return null; - }, - .scalar => |s| { - if (std.meta.stringToEnum(T, s)) |enum_lit| { - return enum_lit; - } else { - log.err("Expected -D{s} to be of type {s}.\n", .{ name, @typeName(T) }); - self.markInvalidUserInput(); - return null; - } - }, - .list => { - log.err("Expected -D{s} to be a string, but received a list.\n", .{name}); - self.markInvalidUserInput(); - return null; - }, - }, - .string => switch (option_ptr.value) { - .flag => { - log.err("Expected -D{s} to be a string, but received a boolean.\n", .{name}); - self.markInvalidUserInput(); - return null; - }, - .list => { - log.err("Expected -D{s} to be a string, but received a list.\n", .{name}); - self.markInvalidUserInput(); - return null; - }, - .scalar => |s| return s, - }, - .list => switch (option_ptr.value) { - .flag => { - log.err("Expected -D{s} to be a list, but received a boolean.\n", .{name}); - self.markInvalidUserInput(); - return null; - }, - .scalar => |s| { - return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch unreachable; - }, - .list => |lst| return lst.items, - }, - } - } - - pub fn step(self: *Builder, name: []const u8, description: []const u8) *Step { - const step_info = self.allocator.create(TopLevelStep) catch unreachable; - step_info.* = TopLevelStep{ - .step = Step.initNoOp(.top_level, name, self.allocator), - .description = self.dupe(description), - }; - self.top_level_steps.append(step_info) catch unreachable; - return &step_info.step; - } - - /// This provides the -Drelease option to the build user and does not give them the choice. - pub fn setPreferredReleaseMode(self: *Builder, mode: std.builtin.Mode) void { - if (self.release_mode != null) { - @panic("setPreferredReleaseMode must be called before standardReleaseOptions and may not be called twice"); - } - const description = self.fmt("Create a release build ({s})", .{@tagName(mode)}); - self.is_release = self.option(bool, "release", description) orelse false; - self.release_mode = if (self.is_release) mode else std.builtin.Mode.Debug; - } - - /// If you call this without first calling `setPreferredReleaseMode` then it gives the build user - /// the choice of what kind of release. - pub fn standardReleaseOptions(self: *Builder) std.builtin.Mode { - if (self.release_mode) |mode| return mode; - - const release_safe = self.option(bool, "release-safe", "Optimizations on and safety on") orelse false; - const release_fast = self.option(bool, "release-fast", "Optimizations on and safety off") orelse false; - const release_small = self.option(bool, "release-small", "Size optimizations on and safety off") orelse false; - - const mode = if (release_safe and !release_fast and !release_small) - std.builtin.Mode.ReleaseSafe - else if (release_fast and !release_safe and !release_small) - std.builtin.Mode.ReleaseFast - else if (release_small and !release_fast and !release_safe) - std.builtin.Mode.ReleaseSmall - else if (!release_fast and !release_safe and !release_small) - std.builtin.Mode.Debug - else x: { - log.err("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)\n", .{}); - self.markInvalidUserInput(); - break :x std.builtin.Mode.Debug; - }; - self.is_release = mode != .Debug; - self.release_mode = mode; - return mode; - } - - pub const StandardTargetOptionsArgs = struct { - whitelist: ?[]const CrossTarget = null, - - default_target: CrossTarget = CrossTarget{}, - }; - - /// Exposes standard `zig build` options for choosing a target. - pub fn standardTargetOptions(self: *Builder, args: StandardTargetOptionsArgs) CrossTarget { - const maybe_triple = self.option( - []const u8, - "target", - "The CPU architecture, OS, and ABI to build for", - ); - const mcpu = self.option([]const u8, "cpu", "Target CPU features to add or subtract"); - - if (maybe_triple == null and mcpu == null) { - return args.default_target; - } - - const triple = maybe_triple orelse "native"; - - var diags: CrossTarget.ParseOptions.Diagnostics = .{}; - const selected_target = CrossTarget.parse(.{ - .arch_os_abi = triple, - .cpu_features = mcpu, - .diagnostics = &diags, - }) catch |err| switch (err) { - error.UnknownCpuModel => { - log.err("Unknown CPU: '{s}'\nAvailable CPUs for architecture '{s}':", .{ - diags.cpu_name.?, - @tagName(diags.arch.?), - }); - for (diags.arch.?.allCpuModels()) |cpu| { - log.err(" {s}", .{cpu.name}); - } - self.markInvalidUserInput(); - return args.default_target; - }, - error.UnknownCpuFeature => { - log.err( - \\Unknown CPU feature: '{s}' - \\Available CPU features for architecture '{s}': - \\ - , .{ - diags.unknown_feature_name.?, - @tagName(diags.arch.?), - }); - for (diags.arch.?.allFeaturesList()) |feature| { - log.err(" {s}: {s}", .{ feature.name, feature.description }); - } - self.markInvalidUserInput(); - return args.default_target; - }, - error.UnknownOperatingSystem => { - log.err( - \\Unknown OS: '{s}' - \\Available operating systems: - \\ - , .{diags.os_name.?}); - inline for (std.meta.fields(std.Target.Os.Tag)) |field| { - log.err(" {s}", .{field.name}); - } - self.markInvalidUserInput(); - return args.default_target; - }, - else => |e| { - log.err("Unable to parse target '{s}': {s}\n", .{ triple, @errorName(e) }); - self.markInvalidUserInput(); - return args.default_target; - }, - }; - - const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch unreachable; - - if (args.whitelist) |list| whitelist_check: { - // Make sure it's a match of one of the list. - var mismatch_triple = true; - var mismatch_cpu_features = true; - var whitelist_item = CrossTarget{}; - for (list) |t| { - mismatch_cpu_features = true; - mismatch_triple = true; - - const t_triple = t.zigTriple(self.allocator) catch unreachable; - if (mem.eql(u8, t_triple, selected_canonicalized_triple)) { - mismatch_triple = false; - whitelist_item = t; - if (t.getCpuFeatures().isSuperSetOf(selected_target.getCpuFeatures())) { - mismatch_cpu_features = false; - break :whitelist_check; - } else { - break; - } - } - } - if (mismatch_triple) { - log.err("Chosen target '{s}' does not match one of the supported targets:", .{ - selected_canonicalized_triple, - }); - for (list) |t| { - const t_triple = t.zigTriple(self.allocator) catch unreachable; - log.err(" {s}", .{t_triple}); - } - } else { - assert(mismatch_cpu_features); - const whitelist_cpu = whitelist_item.getCpu(); - const selected_cpu = selected_target.getCpu(); - log.err("Chosen CPU model '{s}' does not match one of the supported targets:", .{ - selected_cpu.model.name, - }); - log.err(" Supported feature Set: ", .{}); - const all_features = whitelist_cpu.arch.allFeaturesList(); - var populated_cpu_features = whitelist_cpu.model.features; - populated_cpu_features.populateDependencies(all_features); - for (all_features) |feature, i_usize| { - const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize); - const in_cpu_set = populated_cpu_features.isEnabled(i); - if (in_cpu_set) { - log.err("{s} ", .{feature.name}); - } - } - log.err(" Remove: ", .{}); - for (all_features) |feature, i_usize| { - const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize); - const in_cpu_set = populated_cpu_features.isEnabled(i); - const in_actual_set = selected_cpu.features.isEnabled(i); - if (in_actual_set and !in_cpu_set) { - log.err("{s} ", .{feature.name}); - } - } - } - self.markInvalidUserInput(); - return args.default_target; - } - - return selected_target; - } - - pub fn addUserInputOption(self: *Builder, name_raw: []const u8, value_raw: []const u8) !bool { - const name = self.dupe(name_raw); - const value = self.dupe(value_raw); - const gop = try self.user_input_options.getOrPut(name); - if (!gop.found_existing) { - gop.value_ptr.* = UserInputOption{ - .name = name, - .value = .{ .scalar = value }, - .used = false, - }; - return false; - } - - // option already exists - switch (gop.value_ptr.value) { - .scalar => |s| { - // turn it into a list - var list = ArrayList([]const u8).init(self.allocator); - list.append(s) catch unreachable; - list.append(value) catch unreachable; - self.user_input_options.put(name, .{ - .name = name, - .value = .{ .list = list }, - .used = false, - }) catch unreachable; - }, - .list => |*list| { - // append to the list - list.append(value) catch unreachable; - self.user_input_options.put(name, .{ - .name = name, - .value = .{ .list = list.* }, - .used = false, - }) catch unreachable; - }, - .flag => { - log.warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.", .{ name, value, name }); - return true; - }, - } - return false; - } - - pub fn addUserInputFlag(self: *Builder, name_raw: []const u8) !bool { - const name = self.dupe(name_raw); - const gop = try self.user_input_options.getOrPut(name); - if (!gop.found_existing) { - gop.value_ptr.* = .{ - .name = name, - .value = .{ .flag = {} }, - .used = false, - }; - return false; - } - - // option already exists - switch (gop.value_ptr.value) { - .scalar => |s| { - log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, s }); - return true; - }, - .list => { - log.err("Flag '-D{s}' conflicts with multiple options of the same name.", .{name}); - return true; - }, - .flag => {}, - } - return false; - } - - fn typeToEnum(comptime T: type) TypeId { - return switch (@typeInfo(T)) { - .Int => .int, - .Float => .float, - .Bool => .bool, - .Enum => .@"enum", - else => switch (T) { - []const u8 => .string, - []const []const u8 => .list, - else => @compileError("Unsupported type: " ++ @typeName(T)), - }, - }; - } - - fn markInvalidUserInput(self: *Builder) void { - self.invalid_user_input = true; - } - - pub fn validateUserInputDidItFail(self: *Builder) bool { - // make sure all args are used - var it = self.user_input_options.iterator(); - while (it.next()) |entry| { - if (!entry.value_ptr.used) { - log.err("Invalid option: -D{s}\n", .{entry.key_ptr.*}); - self.markInvalidUserInput(); - } - } - - return self.invalid_user_input; - } - - pub fn spawnChild(self: *Builder, argv: []const []const u8) !void { - return self.spawnChildEnvMap(null, self.env_map, argv); - } - - fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { - if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd}); - for (argv) |arg| { - std.debug.print("{s} ", .{arg}); - } - std.debug.print("\n", .{}); - } - - pub fn spawnChildEnvMap(self: *Builder, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void { - if (self.verbose) { - printCmd(cwd, argv); - } - - if (!std.process.can_spawn) - return error.ExecNotSupported; - - var child = std.ChildProcess.init(argv, self.allocator); - child.cwd = cwd; - child.env_map = env_map; - - const term = child.spawnAndWait() catch |err| { - log.err("Unable to spawn {s}: {s}", .{ argv[0], @errorName(err) }); - return err; - }; - - switch (term) { - .Exited => |code| { - if (code != 0) { - log.err("The following command exited with error code {}:", .{code}); - printCmd(cwd, argv); - return error.UncleanExit; - } - }, - else => { - log.err("The following command terminated unexpectedly:", .{}); - printCmd(cwd, argv); - - return error.UncleanExit; - }, - } - } - - pub fn makePath(self: *Builder, path: []const u8) !void { - fs.cwd().makePath(self.pathFromRoot(path)) catch |err| { - log.err("Unable to create path {s}: {s}", .{ path, @errorName(err) }); - return err; - }; - } - - pub fn installArtifact(self: *Builder, artifact: *LibExeObjStep) void { - self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step); - } - - pub fn addInstallArtifact(self: *Builder, artifact: *LibExeObjStep) *InstallArtifactStep { - return InstallArtifactStep.create(self, artifact); - } - - ///`dest_rel_path` is relative to prefix path - pub fn installFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void { - self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .prefix, dest_rel_path).step); - } - - pub fn installDirectory(self: *Builder, options: InstallDirectoryOptions) void { - self.getInstallStep().dependOn(&self.addInstallDirectory(options).step); - } - - ///`dest_rel_path` is relative to bin path - pub fn installBinFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void { - self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .bin, dest_rel_path).step); - } - - ///`dest_rel_path` is relative to lib path - pub fn installLibFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void { - self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .lib, dest_rel_path).step); - } - - /// Output format (BIN vs Intel HEX) determined by filename - pub fn installRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { - const raw = self.addInstallRaw(artifact, dest_filename, options); - self.getInstallStep().dependOn(&raw.step); - return raw; - } - - ///`dest_rel_path` is relative to install prefix path - pub fn addInstallFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep { - return self.addInstallFileWithDir(source.dupe(self), .prefix, dest_rel_path); - } - - ///`dest_rel_path` is relative to bin path - pub fn addInstallBinFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep { - return self.addInstallFileWithDir(source.dupe(self), .bin, dest_rel_path); - } - - ///`dest_rel_path` is relative to lib path - pub fn addInstallLibFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep { - return self.addInstallFileWithDir(source.dupe(self), .lib, dest_rel_path); - } - - pub fn addInstallHeaderFile(b: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep { - return b.addInstallFileWithDir(.{ .path = src_path }, .header, dest_rel_path); - } - - pub fn addInstallRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { - return InstallRawStep.create(self, artifact, dest_filename, options); - } - - pub fn addInstallFileWithDir( - self: *Builder, - source: FileSource, - install_dir: InstallDir, - dest_rel_path: []const u8, - ) *InstallFileStep { - if (dest_rel_path.len == 0) { - panic("dest_rel_path must be non-empty", .{}); - } - const install_step = self.allocator.create(InstallFileStep) catch unreachable; - install_step.* = InstallFileStep.init(self, source.dupe(self), install_dir, dest_rel_path); - return install_step; - } - - pub fn addInstallDirectory(self: *Builder, options: InstallDirectoryOptions) *InstallDirStep { - const install_step = self.allocator.create(InstallDirStep) catch unreachable; - install_step.* = InstallDirStep.init(self, options); - return install_step; - } - - pub fn pushInstalledFile(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) void { - const file = InstalledFile{ - .dir = dir, - .path = dest_rel_path, - }; - self.installed_files.append(file.dupe(self)) catch unreachable; - } - - pub fn updateFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void { - if (self.verbose) { - log.info("cp {s} {s} ", .{ source_path, dest_path }); - } - const cwd = fs.cwd(); - const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{}); - if (self.verbose) switch (prev_status) { - .stale => log.info("# installed", .{}), - .fresh => log.info("# up-to-date", .{}), - }; - } - - pub fn truncateFile(self: *Builder, dest_path: []const u8) !void { - if (self.verbose) { - log.info("truncate {s}", .{dest_path}); - } - const cwd = fs.cwd(); - var src_file = cwd.createFile(dest_path, .{}) catch |err| switch (err) { - error.FileNotFound => blk: { - if (fs.path.dirname(dest_path)) |dirname| { - try cwd.makePath(dirname); - } - break :blk try cwd.createFile(dest_path, .{}); - }, - else => |e| return e, - }; - src_file.close(); - } - - pub fn pathFromRoot(self: *Builder, rel_path: []const u8) []u8 { - return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch unreachable; - } - - /// Shorthand for `std.fs.path.join(builder.allocator, paths) catch unreachable` - pub fn pathJoin(self: *Builder, paths: []const []const u8) []u8 { - return fs.path.join(self.allocator, paths) catch unreachable; - } - - pub fn fmt(self: *Builder, comptime format: []const u8, args: anytype) []u8 { - return fmt_lib.allocPrint(self.allocator, format, args) catch unreachable; - } - - pub fn findProgram(self: *Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 { - // TODO report error for ambiguous situations - const exe_extension = @as(CrossTarget, .{}).exeFileExt(); - for (self.search_prefixes.items) |search_prefix| { - for (names) |name| { - if (fs.path.isAbsolute(name)) { - return name; - } - const full_path = self.pathJoin(&.{ - search_prefix, - "bin", - self.fmt("{s}{s}", .{ name, exe_extension }), - }); - return fs.realpathAlloc(self.allocator, full_path) catch continue; - } - } - if (self.env_map.get("PATH")) |PATH| { - for (names) |name| { - if (fs.path.isAbsolute(name)) { - return name; - } - var it = mem.tokenize(u8, PATH, &[_]u8{fs.path.delimiter}); - while (it.next()) |path| { - const full_path = self.pathJoin(&.{ - path, - self.fmt("{s}{s}", .{ name, exe_extension }), - }); - return fs.realpathAlloc(self.allocator, full_path) catch continue; - } - } - } - for (names) |name| { - if (fs.path.isAbsolute(name)) { - return name; - } - for (paths) |path| { - const full_path = self.pathJoin(&.{ - path, - self.fmt("{s}{s}", .{ name, exe_extension }), - }); - return fs.realpathAlloc(self.allocator, full_path) catch continue; - } - } - return error.FileNotFound; - } - - pub fn execAllowFail( - self: *Builder, - argv: []const []const u8, - out_code: *u8, - stderr_behavior: std.ChildProcess.StdIo, - ) ExecError![]u8 { - assert(argv.len != 0); - - if (!std.process.can_spawn) - return error.ExecNotSupported; - - const max_output_size = 400 * 1024; - var child = std.ChildProcess.init(argv, self.allocator); - child.stdin_behavior = .Ignore; - child.stdout_behavior = .Pipe; - child.stderr_behavior = stderr_behavior; - child.env_map = self.env_map; - - try child.spawn(); - - const stdout = child.stdout.?.reader().readAllAlloc(self.allocator, max_output_size) catch { - return error.ReadFailure; - }; - errdefer self.allocator.free(stdout); - - const term = try child.wait(); - switch (term) { - .Exited => |code| { - if (code != 0) { - out_code.* = @truncate(u8, code); - return error.ExitCodeFailure; - } - return stdout; - }, - .Signal, .Stopped, .Unknown => |code| { - out_code.* = @truncate(u8, code); - return error.ProcessTerminated; - }, - } - } - - pub fn execFromStep(self: *Builder, argv: []const []const u8, src_step: ?*Step) ![]u8 { - assert(argv.len != 0); - - if (self.verbose) { - printCmd(null, argv); - } - - if (!std.process.can_spawn) { - if (src_step) |s| log.err("{s}...", .{s.name}); - log.err("Unable to spawn the following command: cannot spawn child process", .{}); - printCmd(null, argv); - std.os.abort(); - } - - var code: u8 = undefined; - return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) { - error.ExecNotSupported => { - if (src_step) |s| log.err("{s}...", .{s.name}); - log.err("Unable to spawn the following command: cannot spawn child process", .{}); - printCmd(null, argv); - std.os.abort(); - }, - error.FileNotFound => { - if (src_step) |s| log.err("{s}...", .{s.name}); - log.err("Unable to spawn the following command: file not found", .{}); - printCmd(null, argv); - std.os.exit(@truncate(u8, code)); - }, - error.ExitCodeFailure => { - if (src_step) |s| log.err("{s}...", .{s.name}); - if (self.prominent_compile_errors) { - log.err("The step exited with error code {d}", .{code}); - } else { - log.err("The following command exited with error code {d}:", .{code}); - printCmd(null, argv); - } - - std.os.exit(@truncate(u8, code)); - }, - error.ProcessTerminated => { - if (src_step) |s| log.err("{s}...", .{s.name}); - log.err("The following command terminated unexpectedly:", .{}); - printCmd(null, argv); - std.os.exit(@truncate(u8, code)); - }, - else => |e| return e, - }; - } - - pub fn exec(self: *Builder, argv: []const []const u8) ![]u8 { - return self.execFromStep(argv, null); - } - - pub fn addSearchPrefix(self: *Builder, search_prefix: []const u8) void { - self.search_prefixes.append(self.dupePath(search_prefix)) catch unreachable; - } - - pub fn getInstallPath(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) []const u8 { - assert(!fs.path.isAbsolute(dest_rel_path)); // Install paths must be relative to the prefix - const base_dir = switch (dir) { - .prefix => self.install_path, - .bin => self.exe_dir, - .lib => self.lib_dir, - .header => self.h_dir, - .custom => |path| self.pathJoin(&.{ self.install_path, path }), - }; - return fs.path.resolve( - self.allocator, - &[_][]const u8{ base_dir, dest_rel_path }, - ) catch unreachable; - } - - pub const Dependency = struct { - builder: *Builder, - - pub fn artifact(d: *Dependency, name: []const u8) *LibExeObjStep { - var found: ?*LibExeObjStep = null; - for (d.builder.install_tls.step.dependencies.items) |dep_step| { - const inst = dep_step.cast(InstallArtifactStep) orelse continue; - if (mem.eql(u8, inst.artifact.name, name)) { - if (found != null) panic("artifact name '{s}' is ambiguous", .{name}); - found = inst.artifact; - } - } - return found orelse { - for (d.builder.install_tls.step.dependencies.items) |dep_step| { - const inst = dep_step.cast(InstallArtifactStep) orelse continue; - log.info("available artifact: '{s}'", .{inst.artifact.name}); - } - panic("unable to find artifact '{s}'", .{name}); - }; - } - }; - - pub fn dependency(b: *Builder, name: []const u8, args: anytype) *Dependency { - const build_runner = @import("root"); - const deps = build_runner.dependencies; - - inline for (@typeInfo(deps.imports).Struct.decls) |decl| { - if (mem.startsWith(u8, decl.name, b.dep_prefix) and - mem.endsWith(u8, decl.name, name) and - decl.name.len == b.dep_prefix.len + name.len) - { - const build_zig = @field(deps.imports, decl.name); - const build_root = @field(deps.build_root, decl.name); - return dependencyInner(b, name, build_root, build_zig, args); - } - } - - const full_path = b.pathFromRoot("build.zig.ini"); - std.debug.print("no dependency named '{s}' in '{s}'\n", .{ name, full_path }); - std.process.exit(1); - } - - fn dependencyInner( - b: *Builder, - name: []const u8, - build_root: []const u8, - comptime build_zig: type, - args: anytype, - ) *Dependency { - const sub_builder = b.createChild(name, build_root, args) catch unreachable; - sub_builder.runBuild(build_zig) catch unreachable; - const dep = b.allocator.create(Dependency) catch unreachable; - dep.* = .{ .builder = sub_builder }; - return dep; - } - - pub fn runBuild(b: *Builder, build_zig: anytype) anyerror!void { - switch (@typeInfo(@typeInfo(@TypeOf(build_zig.build)).Fn.return_type.?)) { - .Void => build_zig.build(b), - .ErrorUnion => try build_zig.build(b), - else => @compileError("expected return type of build to be 'void' or '!void'"), - } - } -}; - -test "builder.findProgram compiles" { - if (builtin.os.tag == .wasi) return error.SkipZigTest; - - var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - defer arena.deinit(); - - const builder = try Builder.create( - arena.allocator(), - "zig", - "zig-cache", - "zig-cache", - "zig-cache", - ); - defer builder.destroy(); - _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null; -} - -pub const Pkg = struct { - name: []const u8, - source: FileSource, - dependencies: ?[]const Pkg = null, -}; - -/// 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 { - /// The step that generates the file - step: *Step, - - /// The path to the generated file. Must be either absolute or relative to the build root. - /// This value must be set in the `fn make()` of the `step` and must not be `null` afterwards. - path: ?[]const u8 = null, - - pub fn getPath(self: GeneratedFile) []const u8 { - return self.path orelse std.debug.panic( - "getPath() was called on a GeneratedFile that wasn't build yet. Is there a missing Step dependency on step '{s}'?", - .{self.step.name}, - ); - } -}; - -/// A file source is a reference to an existing or future file. -/// -pub const FileSource = union(enum) { - /// A plain file path, relative to build root or absolute. - path: []const u8, - - /// A file that is generated by an interface. Those files usually are - /// not available until built by a build step. - generated: *const GeneratedFile, - - /// Returns a new file source that will have a relative path to the build root guaranteed. - /// This should be preferred over setting `.path` directly as it documents that the files are in the project directory. - pub fn relative(path: []const u8) FileSource { - std.debug.assert(!std.fs.path.isAbsolute(path)); - return FileSource{ .path = path }; - } - - /// Returns a string that can be shown to represent the file source. - /// Either returns the path or `"generated"`. - pub fn getDisplayName(self: FileSource) []const u8 { - return switch (self) { - .path => self.path, - .generated => "generated", - }; - } - - /// Adds dependencies this file source implies to the given step. - pub fn addStepDependencies(self: FileSource, step: *Step) void { - switch (self) { - .path => {}, - .generated => |gen| step.dependOn(gen.step), - } - } - - /// Should only be called during make(), returns a path relative to the build root or absolute. - pub fn getPath(self: FileSource, builder: *Builder) []const u8 { - const path = switch (self) { - .path => |p| builder.pathFromRoot(p), - .generated => |gen| gen.getPath(), - }; - return path; - } - - /// Duplicates the file source for a given builder. - pub fn dupe(self: FileSource, b: *Builder) FileSource { - return switch (self) { - .path => |p| .{ .path = b.dupePath(p) }, - .generated => |gen| .{ .generated = gen }, - }; - } -}; - -/// Allocates a new string for assigning a value to a named macro. -/// If the value is omitted, it is set to 1. -/// `name` and `value` need not live longer than the function call. -pub fn constructCMacro(allocator: Allocator, name: []const u8, value: ?[]const u8) []const u8 { - var macro = allocator.alloc( - u8, - name.len + if (value) |value_slice| value_slice.len + 1 else 0, - ) catch |err| if (err == error.OutOfMemory) @panic("Out of memory") else unreachable; - mem.copy(u8, macro, name); - if (value) |value_slice| { - macro[name.len] = '='; - mem.copy(u8, macro[name.len + 1 ..], value_slice); - } - return macro; -} - -/// deprecated: use `InstallDirStep.Options` -pub const InstallDirectoryOptions = InstallDirStep.Options; - -pub const Step = struct { - id: Id, - name: []const u8, - makeFn: MakeFn, - dependencies: ArrayList(*Step), - loop_flag: bool, - done_flag: bool, - - const MakeFn = *const fn (self: *Step) anyerror!void; - - pub const Id = enum { - top_level, - lib_exe_obj, - install_artifact, - install_file, - install_dir, - log, - remove_dir, - fmt, - translate_c, - write_file, - run, - emulatable_run, - check_file, - check_object, - config_header, - install_raw, - options, - custom, - - pub fn Type(comptime id: Id) type { - return switch (id) { - .top_level => Builder.TopLevelStep, - .lib_exe_obj => LibExeObjStep, - .install_artifact => InstallArtifactStep, - .install_file => InstallFileStep, - .install_dir => InstallDirStep, - .log => LogStep, - .remove_dir => RemoveDirStep, - .fmt => FmtStep, - .translate_c => TranslateCStep, - .write_file => WriteFileStep, - .run => RunStep, - .emulatable_run => EmulatableRunStep, - .check_file => CheckFileStep, - .check_object => CheckObjectStep, - .config_header => ConfigHeaderStep, - .install_raw => InstallRawStep, - .options => OptionsStep, - .custom => @compileError("no type available for custom step"), - }; - } - }; - - pub fn init(id: Id, name: []const u8, allocator: Allocator, makeFn: MakeFn) Step { - return Step{ - .id = id, - .name = allocator.dupe(u8, name) catch unreachable, - .makeFn = makeFn, - .dependencies = ArrayList(*Step).init(allocator), - .loop_flag = false, - .done_flag = false, - }; - } - pub fn initNoOp(id: Id, name: []const u8, allocator: Allocator) Step { - return init(id, name, allocator, makeNoOp); - } - - pub fn make(self: *Step) !void { - if (self.done_flag) return; - - try self.makeFn(self); - self.done_flag = true; - } - - pub fn dependOn(self: *Step, other: *Step) void { - self.dependencies.append(other) catch unreachable; - } - - fn makeNoOp(self: *Step) anyerror!void { - _ = self; - } - - pub fn cast(step: *Step, comptime T: type) ?*T { - if (step.id == T.base_id) { - return @fieldParentPtr(T, "step", step); - } - return null; - } -}; - -pub const VcpkgRoot = union(VcpkgRootStatus) { - unattempted: void, - not_found: void, - found: []const u8, -}; - -pub const VcpkgRootStatus = enum { - unattempted, - not_found, - found, -}; - -pub const InstallDir = union(enum) { - prefix: void, - lib: void, - bin: void, - header: void, - /// A path relative to the prefix - custom: []const u8, - - /// Duplicates the install directory including the path if set to custom. - pub fn dupe(self: InstallDir, builder: *Builder) InstallDir { - if (self == .custom) { - // Written with this temporary to avoid RLS problems - const duped_path = builder.dupe(self.custom); - return .{ .custom = duped_path }; - } else { - return self; - } - } -}; - -pub const InstalledFile = struct { - dir: InstallDir, - path: []const u8, - - /// Duplicates the installed file path and directory. - pub fn dupe(self: InstalledFile, builder: *Builder) InstalledFile { - return .{ - .dir = self.dir.dupe(builder), - .path = builder.dupe(self.path), - }; - } -}; - -test "dupePkg()" { - if (builtin.os.tag == .wasi) return error.SkipZigTest; - - var arena = std.heap.ArenaAllocator.init(std.testing.allocator); - defer arena.deinit(); - var builder = try Builder.create( - arena.allocator(), - "test", - "test", - "test", - "test", - ); - defer builder.destroy(); - - var pkg_dep = Pkg{ - .name = "pkg_dep", - .source = .{ .path = "/not/a/pkg_dep.zig" }, - }; - var pkg_top = Pkg{ - .name = "pkg_top", - .source = .{ .path = "/not/a/pkg_top.zig" }, - .dependencies = &[_]Pkg{pkg_dep}, - }; - const dupe = builder.dupePkg(pkg_top); - - const original_deps = pkg_top.dependencies.?; - const dupe_deps = dupe.dependencies.?; - - // probably the same top level package details - try std.testing.expectEqualStrings(pkg_top.name, dupe.name); - - // probably the same dependencies - try std.testing.expectEqual(original_deps.len, dupe_deps.len); - try std.testing.expectEqual(original_deps[0].name, pkg_dep.name); - - // could segfault otherwise if pointers in duplicated package's fields are - // the same as those in stack allocated package's fields - try std.testing.expect(dupe_deps.ptr != original_deps.ptr); - try std.testing.expect(dupe.name.ptr != pkg_top.name.ptr); - try std.testing.expect(dupe.source.path.ptr != pkg_top.source.path.ptr); - try std.testing.expect(dupe_deps[0].name.ptr != pkg_dep.name.ptr); - try std.testing.expect(dupe_deps[0].source.path.ptr != pkg_dep.source.path.ptr); -} - -test { - _ = CheckFileStep; - _ = CheckObjectStep; - _ = EmulatableRunStep; - _ = FmtStep; - _ = InstallArtifactStep; - _ = InstallDirStep; - _ = InstallFileStep; - _ = InstallRawStep; - _ = LibExeObjStep; - _ = LogStep; - _ = OptionsStep; - _ = RemoveDirStep; - _ = RunStep; - _ = TranslateCStep; - _ = WriteFileStep; -} diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 4d949946d8..74c61d229b 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -131,13 +131,16 @@ pub const CodeModel = enum { /// This data structure is used by the Zig language code generation and /// therefore must be kept in sync with the compiler implementation. -pub const Mode = enum { +pub const OptimizeMode = enum { Debug, ReleaseSafe, ReleaseFast, ReleaseSmall, }; +/// Deprecated; use OptimizeMode. +pub const Mode = OptimizeMode; + /// This data structure is used by the Zig language code generation and /// therefore must be kept in sync with the compiler implementation. pub const CallingConvention = enum { diff --git a/lib/std/std.zig b/lib/std/std.zig index ba52784b45..40ba896569 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -9,6 +9,7 @@ pub const AutoArrayHashMapUnmanaged = array_hash_map.AutoArrayHashMapUnmanaged; pub const AutoHashMap = hash_map.AutoHashMap; pub const AutoHashMapUnmanaged = hash_map.AutoHashMapUnmanaged; pub const BoundedArray = @import("bounded_array.zig").BoundedArray; +pub const Build = @import("Build.zig"); pub const BufMap = @import("buf_map.zig").BufMap; pub const BufSet = @import("buf_set.zig").BufSet; pub const ChildProcess = @import("child_process.zig").ChildProcess; @@ -49,7 +50,6 @@ pub const array_hash_map = @import("array_hash_map.zig"); pub const atomic = @import("atomic.zig"); pub const base64 = @import("base64.zig"); pub const bit_set = @import("bit_set.zig"); -pub const build = @import("build.zig"); pub const builtin = @import("builtin.zig"); pub const c = @import("c.zig"); pub const coff = @import("coff.zig"); @@ -96,6 +96,9 @@ pub const wasm = @import("wasm.zig"); pub const zig = @import("zig.zig"); pub const start = @import("start.zig"); +/// deprecated: use `Build`. +pub const build = Build; + const root = @import("root"); const options_override = if (@hasDecl(root, "std_options")) root.std_options else struct {}; diff --git a/lib/std/target.zig b/lib/std/target.zig index 8ae175aac8..4429f8be2d 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1880,6 +1880,559 @@ pub const Target = struct { => 16, }; } + + pub const CType = enum { + short, + ushort, + int, + uint, + long, + ulong, + longlong, + ulonglong, + float, + double, + longdouble, + }; + + pub fn c_type_byte_size(t: Target, c_type: CType) u16 { + return switch (c_type) { + .short, + .ushort, + .int, + .uint, + .long, + .ulong, + .longlong, + .ulonglong, + => @divExact(c_type_bit_size(t, c_type), 8), + + .float => 4, + .double => 8, + + .longdouble => switch (c_type_bit_size(t, c_type)) { + 16 => 2, + 32 => 4, + 64 => 8, + 80 => @intCast(u16, mem.alignForward(10, c_type_alignment(t, .longdouble))), + 128 => 16, + else => unreachable, + }, + }; + } + + pub fn c_type_bit_size(target: Target, c_type: CType) u16 { + switch (target.os.tag) { + .freestanding, .other => switch (target.cpu.arch) { + .msp430 => switch (c_type) { + .short, .ushort, .int, .uint => return 16, + .float, .long, .ulong => return 32, + .longlong, .ulonglong, .double, .longdouble => return 64, + }, + .avr => switch (c_type) { + .short, .ushort, .int, .uint => return 16, + .long, .ulong, .float, .double, .longdouble => return 32, + .longlong, .ulonglong => return 64, + }, + .tce, .tcele => switch (c_type) { + .short, .ushort => return 16, + .int, .uint, .long, .ulong, .longlong, .ulonglong => return 32, + .float, .double, .longdouble => return 32, + }, + .mips64, .mips64el => switch (c_type) { + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => return if (target.abi != .gnuabin32) 64 else 32, + .longlong, .ulonglong, .double => return 64, + .longdouble => return 128, + }, + .x86_64 => switch (c_type) { + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => switch (target.abi) { + .gnux32, .muslx32 => return 32, + else => return 64, + }, + .longlong, .ulonglong, .double => return 64, + .longdouble => return 80, + }, + else => switch (c_type) { + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => return target.cpu.arch.ptrBitWidth(), + .longlong, .ulonglong, .double => return 64, + .longdouble => switch (target.cpu.arch) { + .x86 => switch (target.abi) { + .android => return 64, + else => return 80, + }, + + .powerpc, + .powerpcle, + .powerpc64, + .powerpc64le, + => switch (target.abi) { + .musl, + .musleabi, + .musleabihf, + .muslx32, + => return 64, + else => return 128, + }, + + .riscv32, + .riscv64, + .aarch64, + .aarch64_be, + .aarch64_32, + .s390x, + .sparc, + .sparc64, + .sparcel, + .wasm32, + .wasm64, + => return 128, + + else => return 64, + }, + }, + }, + + .linux, + .freebsd, + .netbsd, + .dragonfly, + .openbsd, + .wasi, + .emscripten, + .plan9, + .solaris, + .haiku, + .ananas, + .fuchsia, + .minix, + => switch (target.cpu.arch) { + .msp430 => switch (c_type) { + .short, .ushort, .int, .uint => return 16, + .long, .ulong, .float => return 32, + .longlong, .ulonglong, .double, .longdouble => return 64, + }, + .avr => switch (c_type) { + .short, .ushort, .int, .uint => return 16, + .long, .ulong, .float, .double, .longdouble => return 32, + .longlong, .ulonglong => return 64, + }, + .tce, .tcele => switch (c_type) { + .short, .ushort => return 16, + .int, .uint, .long, .ulong, .longlong, .ulonglong => return 32, + .float, .double, .longdouble => return 32, + }, + .mips64, .mips64el => switch (c_type) { + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => return if (target.abi != .gnuabin32) 64 else 32, + .longlong, .ulonglong, .double => return 64, + .longdouble => if (target.os.tag == .freebsd) return 64 else return 128, + }, + .x86_64 => switch (c_type) { + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => switch (target.abi) { + .gnux32, .muslx32 => return 32, + else => return 64, + }, + .longlong, .ulonglong, .double => return 64, + .longdouble => return 80, + }, + else => switch (c_type) { + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => return target.cpu.arch.ptrBitWidth(), + .longlong, .ulonglong, .double => return 64, + .longdouble => switch (target.cpu.arch) { + .x86 => switch (target.abi) { + .android => return 64, + else => return 80, + }, + + .powerpc, + .powerpcle, + => switch (target.abi) { + .musl, + .musleabi, + .musleabihf, + .muslx32, + => return 64, + else => switch (target.os.tag) { + .freebsd, .netbsd, .openbsd => return 64, + else => return 128, + }, + }, + + .powerpc64, + .powerpc64le, + => switch (target.abi) { + .musl, + .musleabi, + .musleabihf, + .muslx32, + => return 64, + else => switch (target.os.tag) { + .freebsd, .openbsd => return 64, + else => return 128, + }, + }, + + .riscv32, + .riscv64, + .aarch64, + .aarch64_be, + .aarch64_32, + .s390x, + .mips64, + .mips64el, + .sparc, + .sparc64, + .sparcel, + .wasm32, + .wasm64, + => return 128, + + else => return 64, + }, + }, + }, + + .windows, .uefi => switch (target.cpu.arch) { + .x86 => switch (c_type) { + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => return 32, + .longlong, .ulonglong, .double => return 64, + .longdouble => switch (target.abi) { + .gnu, .gnuilp32, .cygnus => return 80, + else => return 64, + }, + }, + .x86_64 => switch (c_type) { + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => switch (target.abi) { + .cygnus => return 64, + else => return 32, + }, + .longlong, .ulonglong, .double => return 64, + .longdouble => switch (target.abi) { + .gnu, .gnuilp32, .cygnus => return 80, + else => return 64, + }, + }, + else => switch (c_type) { + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => return 32, + .longlong, .ulonglong, .double => return 64, + .longdouble => return 64, + }, + }, + + .macos, .ios, .tvos, .watchos => switch (c_type) { + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => switch (target.cpu.arch) { + .x86, .arm, .aarch64_32 => return 32, + .x86_64 => switch (target.abi) { + .gnux32, .muslx32 => return 32, + else => return 64, + }, + else => return 64, + }, + .longlong, .ulonglong, .double => return 64, + .longdouble => switch (target.cpu.arch) { + .x86 => switch (target.abi) { + .android => return 64, + else => return 80, + }, + .x86_64 => return 80, + else => return 64, + }, + }, + + .nvcl, .cuda => switch (c_type) { + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => switch (target.cpu.arch) { + .nvptx => return 32, + .nvptx64 => return 64, + else => return 64, + }, + .longlong, .ulonglong, .double => return 64, + .longdouble => return 64, + }, + + .amdhsa, .amdpal => switch (c_type) { + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong, .longlong, .ulonglong, .double => return 64, + .longdouble => return 128, + }, + + .cloudabi, + .kfreebsd, + .lv2, + .zos, + .rtems, + .nacl, + .aix, + .ps4, + .ps5, + .elfiamcu, + .mesa3d, + .contiki, + .hermit, + .hurd, + .opencl, + .glsl450, + .vulkan, + .driverkit, + .shadermodel, + => @panic("TODO specify the C integer and float type sizes for this OS"), + } + } + + pub fn c_type_alignment(target: Target, c_type: CType) u16 { + // Overrides for unusual alignments + switch (target.cpu.arch) { + .avr => switch (c_type) { + .short, .ushort => return 2, + else => return 1, + }, + .x86 => switch (target.os.tag) { + .windows, .uefi => switch (c_type) { + .longlong, .ulonglong, .double => return 8, + .longdouble => switch (target.abi) { + .gnu, .gnuilp32, .cygnus => return 4, + else => return 8, + }, + else => {}, + }, + else => {}, + }, + else => {}, + } + + // Next-power-of-two-aligned, up to a maximum. + return @min( + std.math.ceilPowerOfTwoAssert(u16, (c_type_bit_size(target, c_type) + 7) / 8), + switch (target.cpu.arch) { + .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) { + .netbsd => switch (target.abi) { + .gnueabi, + .gnueabihf, + .eabi, + .eabihf, + .android, + .musleabi, + .musleabihf, + => 8, + + else => @as(u16, 4), + }, + .ios, .tvos, .watchos => 4, + else => 8, + }, + + .msp430, + .avr, + => 2, + + .arc, + .csky, + .x86, + .xcore, + .dxil, + .loongarch32, + .tce, + .tcele, + .le32, + .amdil, + .hsail, + .spir, + .spirv32, + .kalimba, + .shave, + .renderscript32, + .ve, + .spu_2, + => 4, + + .aarch64_32, + .amdgcn, + .amdil64, + .bpfel, + .bpfeb, + .hexagon, + .hsail64, + .loongarch64, + .m68k, + .mips, + .mipsel, + .sparc, + .sparcel, + .sparc64, + .lanai, + .le64, + .nvptx, + .nvptx64, + .r600, + .s390x, + .spir64, + .spirv64, + .renderscript64, + => 8, + + .aarch64, + .aarch64_be, + .mips64, + .mips64el, + .powerpc, + .powerpcle, + .powerpc64, + .powerpc64le, + .riscv32, + .riscv64, + .x86_64, + .wasm32, + .wasm64, + => 16, + }, + ); + } + + pub fn c_type_preferred_alignment(target: Target, c_type: CType) u16 { + // Overrides for unusual alignments + switch (target.cpu.arch) { + .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) { + .netbsd => switch (target.abi) { + .gnueabi, + .gnueabihf, + .eabi, + .eabihf, + .android, + .musleabi, + .musleabihf, + => {}, + + else => switch (c_type) { + .longdouble => return 4, + else => {}, + }, + }, + .ios, .tvos, .watchos => switch (c_type) { + .longdouble => return 4, + else => {}, + }, + else => {}, + }, + .arc => switch (c_type) { + .longdouble => return 4, + else => {}, + }, + .avr => switch (c_type) { + .int, .uint, .long, .ulong, .float, .longdouble => return 1, + .short, .ushort => return 2, + .double => return 4, + .longlong, .ulonglong => return 8, + }, + .x86 => switch (target.os.tag) { + .windows, .uefi => switch (c_type) { + .longdouble => switch (target.abi) { + .gnu, .gnuilp32, .cygnus => return 4, + else => return 8, + }, + else => {}, + }, + else => switch (c_type) { + .longdouble => return 4, + else => {}, + }, + }, + else => {}, + } + + // Next-power-of-two-aligned, up to a maximum. + return @min( + std.math.ceilPowerOfTwoAssert(u16, (c_type_bit_size(target, c_type) + 7) / 8), + switch (target.cpu.arch) { + .msp430 => @as(u16, 2), + + .csky, + .xcore, + .dxil, + .loongarch32, + .tce, + .tcele, + .le32, + .amdil, + .hsail, + .spir, + .spirv32, + .kalimba, + .shave, + .renderscript32, + .ve, + .spu_2, + => 4, + + .arc, + .arm, + .armeb, + .avr, + .thumb, + .thumbeb, + .aarch64_32, + .amdgcn, + .amdil64, + .bpfel, + .bpfeb, + .hexagon, + .hsail64, + .x86, + .loongarch64, + .m68k, + .mips, + .mipsel, + .sparc, + .sparcel, + .sparc64, + .lanai, + .le64, + .nvptx, + .nvptx64, + .r600, + .s390x, + .spir64, + .spirv64, + .renderscript64, + => 8, + + .aarch64, + .aarch64_be, + .mips64, + .mips64el, + .powerpc, + .powerpcle, + .powerpc64, + .powerpc64le, + .riscv32, + .riscv64, + .x86_64, + .wasm32, + .wasm64, + => 16, + }, + ); + } }; test { |
