Search code examples
importbuildzig

How to import zig module from another module?


Assuming following folder structure:

src/
   lib.zig
   module_a/
      file_a.zig
      file_a_test.zig
   module_b/
      file_b.zig
      file_b_test.zig
   tools/
      tools.zig
      tools_test.zig

How should I structure my build.zig in order to be somehow able to use:

const tools = @import("tools")

in my two other modules: module_a and module_b?

I also want to be able to run the tests somehow, so they don't break, for example in this fassion:

zig test src/module_a/file_a_test.zig

This is NOT an application, I don't have a main.zig file. I can have a lib.zig file though somewhere under src directory if it's needed.

Tried to look for a simple answer on Reddit and on StackOverflow, but couldn't find any. All the solutions use either an old std.build.Pkg system, or b.addModule but with a main.zig build target.


Solution

  • Ok, so eventually I was able to figure this one out, but there could be some kind of a tutorial about this, because this was a trial and error.

    So I've managed to add a main testing file to my file structure and it now looks like this:

    src/
       lib.zig
       lib_test.zig
       module_a/
          file_a.zig
          file_a_test.zig
       module_b/
          file_b.zig
          file_b_test.zig
       tools/
          tools.zig
          tools_test.zig
    

    The content of lib_test.zig is as follows:

    const std = @import("std");
    
    test {
        _ = @import("module_a/file_a_test.zig");
        _ = @import("module_b/file_b_test.zig");
        _ = @import("tools/tools_test.zig");
    }
    
    test "my custom lib test" {
        try std.testing.assert(1 == 1)
    }
    

    And my content of build.zig is like this:

    const std = @import("std");
    
    pub fn build(b: *std.Build) void {
        const target = b.standardTargetOptions(.{});
        const optimize = b.standardOptimizeOption(.{});
    
        const tools = b.addModule("tools", .{ .source_file = .{ .path = "src/tools/tools.zig" } });
        const module_a = b.addModule("module-a", .{ .source_file = .{ .path = "src/module_a/file_a.zig" } });
        const module_b = b.addModule("module-b", .{ .source_file = .{ .path = "src/module_b/file_b.zig" } });
        
        const unit_tests = b.addTest(.{
            .root_source_file = .{ .path = "src/lib_test.zig" },
            .target = target,
            .optimize = optimize,
        });
        unit_tests.addModule("tools", tools);
        unit_tests.addModule("module-a", module_a);
        unit_tests.addModule("module-b", module_b);
        
        const run_unit_tests = b.addRunArtifact(unit_tests);
    
        const test_step = b.step("test", "Run unit tests");
        test_step.dependOn(&run_unit_tests.step);
    }
    

    So we have to create our tools module by addModule, and then when adding another modules to the build "namespace" we have to provide all the dependencies that those modules (i.e. .zig files) use, we do that by providing them to unit_tests, because unit tests are in fact "ran".

    We can gather all the tests into a single file with a special test syntax.

    After that to run the tests we do:

    zig build test
    

    The tests are ran every time there is a change in the code, not every time we invoke the command.