| ... | @@ -73,8 +73,6 @@ pub const Builder = struct { | ... | @@ -73,8 +73,6 @@ pub const Builder = struct { |
| 73 | build_root: []const u8, | 73 | build_root: []const u8, |
| 74 | cache_root: []const u8, | 74 | cache_root: []const u8, |
| 75 | global_cache_root: []const u8, | 75 | global_cache_root: []const u8, |
| 76 | release_mode: ?std.builtin.Mode, | | |
| 77 | is_release: bool, | | |
| 78 | /// zig lib dir | 76 | /// zig lib dir |
| 79 | override_lib_dir: ?[]const u8, | 77 | override_lib_dir: ?[]const u8, |
| 80 | vcpkg_root: VcpkgRoot = .unattempted, | 78 | vcpkg_root: VcpkgRoot = .unattempted, |
| ... | @@ -150,6 +148,7 @@ pub const Builder = struct { | ... | @@ -150,6 +148,7 @@ pub const Builder = struct { |
| 150 | flag: void, | 148 | flag: void, |
| 151 | scalar: []const u8, | 149 | scalar: []const u8, |
| 152 | list: ArrayList([]const u8), | 150 | list: ArrayList([]const u8), |
| | 151 | map: StringHashMap(*const UserValue), |
| 153 | }; | 152 | }; |
| 154 | | 153 | |
| 155 | const TypeId = enum { | 154 | const TypeId = enum { |
| ... | @@ -223,8 +222,6 @@ pub const Builder = struct { | ... | @@ -223,8 +222,6 @@ pub const Builder = struct { |
| 223 | .step = Step.init(.top_level, "uninstall", allocator, makeUninstall), | 222 | .step = Step.init(.top_level, "uninstall", allocator, makeUninstall), |
| 224 | .description = "Remove build artifacts from prefix path", | 223 | .description = "Remove build artifacts from prefix path", |
| 225 | }, | 224 | }, |
| 226 | .release_mode = null, | | |
| 227 | .is_release = false, | | |
| 228 | .override_lib_dir = null, | 225 | .override_lib_dir = null, |
| 229 | .install_path = undefined, | 226 | .install_path = undefined, |
| 230 | .args = null, | 227 | .args = null, |
| ... | @@ -291,8 +288,6 @@ pub const Builder = struct { | ... | @@ -291,8 +288,6 @@ pub const Builder = struct { |
| 291 | .build_root = build_root, | 288 | .build_root = build_root, |
| 292 | .cache_root = parent.cache_root, | 289 | .cache_root = parent.cache_root, |
| 293 | .global_cache_root = parent.global_cache_root, | 290 | .global_cache_root = parent.global_cache_root, |
| 294 | .release_mode = parent.release_mode, | | |
| 295 | .is_release = parent.is_release, | | |
| 296 | .override_lib_dir = parent.override_lib_dir, | 291 | .override_lib_dir = parent.override_lib_dir, |
| 297 | .debug_log_scopes = parent.debug_log_scopes, | 292 | .debug_log_scopes = parent.debug_log_scopes, |
| 298 | .debug_compile_errors = parent.debug_compile_errors, | 293 | .debug_compile_errors = parent.debug_compile_errors, |
| ... | @@ -312,10 +307,55 @@ pub const Builder = struct { | ... | @@ -312,10 +307,55 @@ pub const Builder = struct { |
| 312 | } | 307 | } |
| 313 | | 308 | |
| 314 | fn applyArgs(b: *Builder, args: anytype) !void { | 309 | fn applyArgs(b: *Builder, args: anytype) !void { |
| 315 | // TODO this function is the way that a build.zig file communicates | 310 | inline for (@typeInfo(@TypeOf(args)).Struct.fields) |field| { |
| 316 | // options to its dependencies. It is the programmatic way to give | 311 | const v = @field(args, field.name); |
| 317 | // command line arguments to a build.zig script. | 312 | const T = @TypeOf(v); |
| 318 | _ = args; | 313 | switch (T) { |
| | 314 | CrossTarget => { |
| | 315 | try b.user_input_options.put(field.name, .{ |
| | 316 | .name = field.name, |
| | 317 | .value = .{ .scalar = try v.zigTriple(b.allocator) }, |
| | 318 | .used = false, |
| | 319 | }); |
| | 320 | try b.user_input_options.put("cpu", .{ |
| | 321 | .name = "cpu", |
| | 322 | .value = .{ .scalar = try serializeCpu(b.allocator, v.getCpu()) }, |
| | 323 | .used = false, |
| | 324 | }); |
| | 325 | }, |
| | 326 | []const u8 => { |
| | 327 | try b.user_input_options.put(field.name, .{ |
| | 328 | .name = field.name, |
| | 329 | .value = .{ .scalar = v }, |
| | 330 | .used = false, |
| | 331 | }); |
| | 332 | }, |
| | 333 | else => switch (@typeInfo(T)) { |
| | 334 | .Bool => { |
| | 335 | try b.user_input_options.put(field.name, .{ |
| | 336 | .name = field.name, |
| | 337 | .value = .{ .scalar = if (v) "true" else "false" }, |
| | 338 | .used = false, |
| | 339 | }); |
| | 340 | }, |
| | 341 | .Enum => { |
| | 342 | try b.user_input_options.put(field.name, .{ |
| | 343 | .name = field.name, |
| | 344 | .value = .{ .scalar = @tagName(v) }, |
| | 345 | .used = false, |
| | 346 | }); |
| | 347 | }, |
| | 348 | .Int => { |
| | 349 | try b.user_input_options.put(field.name, .{ |
| | 350 | .name = field.name, |
| | 351 | .value = .{ .scalar = try std.fmt.allocPrint(b.allocator, "{d}", .{v}) }, |
| | 352 | .used = false, |
| | 353 | }); |
| | 354 | }, |
| | 355 | else => @compileError("option '" ++ field.name ++ "' has unsupported type: " ++ @typeName(T)), |
| | 356 | }, |
| | 357 | } |
| | 358 | } |
| 319 | const Hasher = std.crypto.auth.siphash.SipHash128(1, 3); | 359 | const Hasher = std.crypto.auth.siphash.SipHash128(1, 3); |
| 320 | // Random bytes to make unique. Refresh this with new random bytes when | 360 | // Random bytes to make unique. Refresh this with new random bytes when |
| 321 | // implementation is modified in a non-backwards-compatible way. | 361 | // implementation is modified in a non-backwards-compatible way. |
| ... | @@ -679,15 +719,19 @@ pub const Builder = struct { | ... | @@ -679,15 +719,19 @@ pub const Builder = struct { |
| 679 | return null; | 719 | return null; |
| 680 | } | 720 | } |
| 681 | }, | 721 | }, |
| 682 | .list => { | 722 | .list, .map => { |
| 683 | log.err("Expected -D{s} to be a boolean, but received a list.\n", .{name}); | 723 | log.err("Expected -D{s} to be a boolean, but received a {s}.\n", .{ |
| | 724 | name, @tagName(option_ptr.value), |
| | 725 | }); |
| 684 | self.markInvalidUserInput(); | 726 | self.markInvalidUserInput(); |
| 685 | return null; | 727 | return null; |
| 686 | }, | 728 | }, |
| 687 | }, | 729 | }, |
| 688 | .int => switch (option_ptr.value) { | 730 | .int => switch (option_ptr.value) { |
| 689 | .flag => { | 731 | .flag, .list, .map => { |
| 690 | log.err("Expected -D{s} to be an integer, but received a boolean.\n", .{name}); | 732 | log.err("Expected -D{s} to be an integer, but received a {s}.\n", .{ |
| | 733 | name, @tagName(option_ptr.value), |
| | 734 | }); |
| 691 | self.markInvalidUserInput(); | 735 | self.markInvalidUserInput(); |
| 692 | return null; | 736 | return null; |
| 693 | }, | 737 | }, |
| ... | @@ -706,15 +750,12 @@ pub const Builder = struct { | ... | @@ -706,15 +750,12 @@ pub const Builder = struct { |
| 706 | }; | 750 | }; |
| 707 | return n; | 751 | return n; |
| 708 | }, | 752 | }, |
| 709 | .list => { | | |
| 710 | log.err("Expected -D{s} to be an integer, but received a list.\n", .{name}); | | |
| 711 | self.markInvalidUserInput(); | | |
| 712 | return null; | | |
| 713 | }, | | |
| 714 | }, | 753 | }, |
| 715 | .float => switch (option_ptr.value) { | 754 | .float => switch (option_ptr.value) { |
| 716 | .flag => { | 755 | .flag, .map, .list => { |
| 717 | log.err("Expected -D{s} to be a float, but received a boolean.\n", .{name}); | 756 | log.err("Expected -D{s} to be a float, but received a {s}.\n", .{ |
| | 757 | name, @tagName(option_ptr.value), |
| | 758 | }); |
| 718 | self.markInvalidUserInput(); | 759 | self.markInvalidUserInput(); |
| 719 | return null; | 760 | return null; |
| 720 | }, | 761 | }, |
| ... | @@ -726,15 +767,12 @@ pub const Builder = struct { | ... | @@ -726,15 +767,12 @@ pub const Builder = struct { |
| 726 | }; | 767 | }; |
| 727 | return n; | 768 | return n; |
| 728 | }, | 769 | }, |
| 729 | .list => { | | |
| 730 | log.err("Expected -D{s} to be a float, but received a list.\n", .{name}); | | |
| 731 | self.markInvalidUserInput(); | | |
| 732 | return null; | | |
| 733 | }, | | |
| 734 | }, | 770 | }, |
| 735 | .@"enum" => switch (option_ptr.value) { | 771 | .@"enum" => switch (option_ptr.value) { |
| 736 | .flag => { | 772 | .flag, .map, .list => { |
| 737 | log.err("Expected -D{s} to be a string, but received a boolean.\n", .{name}); | 773 | log.err("Expected -D{s} to be an enum, but received a {s}.\n", .{ |
| | 774 | name, @tagName(option_ptr.value), |
| | 775 | }); |
| 738 | self.markInvalidUserInput(); | 776 | self.markInvalidUserInput(); |
| 739 | return null; | 777 | return null; |
| 740 | }, | 778 | }, |
| ... | @@ -747,28 +785,22 @@ pub const Builder = struct { | ... | @@ -747,28 +785,22 @@ pub const Builder = struct { |
| 747 | return null; | 785 | return null; |
| 748 | } | 786 | } |
| 749 | }, | 787 | }, |
| 750 | .list => { | | |
| 751 | log.err("Expected -D{s} to be a string, but received a list.\n", .{name}); | | |
| 752 | self.markInvalidUserInput(); | | |
| 753 | return null; | | |
| 754 | }, | | |
| 755 | }, | 788 | }, |
| 756 | .string => switch (option_ptr.value) { | 789 | .string => switch (option_ptr.value) { |
| 757 | .flag => { | 790 | .flag, .list, .map => { |
| 758 | log.err("Expected -D{s} to be a string, but received a boolean.\n", .{name}); | 791 | log.err("Expected -D{s} to be a string, but received a {s}.\n", .{ |
| 759 | self.markInvalidUserInput(); | 792 | name, @tagName(option_ptr.value), |
| 760 | return null; | 793 | }); |
| 761 | }, | | |
| 762 | .list => { | | |
| 763 | log.err("Expected -D{s} to be a string, but received a list.\n", .{name}); | | |
| 764 | self.markInvalidUserInput(); | 794 | self.markInvalidUserInput(); |
| 765 | return null; | 795 | return null; |
| 766 | }, | 796 | }, |
| 767 | .scalar => |s| return s, | 797 | .scalar => |s| return s, |
| 768 | }, | 798 | }, |
| 769 | .list => switch (option_ptr.value) { | 799 | .list => switch (option_ptr.value) { |
| 770 | .flag => { | 800 | .flag, .map => { |
| 771 | log.err("Expected -D{s} to be a list, but received a boolean.\n", .{name}); | 801 | log.err("Expected -D{s} to be a list, but received a {s}.\n", .{ |
| | 802 | name, @tagName(option_ptr.value), |
| | 803 | }); |
| 772 | self.markInvalidUserInput(); | 804 | self.markInvalidUserInput(); |
| 773 | return null; | 805 | return null; |
| 774 | }, | 806 | }, |
| ... | @@ -790,41 +822,24 @@ pub const Builder = struct { | ... | @@ -790,41 +822,24 @@ pub const Builder = struct { |
| 790 | return &step_info.step; | 822 | return &step_info.step; |
| 791 | } | 823 | } |
| 792 | | 824 | |
| 793 | /// This provides the -Drelease option to the build user and does not give them the choice. | 825 | pub const StandardOptimizeOptionOptions = struct { |
| 794 | pub fn setPreferredReleaseMode(self: *Builder, mode: std.builtin.Mode) void { | 826 | preferred_optimize_mode: ?std.builtin.Mode = null, |
| 795 | if (self.release_mode != null) { | 827 | }; |
| 796 | @panic("setPreferredReleaseMode must be called before standardReleaseOptions and may not be called twice"); | 828 | |
| | 829 | pub fn standardOptimizeOption(self: *Builder, options: StandardOptimizeOptionOptions) std.builtin.Mode { |
| | 830 | if (options.preferred_optimize_mode) |mode| { |
| | 831 | if (self.option(bool, "release", "optimize for end users") orelse false) { |
| | 832 | return mode; |
| | 833 | } else { |
| | 834 | return .Debug; |
| | 835 | } |
| | 836 | } else { |
| | 837 | return self.option( |
| | 838 | std.builtin.Mode, |
| | 839 | "optimize", |
| | 840 | "prioritize performance, safety, or binary size (-O flag)", |
| | 841 | ) orelse .Debug; |
| 797 | } | 842 | } |
| 798 | const description = self.fmt("Create a release build ({s})", .{@tagName(mode)}); | | |
| 799 | self.is_release = self.option(bool, "release", description) orelse false; | | |
| 800 | self.release_mode = if (self.is_release) mode else std.builtin.Mode.Debug; | | |
| 801 | } | | |
| 802 | | | |
| 803 | /// If you call this without first calling `setPreferredReleaseMode` then it gives the build user | | |
| 804 | /// the choice of what kind of release. | | |
| 805 | pub fn standardReleaseOptions(self: *Builder) std.builtin.Mode { | | |
| 806 | if (self.release_mode) |mode| return mode; | | |
| 807 | | | |
| 808 | const release_safe = self.option(bool, "release-safe", "Optimizations on and safety on") orelse false; | | |
| 809 | const release_fast = self.option(bool, "release-fast", "Optimizations on and safety off") orelse false; | | |
| 810 | const release_small = self.option(bool, "release-small", "Size optimizations on and safety off") orelse false; | | |
| 811 | | | |
| 812 | const mode = if (release_safe and !release_fast and !release_small) | | |
| 813 | std.builtin.Mode.ReleaseSafe | | |
| 814 | else if (release_fast and !release_safe and !release_small) | | |
| 815 | std.builtin.Mode.ReleaseFast | | |
| 816 | else if (release_small and !release_fast and !release_safe) | | |
| 817 | std.builtin.Mode.ReleaseSmall | | |
| 818 | else if (!release_fast and !release_safe and !release_small) | | |
| 819 | std.builtin.Mode.Debug | | |
| 820 | else x: { | | |
| 821 | log.err("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)\n", .{}); | | |
| 822 | self.markInvalidUserInput(); | | |
| 823 | break :x std.builtin.Mode.Debug; | | |
| 824 | }; | | |
| 825 | self.is_release = mode != .Debug; | | |
| 826 | self.release_mode = mode; | | |
| 827 | return mode; | | |
| 828 | } | 843 | } |
| 829 | | 844 | |
| 830 | pub const StandardTargetOptionsArgs = struct { | 845 | pub const StandardTargetOptionsArgs = struct { |
| ... | @@ -1004,6 +1019,11 @@ pub const Builder = struct { | ... | @@ -1004,6 +1019,11 @@ pub const Builder = struct { |
| 1004 | log.warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.", .{ name, value, name }); | 1019 | log.warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.", .{ name, value, name }); |
| 1005 | return true; | 1020 | return true; |
| 1006 | }, | 1021 | }, |
| | 1022 | .map => |*map| { |
| | 1023 | _ = map; |
| | 1024 | log.warn("TODO maps as command line arguments is not implemented yet.", .{}); |
| | 1025 | return true; |
| | 1026 | }, |
| 1007 | } | 1027 | } |
| 1008 | return false; | 1028 | return false; |
| 1009 | } | 1029 | } |
| ... | @@ -1026,7 +1046,7 @@ pub const Builder = struct { | ... | @@ -1026,7 +1046,7 @@ pub const Builder = struct { |
| 1026 | log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, s }); | 1046 | log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, s }); |
| 1027 | return true; | 1047 | return true; |
| 1028 | }, | 1048 | }, |
| 1029 | .list => { | 1049 | .list, .map => { |
| 1030 | log.err("Flag '-D{s}' conflicts with multiple options of the same name.", .{name}); | 1050 | log.err("Flag '-D{s}' conflicts with multiple options of the same name.", .{name}); |
| 1031 | return true; | 1051 | return true; |
| 1032 | }, | 1052 | }, |
| ... | @@ -1058,7 +1078,7 @@ pub const Builder = struct { | ... | @@ -1058,7 +1078,7 @@ pub const Builder = struct { |
| 1058 | var it = self.user_input_options.iterator(); | 1078 | var it = self.user_input_options.iterator(); |
| 1059 | while (it.next()) |entry| { | 1079 | while (it.next()) |entry| { |
| 1060 | if (!entry.value_ptr.used) { | 1080 | if (!entry.value_ptr.used) { |
| 1061 | log.err("Invalid option: -D{s}\n", .{entry.key_ptr.*}); | 1081 | log.err("Invalid option: -D{s}", .{entry.key_ptr.*}); |
| 1062 | self.markInvalidUserInput(); | 1082 | self.markInvalidUserInput(); |
| 1063 | } | 1083 | } |
| 1064 | } | 1084 | } |
| ... | @@ -1456,6 +1476,11 @@ pub const Builder = struct { | ... | @@ -1456,6 +1476,11 @@ pub const Builder = struct { |
| 1456 | ) *Dependency { | 1476 | ) *Dependency { |
| 1457 | const sub_builder = b.createChild(name, build_root, args) catch unreachable; | 1477 | const sub_builder = b.createChild(name, build_root, args) catch unreachable; |
| 1458 | sub_builder.runBuild(build_zig) catch unreachable; | 1478 | sub_builder.runBuild(build_zig) catch unreachable; |
| | 1479 | |
| | 1480 | if (sub_builder.validateUserInputDidItFail()) { |
| | 1481 | std.debug.dumpCurrentStackTrace(@returnAddress()); |
| | 1482 | } |
| | 1483 | |
| 1459 | const dep = b.allocator.create(Dependency) catch unreachable; | 1484 | const dep = b.allocator.create(Dependency) catch unreachable; |
| 1460 | dep.* = .{ .builder = sub_builder }; | 1485 | dep.* = .{ .builder = sub_builder }; |
| 1461 | return dep; | 1486 | return dep; |
| ... | @@ -1718,6 +1743,34 @@ pub const InstalledFile = struct { | ... | @@ -1718,6 +1743,34 @@ pub const InstalledFile = struct { |
| 1718 | } | 1743 | } |
| 1719 | }; | 1744 | }; |
| 1720 | | 1745 | |
| | 1746 | pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 { |
| | 1747 | // TODO this logic can disappear if cpu model + features becomes part of the target triple |
| | 1748 | const all_features = cpu.arch.allFeaturesList(); |
| | 1749 | var populated_cpu_features = cpu.model.features; |
| | 1750 | populated_cpu_features.populateDependencies(all_features); |
| | 1751 | |
| | 1752 | if (populated_cpu_features.eql(cpu.features)) { |
| | 1753 | // The CPU name alone is sufficient. |
| | 1754 | return cpu.model.name; |
| | 1755 | } else { |
| | 1756 | var mcpu_buffer = ArrayList(u8).init(allocator); |
| | 1757 | try mcpu_buffer.appendSlice(cpu.model.name); |
| | 1758 | |
| | 1759 | for (all_features) |feature, i_usize| { |
| | 1760 | const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize); |
| | 1761 | const in_cpu_set = populated_cpu_features.isEnabled(i); |
| | 1762 | const in_actual_set = cpu.features.isEnabled(i); |
| | 1763 | if (in_cpu_set and !in_actual_set) { |
| | 1764 | try mcpu_buffer.writer().print("-{s}", .{feature.name}); |
| | 1765 | } else if (!in_cpu_set and in_actual_set) { |
| | 1766 | try mcpu_buffer.writer().print("+{s}", .{feature.name}); |
| | 1767 | } |
| | 1768 | } |
| | 1769 | |
| | 1770 | return try mcpu_buffer.toOwnedSlice(); |
| | 1771 | } |
| | 1772 | } |
| | 1773 | |
| 1721 | test "dupePkg()" { | 1774 | test "dupePkg()" { |
| 1722 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | 1775 | if (builtin.os.tag == .wasi) return error.SkipZigTest; |
| 1723 | | 1776 | |