Commit ae2345b742

Andrew Kelley <andrew@ziglang.org>
2019-07-09 19:31:42
zig build: add standardTargetOptions and deprecate setTarget
in favor of setTheTarget
1 parent 0389462
Changed files (1)
std/build.zig
@@ -380,10 +380,12 @@ pub const Builder = struct {
         switch (builtin.os) {
             .windows => {},
             else => {
-                const triple = (CrossTarget{
-                    .arch = builtin.arch,
-                    .os = builtin.os,
-                    .abi = builtin.abi,
+                const triple = (Target{
+                    .Cross = CrossTarget{
+                        .arch = builtin.arch,
+                        .os = builtin.os,
+                        .abi = builtin.abi,
+                    },
                 }).linuxTriple(self.allocator);
 
                 // TODO: $ ld --verbose | grep SEARCH_DIR
@@ -507,6 +509,26 @@ pub const Builder = struct {
         return mode;
     }
 
+    /// Exposes standard `zig build` options for choosing a target. Pass `null` to support all targets.
+    pub fn standardTargetOptions(self: *Builder, supported_targets: ?[]const Target) Target {
+        if (supported_targets) |target_list| {
+            // TODO detect multiple args and emit an error message
+            // there's probably a better way to collect the target
+            for (target_list) |targ| {
+                const targ_str = targ.zigTriple(self.allocator) catch unreachable;
+                const targ_desc = targ.allocDescription(self.allocator) catch unreachable;
+                const this_targ_opt = self.option(bool, targ_str, targ_desc) orelse false;
+                if (this_targ_opt) {
+                    return targ;
+                }
+            }
+            return Target.Native;
+        } else {
+            const target_str = self.option([]const u8, "target", "the target to build for") orelse return Target.Native;
+            return Target.parse(target_str) catch unreachable; // TODO better error message for bad target
+        }
+    }
+
     pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) !bool {
         const gop = try self.user_input_options.getOrPut(name);
         if (!gop.found_existing) {
@@ -858,67 +880,193 @@ pub const Builder = struct {
     }
 };
 
-const Version = struct {
+pub const Version = struct {
     major: u32,
     minor: u32,
     patch: u32,
 };
 
-const CrossTarget = struct {
+pub const CrossTarget = struct {
     arch: builtin.Arch,
     os: builtin.Os,
     abi: builtin.Abi,
+};
+
+pub const Target = union(enum) {
+    Native: void,
+    Cross: CrossTarget,
 
-    pub fn zigTriple(cross_target: CrossTarget, allocator: *Allocator) []u8 {
+    pub fn zigTriple(self: Target, allocator: *Allocator) ![]u8 {
         return std.fmt.allocPrint(
             allocator,
             "{}{}-{}-{}",
-            @tagName(cross_target.arch),
-            Target.archSubArchName(cross_target.arch),
-            @tagName(cross_target.os),
-            @tagName(cross_target.abi),
-        ) catch unreachable;
+            @tagName(self.getArch()),
+            Target.archSubArchName(self.getArch()),
+            @tagName(self.getOs()),
+            @tagName(self.getAbi()),
+        );
+    }
+
+    pub fn allocDescription(self: Target, allocator: *Allocator) ![]u8 {
+        // TODO is there anything else worthy of the description that is not
+        // already captured in the triple?
+        return self.zigTriple(allocator);
     }
 
-    pub fn linuxTriple(cross_target: CrossTarget, allocator: *Allocator) []u8 {
+    pub fn zigTripleNoSubArch(self: Target, allocator: *Allocator) ![]u8 {
         return std.fmt.allocPrint(
             allocator,
             "{}-{}-{}",
-            @tagName(cross_target.arch),
-            @tagName(cross_target.os),
-            @tagName(cross_target.abi),
-        ) catch unreachable;
+            @tagName(self.getArch()),
+            @tagName(self.getOs()),
+            @tagName(self.getAbi()),
+        );
     }
-};
 
-pub const Target = union(enum) {
-    Native: void,
-    Cross: CrossTarget,
+    pub fn linuxTriple(self: Target, allocator: *Allocator) ![]u8 {
+        return std.fmt.allocPrint(
+            allocator,
+            "{}-{}-{}",
+            @tagName(self.getArch()),
+            @tagName(self.getOs()),
+            @tagName(self.getAbi()),
+        );
+    }
+
+    pub fn parse(text: []const u8) !Target {
+        var it = mem.separate(text, "-");
+        const arch_name = it.next() orelse return error.MissingArchitecture;
+        const os_name = it.next() orelse return error.MissingOperatingSystem;
+        const abi_name = it.next();
+
+        var cross = CrossTarget{
+            .arch = try parseArchSub(arch_name),
+            .os = try parseOs(os_name),
+            .abi = undefined,
+        };
+        cross.abi = if (abi_name) |n| try parseAbi(n) else defaultAbi(cross.arch, cross.os);
+        return Target{ .Cross = cross };
+    }
+
+    pub fn defaultAbi(arch: builtin.Arch, target_os: builtin.Os) builtin.Abi {
+        switch (arch) {
+            .wasm32, .wasm64 => return .musl,
+            else => {},
+        }
+        switch (target_os) {
+            .freestanding,
+            .ananas,
+            .cloudabi,
+            .dragonfly,
+            .lv2,
+            .solaris,
+            .haiku,
+            .minix,
+            .rtems,
+            .nacl,
+            .cnk,
+            .aix,
+            .cuda,
+            .nvcl,
+            .amdhsa,
+            .ps4,
+            .elfiamcu,
+            .mesa3d,
+            .contiki,
+            .amdpal,
+            .zen,
+            .hermit,
+            => return .eabi,
+            .openbsd,
+            .macosx,
+            .freebsd,
+            .ios,
+            .tvos,
+            .watchos,
+            .fuchsia,
+            .kfreebsd,
+            .netbsd,
+            .hurd,
+            => return .gnu,
+            .windows,
+            .uefi,
+            => return .msvc,
+            .linux,
+            .wasi,
+            => return .musl,
+        }
+    }
+
+    pub const ParseArchSubError = error{
+        UnknownArchitecture,
+        UnknownSubArchitecture,
+    };
+
+    pub fn parseArchSub(text: []const u8) ParseArchSubError!builtin.Arch {
+        const info = @typeInfo(builtin.Arch);
+        inline for (info.Union.fields) |field| {
+            if (mem.eql(u8, text, field.name)) {
+                if (field.field_type == void) {
+                    return (builtin.Arch)(@field(builtin.Arch, field.name));
+                } else {
+                    const sub_info = @typeInfo(field.field_type);
+                    inline for (sub_info.Enum.fields) |sub_field| {
+                        const combined = field.name ++ sub_field.name;
+                        if (mem.eql(u8, text, combined)) {
+                            return @unionInit(builtin.Arch, field.name, @field(field.field_type, sub_field.name));
+                        }
+                    }
+                    return error.UnknownSubArchitecture;
+                }
+            }
+        }
+        return error.UnknownArchitecture;
+    }
+
+    pub fn parseOs(text: []const u8) !builtin.Os {
+        const info = @typeInfo(builtin.Os);
+        inline for (info.Enum.fields) |field| {
+            if (mem.eql(u8, text, field.name)) {
+                return @field(builtin.Os, field.name);
+            }
+        }
+        return error.UnknownOperatingSystem;
+    }
+
+    pub fn parseAbi(text: []const u8) !builtin.Abi {
+        const info = @typeInfo(builtin.Abi);
+        inline for (info.Enum.fields) |field| {
+            if (mem.eql(u8, text, field.name)) {
+                return @field(builtin.Abi, field.name);
+            }
+        }
+        return error.UnknownApplicationBinaryInterface;
+    }
 
     fn archSubArchName(arch: builtin.Arch) []const u8 {
         return switch (arch) {
-            builtin.Arch.arm => |sub| @tagName(sub),
-            builtin.Arch.armeb => |sub| @tagName(sub),
-            builtin.Arch.thumb => |sub| @tagName(sub),
-            builtin.Arch.thumbeb => |sub| @tagName(sub),
-            builtin.Arch.aarch64 => |sub| @tagName(sub),
-            builtin.Arch.aarch64_be => |sub| @tagName(sub),
-            builtin.Arch.kalimba => |sub| @tagName(sub),
+            .arm => |sub| @tagName(sub),
+            .armeb => |sub| @tagName(sub),
+            .thumb => |sub| @tagName(sub),
+            .thumbeb => |sub| @tagName(sub),
+            .aarch64 => |sub| @tagName(sub),
+            .aarch64_be => |sub| @tagName(sub),
+            .kalimba => |sub| @tagName(sub),
             else => "",
         };
     }
 
     pub fn subArchName(self: Target) []const u8 {
         switch (self) {
-            Target.Native => return archSubArchName(builtin.arch),
-            Target.Cross => |cross| return archSubArchName(cross.arch),
+            .Native => return archSubArchName(builtin.arch),
+            .Cross => |cross| return archSubArchName(cross.arch),
         }
     }
 
     pub fn oFileExt(self: Target) []const u8 {
         const abi = switch (self) {
-            Target.Native => builtin.abi,
-            Target.Cross => |t| t.abi,
+            .Native => builtin.abi,
+            .Cross => |t| t.abi,
         };
         return switch (abi) {
             builtin.Abi.msvc => ".obj",
@@ -942,15 +1090,22 @@ pub const Target = union(enum) {
 
     pub fn getOs(self: Target) builtin.Os {
         return switch (self) {
-            Target.Native => builtin.os,
-            Target.Cross => |t| t.os,
+            .Native => builtin.os,
+            .Cross => |t| t.os,
         };
     }
 
     pub fn getArch(self: Target) builtin.Arch {
         switch (self) {
-            Target.Native => return builtin.arch,
-            Target.Cross => |t| return t.arch,
+            .Native => return builtin.arch,
+            .Cross => |t| return t.arch,
+        }
+    }
+
+    pub fn getAbi(self: Target) builtin.Abi {
+        switch (self) {
+            .Native => return builtin.abi,
+            .Cross => |t| return t.abi,
         }
     }
 
@@ -1206,19 +1361,24 @@ pub const LibExeObjStep = struct {
         }
     }
 
+    /// Deprecated. Use `setTheTarget`.
     pub fn setTarget(
         self: *LibExeObjStep,
         target_arch: builtin.Arch,
         target_os: builtin.Os,
         target_abi: builtin.Abi,
     ) void {
-        self.target = Target{
+        return self.setTheTarget(Target{
             .Cross = CrossTarget{
                 .arch = target_arch,
                 .os = target_os,
                 .abi = target_abi,
             },
-        };
+        });
+    }
+
+    pub fn setTheTarget(self: *LibExeObjStep, target: Target) void {
+        self.target = target;
         self.computeOutFileNames();
     }
 
@@ -1595,9 +1755,9 @@ pub const LibExeObjStep = struct {
 
         switch (self.target) {
             Target.Native => {},
-            Target.Cross => |cross_target| {
+            Target.Cross => {
                 try zig_args.append("-target");
-                try zig_args.append(cross_target.zigTriple(builder.allocator));
+                try zig_args.append(self.target.zigTriple(builder.allocator) catch unreachable);
             },
         }