authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-02-27 01:31:09-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-02-27 01:31:09-05:00
logdea72d15da4fba909dc3ccb2e9dc5286372ac023
treedaea2909e476467396347897bc12b13ae2a37ff7
parentab381933c87bcc744058d25a876cfdc0d23fc674
parent43a949ee95ce72003bb2d2ee22e1f4b5aea96739
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #22898 from kristoff-it/deprecated-proposal

Implement `@deprecated`

17 files changed, 251 insertions(+), 15 deletions(-)

doc/langref.html.in+49-13
......@@ -2288,7 +2288,7 @@ or
22882288 {#code|test_aligned_struct_fields.zig#}
22892289
22902290 <p>
2291 Equating packed structs results in a comparison of the backing integer,
2291 Equating packed structs results in a comparison of the backing integer,
22922292 and only works for the `==` and `!=` operators.
22932293 </p>
22942294 {#code|test_packed_struct_equality.zig#}
......@@ -4086,7 +4086,7 @@ fn performFn(start_value: i32) i32 {
40864086 special-case syntax.
40874087 </p>
40884088 <p>
4089 Here is an example of a generic {#syntax#}List{#endsyntax#} data structure.
4089 Here is an example of a generic {#syntax#}List{#endsyntax#} data structure.
40904090 </p>
40914091 {#code|generic_data_structure.zig#}
40924092
......@@ -4291,10 +4291,10 @@ pub fn print(self: *Writer, arg0: []const u8, arg1: i32) !void {
42914291 <pre>{#syntax#}@addrSpaceCast(ptr: anytype) anytype{#endsyntax#}</pre>
42924292 <p>
42934293 Converts a pointer from one address space to another. The new address space is inferred
4294 based on the result type. Depending on the current target and address spaces, this cast
4295 may be a no-op, a complex operation, or illegal. If the cast is legal, then the resulting
4296 pointer points to the same memory location as the pointer operand. It is always valid to
4297 cast a pointer between the same address spaces.
4294 based on the result type. Depending on the current target and address spaces, this cast
4295 may be a no-op, a complex operation, or illegal. If the cast is legal, then the resulting
4296 pointer points to the same memory location as the pointer operand. It is always valid to
4297 cast a pointer between the same address spaces.
42984298 </p>
42994299 {#header_close#}
43004300 {#header_open|@addWithOverflow#}
......@@ -4307,7 +4307,7 @@ pub fn print(self: *Writer, arg0: []const u8, arg1: i32) !void {
43074307 <pre>{#syntax#}@alignCast(ptr: anytype) anytype{#endsyntax#}</pre>
43084308 <p>
43094309 {#syntax#}ptr{#endsyntax#} can be {#syntax#}*T{#endsyntax#}, {#syntax#}?*T{#endsyntax#}, or {#syntax#}[]T{#endsyntax#}.
4310 Changes the alignment of a pointer. The alignment to use is inferred based on the result type.
4310 Changes the alignment of a pointer. The alignment to use is inferred based on the result type.
43114311 </p>
43124312 <p>A {#link|pointer alignment safety check|Incorrect Pointer Alignment#} is added
43134313 to the generated code to make sure the pointer is aligned as promised.</p>
......@@ -4384,7 +4384,7 @@ comptime {
43844384 <pre>{#syntax#}@bitCast(value: anytype) anytype{#endsyntax#}</pre>
43854385 <p>
43864386 Converts a value of one type to another type. The return type is the
4387 inferred result type.
4387 inferred result type.
43884388 </p>
43894389 <p>
43904390 Asserts that {#syntax#}@sizeOf(@TypeOf(value)) == @sizeOf(DestType){#endsyntax#}.
......@@ -4741,6 +4741,42 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
47414741 {#see_also|@cVaArg|@cVaCopy|@cVaEnd#}
47424742 {#header_close#}
47434743
4744 {#header_open|@deprecated#}
4745 <pre>{#syntax#}@deprecated(value: anytype) @TypeOf(value){#endsyntax#}</pre>
4746 <pre>{#syntax#}@deprecated() void{#endsyntax#}</pre>
4747 <p>
4748 Marks a given code path as scheduled for removal. Evaluates to the same
4749 value passed in as argument, or the {#syntax#}void{#endsyntax#} value
4750 when given none.
4751 </p>
4752 <p>
4753 When a public declaration has been moved to a new location, the old
4754 location can be marked {#syntax#}@deprecated{#endsyntax#}:
4755 </p>
4756 {#syntax_block|zig|root.zig#}
4757pub const fooToBar = @deprecated(bar.fromFoo); // moved
4758 {#end_syntax_block#}
4759 <p>
4760 By default deprecated code paths are disallowed in a module defined by
4761 the root package but allowed in modules defined by the rest of the
4762 dependency tree. This behavior can be overridden by passing
4763 <code>-fallow-deprecated</code> or <code>-fno-allow-deprecated</code> to
4764 <code>zig build</code>.
4765 </p>
4766 <p>
4767 The purpose of {#syntax#}@deprecated{#endsyntax#} is to provide at least
4768 one version (a "grace period") of a package that supports both old and new APIs
4769 simultaneously, while providing tooling for programmers to discover what needs
4770 to be upgraded to migrate to the new API. Such a grace period has the key property
4771 that it allows a project's dependency tree to be upgraded <em>one package at a time</em>.
4772 </p>
4773 <p>
4774 Using {#syntax#}@deprecated{#endsyntax#} without an argument can be
4775 useful inside of conditionally compiled blocks:
4776 </p>
4777 {#code|test_deprecated_builtin.zig#}
4778 {#header_close#}
4779
47444780 {#header_open|@divExact#}
47454781 <pre>{#syntax#}@divExact(numerator: T, denominator: T) T{#endsyntax#}</pre>
47464782 <p>
......@@ -4855,8 +4891,8 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
48554891 <pre>{#syntax#}@errorCast(value: anytype) anytype{#endsyntax#}</pre>
48564892 <p>
48574893 Converts an error set or error union value from one error set to another error set. The return type is the
4858 inferred result type. Attempting to convert an error which is not in the destination error
4859 set results in safety-checked {#link|Illegal Behavior#}.
4894 inferred result type. Attempting to convert an error which is not in the destination error
4895 set results in safety-checked {#link|Illegal Behavior#}.
48604896 </p>
48614897 {#header_close#}
48624898
......@@ -4935,7 +4971,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
49354971 <pre>{#syntax#}@floatFromInt(int: anytype) anytype{#endsyntax#}</pre>
49364972 <p>
49374973 Converts an integer to the closest floating point representation. The return type is the inferred result type.
4938 To convert the other way, use {#link|@intFromFloat#}. This operation is legal
4974 To convert the other way, use {#link|@intFromFloat#}. This operation is legal
49394975 for all values of all integer types.
49404976 </p>
49414977 {#header_close#}
......@@ -5027,7 +5063,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
50275063 <pre>{#syntax#}@intCast(int: anytype) anytype{#endsyntax#}</pre>
50285064 <p>
50295065 Converts an integer to another integer while keeping the same numerical value.
5030 The return type is the inferred result type.
5066 The return type is the inferred result type.
50315067 Attempting to convert a number which is out of range of the destination type results in
50325068 safety-checked {#link|Illegal Behavior#}.
50335069 </p>
......@@ -5280,7 +5316,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
52805316 <pre>{#syntax#}@ptrFromInt(address: usize) anytype{#endsyntax#}</pre>
52815317 <p>
52825318 Converts an integer to a {#link|pointer|Pointers#}. The return type is the inferred result type.
5283 To convert the other way, use {#link|@intFromPtr#}. Casting an address of 0 to a destination type
5319 To convert the other way, use {#link|@intFromPtr#}. Casting an address of 0 to a destination type
52845320 which in not {#link|optional|Optional Pointers#} and does not have the {#syntax#}allowzero{#endsyntax#} attribute will result in a
52855321 {#link|Pointer Cast Invalid Null#} panic when runtime safety checks are enabled.
52865322 </p>
doc/langref/test_deprecated_builtin.zig created+22
......@@ -0,0 +1,22 @@
1test "deprecated code path" {
2 compute(.greedy, false, 42);
3}
4
5const Strategy = enum { greedy, expensive, fast };
6fn compute(comptime strat: Strategy, comptime foo: bool, bar: usize) void {
7 switch (strat) {
8 .greedy => {
9 // This combination turned out to be ineffective.
10 if (!foo) @deprecated(); // use fast strategy when foo is false
11 runGreedy(foo, bar);
12 },
13 .expensive => runExpensive(foo, bar),
14 .fast => runFast(foo, bar),
15 }
16}
17
18extern fn runGreedy(foo: bool, bar: usize) void;
19extern fn runExpensive(foo: bool, bar: usize) void;
20extern fn runFast(foo: bool, bar: usize) void;
21
22// test_error=deprecated
lib/compiler/build_runner.zig+8
......@@ -80,6 +80,7 @@ pub fn main() !void {
8080 .query = .{},
8181 .result = try std.zig.system.resolveTargetQuery(.{}),
8282 },
83 .root_builder = undefined, // populated below
8384 };
8485
8586 graph.cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() });
......@@ -94,6 +95,7 @@ pub fn main() !void {
9495 local_cache_directory,
9596 dependencies.root_deps,
9697 );
98 graph.root_builder = builder;
9799
98100 var targets = ArrayList([]const u8).init(arena);
99101 var debug_log_scopes = ArrayList([]const u8).init(arena);
......@@ -260,6 +262,10 @@ pub fn main() !void {
260262 graph.incremental = true;
261263 } else if (mem.eql(u8, arg, "-fno-incremental")) {
262264 graph.incremental = false;
265 } else if (mem.eql(u8, arg, "-fallow-deprecated")) {
266 graph.allow_deprecated = true;
267 } else if (mem.eql(u8, arg, "-fno-allow-deprecated")) {
268 graph.allow_deprecated = false;
263269 } else if (mem.eql(u8, arg, "-fwine")) {
264270 builder.enable_wine = true;
265271 } else if (mem.eql(u8, arg, "-fno-wine")) {
......@@ -1290,6 +1296,8 @@ fn usage(b: *std.Build, out_stream: anytype) !void {
12901296 \\ new Omit cached steps
12911297 \\ failures (Default) Only print failed steps
12921298 \\ none Do not print the build summary
1299 \\ -fallow-deprecated Allow usage of deprecated code for the entire build graph
1300 \\ -fno-allow-deprecated Disallow usage of deprecated code for the entire build graph
12931301 \\ -j<N> Limit concurrent jobs (default is to use all CPU cores)
12941302 \\ --maxrss <bytes> Limit memory usage (default is to use available memory)
12951303 \\ --skip-oom-steps Instead of failing, skip steps that would exceed --maxrss
lib/std/Build.zig+2
......@@ -121,6 +121,8 @@ pub const Graph = struct {
121121 random_seed: u32 = 0,
122122 dependency_cache: InitializedDepMap = .empty,
123123 allow_so_scripts: ?bool = null,
124 allow_deprecated: ?bool = null,
125 root_builder: *std.Build,
124126};
125127
126128const AvailableDeps = []const struct { []const u8, []const u8 };
lib/std/Build/Module.zig+4
......@@ -557,6 +557,10 @@ pub fn appendZigProcessFlags(
557557 try addFlag(zig_args, m.pic, "-fPIC", "-fno-PIC");
558558 try addFlag(zig_args, m.red_zone, "-mred-zone", "-mno-red-zone");
559559
560 // -fno-allow-deprecated is the CLI default, and not inherited, so only pass the flag if true.
561 const allow_deprecated = m.owner.graph.allow_deprecated orelse (m.owner.graph.root_builder != m.owner);
562 if (allow_deprecated == true) try zig_args.append("-fallow-deprecated");
563
560564 if (m.dwarf_format) |dwarf_format| {
561565 try zig_args.append(switch (dwarf_format) {
562566 .@"32" => "-gdwarf32",
lib/std/Build/Step/Options.zig+3
......@@ -514,6 +514,7 @@ test Options {
514514 .result = try std.zig.system.resolveTargetQuery(.{}),
515515 },
516516 .zig_lib_directory = std.Build.Cache.Directory.cwd(),
517 .root_builder = undefined,
517518 };
518519
519520 var builder = try std.Build.create(
......@@ -523,6 +524,8 @@ test Options {
523524 &.{},
524525 );
525526
527 graph.root_builder = builder;
528
526529 const options = builder.addOptions();
527530
528531 const KeywordEnum = enum {
lib/std/zig/AstGen.zig+13
......@@ -9695,6 +9695,19 @@ fn builtinCall(
96959695 .volatile_cast,
96969696 => return ptrCast(gz, scope, ri, node),
96979697
9698 .deprecated => {
9699 _ = try gz.addExtendedNodeSmall(.deprecated, node, 0);
9700 switch (params.len) {
9701 0 => return .void_value,
9702 1 => return expr(gz, scope, ri, params[0]),
9703 else => return astgen.failNode(
9704 node,
9705 "expected 0 or 1 argument, found {}",
9706 .{params.len},
9707 ),
9708 }
9709 },
9710
96989711 // zig fmt: off
96999712 .has_decl => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_decl),
97009713 .has_field => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_field),
lib/std/zig/AstRlAnnotate.zig+5-2
......@@ -817,8 +817,6 @@ fn blockExpr(astrl: *AstRlAnnotate, parent_block: ?*Block, ri: ResultInfo, node:
817817}
818818
819819fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.Node.Index, args: []const Ast.Node.Index) !bool {
820 _ = ri; // Currently, no builtin consumes its result location.
821
822820 const tree = astrl.tree;
823821 const main_tokens = tree.nodes.items(.main_token);
824822 const builtin_token = main_tokens[node];
......@@ -828,6 +826,11 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
828826 if (expected != args.len) return false;
829827 }
830828 switch (info.tag) {
829 .deprecated => if (args.len >= 1) {
830 return astrl.expr(args[0], block, ri);
831 } else {
832 return false;
833 },
831834 .import => return false,
832835 .branch_hint => {
833836 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
lib/std/zig/BuiltinFn.zig+9
......@@ -121,6 +121,7 @@ pub const Tag = enum {
121121 work_item_id,
122122 work_group_size,
123123 work_group_id,
124 deprecated,
124125};
125126
126127pub const EvalToError = enum {
......@@ -1016,6 +1017,14 @@ pub const list = list: {
10161017 .illegal_outside_function = true,
10171018 },
10181019 },
1020 .{
1021 "@deprecated",
1022 .{
1023 .tag = .deprecated,
1024 .param_count = null,
1025 .eval_to_error = .maybe,
1026 },
1027 },
10191028 });
10201029};
10211030
lib/std/zig/Zir.zig+4
......@@ -2112,6 +2112,9 @@ pub const Inst = struct {
21122112 /// any code may have gone here, avoiding false-positive "unreachable code" errors.
21132113 astgen_error,
21142114
2115 /// `operand` is `src_node: i32`.
2116 deprecated,
2117
21152118 pub const InstData = struct {
21162119 opcode: Extended,
21172120 small: u16,
......@@ -4363,6 +4366,7 @@ fn findTrackableInner(
43634366 .tuple_decl,
43644367 .dbg_empty_stmt,
43654368 .astgen_error,
4369 .deprecated,
43664370 => return,
43674371
43684372 // `@TypeOf` has a body.
src/Compilation.zig+1
......@@ -869,6 +869,7 @@ pub const cache_helpers = struct {
869869 hh.add(mod.sanitize_c);
870870 hh.add(mod.sanitize_thread);
871871 hh.add(mod.fuzz);
872 hh.add(mod.allow_deprecated);
872873 hh.add(mod.unwind_tables);
873874 hh.add(mod.structured_cfg);
874875 hh.add(mod.no_builtin);
src/Package/Module.zig+10
......@@ -27,6 +27,7 @@ red_zone: bool,
2727sanitize_c: bool,
2828sanitize_thread: bool,
2929fuzz: bool,
30allow_deprecated: bool,
3031unwind_tables: std.builtin.UnwindTables,
3132cc_argv: []const []const u8,
3233/// (SPIR-V) whether to generate a structured control flow graph or not
......@@ -95,6 +96,7 @@ pub const CreateOptions = struct {
9596 sanitize_c: ?bool = null,
9697 sanitize_thread: ?bool = null,
9798 fuzz: ?bool = null,
99 allow_deprecated: ?bool = null,
98100 structured_cfg: ?bool = null,
99101 no_builtin: ?bool = null,
100102 };
......@@ -234,6 +236,11 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
234236 break :b false;
235237 };
236238
239 const allow_deprecated = b: {
240 if (options.inherited.allow_deprecated) |x| break :b x;
241 break :b false;
242 };
243
237244 const code_model = b: {
238245 if (options.inherited.code_model) |x| break :b x;
239246 if (options.parent) |p| break :b p.code_model;
......@@ -380,6 +387,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
380387 .sanitize_c = sanitize_c,
381388 .sanitize_thread = sanitize_thread,
382389 .fuzz = fuzz,
390 .allow_deprecated = allow_deprecated,
383391 .unwind_tables = unwind_tables,
384392 .cc_argv = options.cc_argv,
385393 .structured_cfg = structured_cfg,
......@@ -474,6 +482,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
474482 .sanitize_c = sanitize_c,
475483 .sanitize_thread = sanitize_thread,
476484 .fuzz = fuzz,
485 .allow_deprecated = allow_deprecated,
477486 .unwind_tables = unwind_tables,
478487 .cc_argv = &.{},
479488 .structured_cfg = structured_cfg,
......@@ -532,6 +541,7 @@ pub fn createLimited(gpa: Allocator, options: LimitedOptions) Allocator.Error!*P
532541 .sanitize_c = undefined,
533542 .sanitize_thread = undefined,
534543 .fuzz = undefined,
544 .allow_deprecated = undefined,
535545 .unwind_tables = undefined,
536546 .cc_argv = undefined,
537547 .structured_cfg = undefined,
src/Sema.zig+11
......@@ -1091,6 +1091,7 @@ fn analyzeBodyInner(
10911091 const map = &sema.inst_map;
10921092 const tags = sema.code.instructions.items(.tag);
10931093 const datas = sema.code.instructions.items(.data);
1094 const mod = block.ownerModule();
10941095
10951096 var crash_info = crash_report.prepAnalyzeBody(sema, block, body);
10961097 crash_info.push();
......@@ -1404,6 +1405,16 @@ fn analyzeBodyInner(
14041405 i += 1;
14051406 continue;
14061407 },
1408 .deprecated => {
1409 if (!mod.allow_deprecated) {
1410 const src_node: i32 = @bitCast(extended.operand);
1411 const src = block.nodeOffset(src_node);
1412 return sema.fail(block, src, "reached deprecated code", .{});
1413 }
1414
1415 i += 1;
1416 continue;
1417 },
14071418 .disable_instrumentation => {
14081419 try sema.zirDisableInstrumentation();
14091420 i += 1;
src/main.zig+6
......@@ -520,6 +520,8 @@ const usage_build_generic =
520520 \\ -fno-sanitize-thread Disable Thread Sanitizer
521521 \\ -ffuzz Enable fuzz testing instrumentation
522522 \\ -fno-fuzz Disable fuzz testing instrumentation
523 \\ -fallow-deprecated Allow usage of deprecated code
524 \\ -fno-allow-deprecated Disallow usage of deprecated code
523525 \\ -funwind-tables Always produce unwind table entries for all functions
524526 \\ -fasync-unwind-tables Always produce asynchronous unwind table entries for all functions
525527 \\ -fno-unwind-tables Never produce unwind table entries
......@@ -1454,6 +1456,10 @@ fn buildOutputType(
14541456 mod_opts.fuzz = true;
14551457 } else if (mem.eql(u8, arg, "-fno-fuzz")) {
14561458 mod_opts.fuzz = false;
1459 } else if (mem.eql(u8, arg, "-fallow-deprecated")) {
1460 mod_opts.allow_deprecated = true;
1461 } else if (mem.eql(u8, arg, "-fno-allow-deprecated")) {
1462 mod_opts.allow_deprecated = false;
14571463 } else if (mem.eql(u8, arg, "-fllvm")) {
14581464 create_module.opts.use_llvm = true;
14591465 } else if (mem.eql(u8, arg, "-fno-llvm")) {
src/print_zir.zig+1
......@@ -535,6 +535,7 @@ const Writer = struct {
535535 .c_va_start,
536536 .in_comptime,
537537 .value_placeholder,
538 .deprecated,
538539 => try self.writeExtNode(stream, extended),
539540
540541 .builtin_src => {
test/cases/compile_errors/deprecated.zig created+9
......@@ -0,0 +1,9 @@
1const bad = @deprecated(42);
2
3pub export fn foo() usize {
4 return bad;
5}
6
7// error
8//
9// :1:13: error: reached deprecated code
test/tests.zig+94
......@@ -1176,6 +1176,100 @@ pub fn addCliTests(b: *std.Build) *Step {
11761176 step.dependOn(&cleanup.step);
11771177 }
11781178
1179 {
1180 // Test `zig build -fallow-deprecated`.
1181
1182 const deprecated_check: std.Build.Step.Run.StdIo.Check = .{
1183 .expect_stderr_match = "reached deprecated code",
1184 };
1185
1186 const tmp_path = b.makeTempPath();
1187
1188 // create custom main.zig file containing a deprecated decl
1189 {
1190 const new_main_src =
1191 \\const bad = @deprecated(42);
1192 \\
1193 \\pub fn main() u8 {
1194 \\ return bad;
1195 \\}
1196 \\
1197 \\test {
1198 \\ if (bad != 42) return error.Bad;
1199 \\}
1200 ;
1201
1202 var src_dir = std.fs.cwd().makeOpenPath(b.pathJoin(&.{ tmp_path, "src" }), .{}) catch @panic("unable to create tmp path");
1203 defer src_dir.close();
1204
1205 var main = src_dir.createFile("main.zig", .{}) catch @panic("unable to create main.zig");
1206 defer main.close();
1207
1208 main.writeAll(new_main_src) catch @panic("unable to write to main.zig");
1209 }
1210
1211 const init_exe = b.addSystemCommand(&.{ b.graph.zig_exe, "init" });
1212 init_exe.setCwd(.{ .cwd_relative = tmp_path });
1213 init_exe.setName("zig init");
1214 init_exe.expectStdOutEqual("");
1215 init_exe.expectStdErrEqual("info: created build.zig\n" ++
1216 "info: created build.zig.zon\n" ++
1217 "info: preserving already existing file: src" ++ s ++ "main.zig\n" ++
1218 "info: created src" ++ s ++ "root.zig\n");
1219
1220 const run_test_bad = b.addSystemCommand(&.{ b.graph.zig_exe, "build", "test", "--color", "off" });
1221 run_test_bad.setCwd(.{ .cwd_relative = tmp_path });
1222 run_test_bad.setName("zig build test");
1223 run_test_bad.expectExitCode(1);
1224 run_test_bad.expectStdOutEqual("");
1225 run_test_bad.addCheck(deprecated_check);
1226 run_test_bad.step.dependOn(&init_exe.step);
1227
1228 const run_test = b.addSystemCommand(&.{
1229 b.graph.zig_exe,
1230 "build",
1231 "test",
1232 "--color",
1233 "off",
1234 "-fallow-deprecated",
1235 });
1236 run_test.setCwd(.{ .cwd_relative = tmp_path });
1237 run_test.setName("zig build test");
1238 run_test.expectExitCode(0);
1239 run_test.expectStdOutEqual("");
1240 run_test.expectStdErrEqual("");
1241 run_test.step.dependOn(&init_exe.step);
1242
1243 const run_build_bad = b.addSystemCommand(&.{ b.graph.zig_exe, "build", "--color", "off" });
1244 run_build_bad.setCwd(.{ .cwd_relative = tmp_path });
1245 run_build_bad.setName("zig build test");
1246 run_build_bad.expectExitCode(1);
1247 run_build_bad.expectStdOutEqual("");
1248 run_build_bad.addCheck(deprecated_check);
1249 run_build_bad.step.dependOn(&init_exe.step);
1250
1251 const run_build = b.addSystemCommand(&.{
1252 b.graph.zig_exe,
1253 "build",
1254 "--color",
1255 "off",
1256 "-fallow-deprecated",
1257 });
1258 run_build.setCwd(.{ .cwd_relative = tmp_path });
1259 run_build.setName("zig build test");
1260 run_build.expectExitCode(0);
1261 run_build.expectStdOutEqual("");
1262 run_build.expectStdErrEqual("");
1263 run_build.step.dependOn(&init_exe.step);
1264
1265 const cleanup = b.addRemoveDirTree(.{ .cwd_relative = tmp_path });
1266 cleanup.step.dependOn(&run_test.step);
1267 cleanup.step.dependOn(&run_test_bad.step);
1268 cleanup.step.dependOn(&run_build.step);
1269 cleanup.step.dependOn(&run_build_bad.step);
1270
1271 step.dependOn(&cleanup.step);
1272 }
11791273 // Test Godbolt API
11801274 if (builtin.os.tag == .linux and builtin.cpu.arch == .x86_64) {
11811275 const tmp_path = b.makeTempPath();