Why do I have to specify target when compiling a module

If I don’t specify target in my module declaration, I get an error:

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    // public exports
    // argparser
    const argparser_module = b.addModule("argparser", .{
        .root_source_file = b.path("src/argparser/root.zig"),
    });

    // zig build test-argparser
    const step_test_argparser_module = b.step("test-argparser", "run unit tests for argparser module");
    const argparser_module_unit_tests = b.addTest(.{ .root_module = argparser_module });
    const run_argparser_module_unit_tests = b.addRunArtifact(argparser_module_unit_tests);
    step_test_argparser_module.dependOn(&run_argparser_module_unit_tests.step);
jeff@jeff-debian:~/repos/gatorcat$ zig build test-argparser
thread 179492 panic: the root Module of a Compile step must be created with a known 'target' field
/home/jeff/.config/Code/User/globalStorage/ziglang.vscode-zig/zig/x86_64-linux-0.16.0/lib/std/Build/Step/Compile.zig:387:9: 0x1582016 in create (std.zig)
        @panic("the root Module of a Compile step must be created with a known 'target' field");
        ^
/home/jeff/.config/Code/User/globalStorage/ziglang.vscode-zig/zig/x86_64-linux-0.16.0/lib/std/Build.zig:882:19: 0x16ca128 in addTest (std.zig)
    return .create(b, .{
                  ^
/home/jeff/repos/gatorcat/build.zig:23:50: 0x157f52b in build (build.zig)
    const argparser_module_unit_tests = b.addTest(.{ .root_module = argparser_module });
                                                 ^
/home/jeff/.config/Code/User/globalStorage/ziglang.vscode-zig/zig/x86_64-linux-0.16.0/lib/std/Build.zig:2264:33: 0x157daba in runBuild__anon_34149 (std.zig)
        .void => build_zig.build(b),
                                ^
/home/jeff/.config/Code/User/globalStorage/ziglang.vscode-zig/zig/x86_64-linux-0.16.0/lib/compiler/build_runner.zig:463:29: 0x128e524 in main (build_runner.zig)
        try builder.runBuild(root);
                            ^
/home/jeff/.config/Code/User/globalStorage/ziglang.vscode-zig/zig/x86_64-linux-0.16.0/lib/std/start.zig:699:88: 0x1294040 in callMain (std.zig)
    if (fn_info.params[0].type.? == std.process.Init.Minimal) return wrapMain(root.main(.{
                                                                                       ^
/home/jeff/.config/Code/User/globalStorage/ziglang.vscode-zig/zig/x86_64-linux-0.16.0/lib/std/start.zig:190:5: 0x1278201 in _start (std.zig)
    asm volatile (switch (native_arch) {
    ^
error: the following build command terminated with signal ABRT:
.zig-cache/o/8591f77b64d2c25af47ae4462cd89120/build /home/jeff/.config/Code/User/globalStorage/ziglang.vscode-zig/zig/x86_64-linux-0.16.0/zig /home/jeff/.config/Code/User/globalStorage/ziglang.vscode-zig/zig/x86_64-linux-0.16.0/lib /home/jeff/repos/gatorcat .zig-cache /home/jeff/.cache/zig --seed 0x91912ac4 -Z6a263f3ee24db1e3 test-argparser

So I have to specify target like this:

const argparser_module = b.addModule("argparser", .{
        .root_source_file = b.path("src/argparser/root.zig"),
        .target = target,
    });
  1. Why is target required?
  2. Why is target required but not optimize?

It is only required for a root module, for imports it’s inherited from root. Now should modules even have a concept of optimize and target is better question to ask :grinning_face_with_smiling_eyes:

1 Like

The current implementation of the Zig build system at the underlying compilation level is to call processes like zig build-exe and zig build-obj. optimize is mainly used to add the -O [mode] parameter to their per-module compile options when calling these processes, and it is not used elsewhere.

Only the target of the root module is required to be provided. The necessity of per-module targets is discussed in this link:
Proposal: remove per-module targets · Issue #22285 · ziglang/zig

The reason why the targets of the root module are required is that the build system, in addition to passing them as process parameters, also uses the target information elsewhere, e.g., when determining the output file names.

I could see a use case for different optimize flags: have a well known library you know works well compiled in release mode while debugging your own code in debug mode. This would improve debug performance.
For different target I can’t think of a use case though.

Yes, but its target that is the focus. That one really doesn’t make sense to be per module.

The reason it is this way was to allow you to optimise code for multiple sets of CPU features. But that doesn’t work if you import that code normally as files can only exist in one module in an import tree.

So to make the intended use case work it has to be compiled separately and linked, at which point the target still doesn’t need to be per module.

Source for that was already linked, but here it is again.

Modules with different optimise modes does make sense for dependencies, and is irrelevant to the discussion beyond its association with target.