No module available, even though I added import

My print-debugging skills are not strong enough to understand why I’m getting `no module available` error even though I clearly adding import.

    const wf = b.addWriteFiles();
    const shader_info_file = wf.add("shader_info.generated.zig", shader_info_file_text.items);
    const shader_info_module = b.createModule(.{ .root_source_file = shader_info_file, .target = target, .optimize = optimize });
    for (zig_shader_modules.items) |zip| {
        const name, const module = zip;
        // defer b.allocator.free(name);
        const exe_mod = b.createModule(.{ .root_source_file = b.path("src/shader_info_generator.zig"), .target = target, .optimize = optimize });
        exe_mod.addImport("shader", module);
        const exe = b.addExecutable(.{ .name = "shader_info_generator", .root_module = exe_mod });
        const exe_run = b.addRunArtifact(exe);
        const stdout = exe_run.captureStdOut();
        const s_name = b.fmt("{s}.shader", .{name});
        std.debug.print("adding module '{s}'\n", .{s_name});
        const info_module = b.createModule(.{ .root_source_file = stdout, .target = target, .optimize = optimize });
        shader_info_module.addImport(s_name, info_module);
    }
    for (shader_info_module.import_table.keys()) |k| {
        std.debug.print("import: '{s}'\n", .{k});
    }
    for (modules) |module| { // `modules` here is my main exe module
        module.addImport("shaders_info_generated", shader_info_module);
    }
> zig build
adding module 'test.shader'
adding module 'textured_quad.shader'
import: 'test.shader'
import: 'textured_quad.shader'
install
└─ install zig_sdl3
   └─ compile exe zig_sdl3 Debug native 1 errors
.zig-cache/o/9788381fafe38bea09f56d7e028d2e6c/shader_info.generated.zig:3:29: error: no module named 'test.shader' available within module 'shaders_info_generated'
pub const @"test" = @import("test.shader");
                            ^~~~~~~~~~~~~
referenced by:
    spirvShader__anon_22672: src/shader_helper.zig:8:18
    main: src/main.zig:14:38
    4 reference(s) hidden; use '-freference-trace=6' to see all references

P.S the whole thing is me trying to generate info for zig shaders - number of samplers, storage buffers, etc. In theory I could do it just by importing zig file and counting declaration, but I need to be able to get shader info by comptime name, but import only supports string literals. I tried to create a wrapper that simpy imports shader - one module for each shader, each with one import for one shader, and then generate file that import each wrapper (pub const @"test" = @import('test.shader')), but I got file exist in multiple module for the root file that imports each wrapper. So now i’m trying to hack it with this generator instead:

const std = @import("std");
/// shader - on of src/shaders/*.shader.zig files
const shader = @import("shader");

pub const vert = struct {
    pub const num_samplers = @typeInfo(shader.vertex.samplers).@"struct".decls.len;
    pub const num_storage_textures = @typeInfo(shader.vertex.storage_textures).@"struct".decls.len;
    pub const num_storage_buffers = @typeInfo(shader.vertex.storage_buffers).@"struct".decls.len;
    pub const num_uniform_buffers = @typeInfo(shader.vertex.uniform_buffers).@"struct".decls.len;
};

pub const frag = struct {
    pub const num_samplers = @typeInfo(shader.fragment.samplers).@"struct".decls.len;
    pub const num_storage_textures = @typeInfo(shader.fragment.storage_textures).@"struct".decls.len;
    pub const num_storage_buffers = @typeInfo(shader.fragment.storage_buffers).@"struct".decls.len;
    pub const num_uniform_buffers = @typeInfo(shader.fragment.uniform_buffers).@"struct".decls.len;
};

pub fn main() !void {
    const stdout = std.fs.File.stdout();
    var buffer: [1024]u8 = undefined;
    var writer = stdout.writer(&buffer);
    try writer.interface.print(
        \\pub const vert = struct {{
        \\    pub const num_samplers = {};
        \\    pub const num_storage_textures = {};
        \\    pub const num_storage_buffers = {};
        \\    pub const num_uniform_buffers = {};
        \\}};
        \\
        \\pub const frag = struct {{
        \\    pub const num_samplers = {};
        \\    pub const num_storage_textures = {};
        \\    pub const num_storage_buffers = {};
        \\    pub const num_uniform_buffers = {};
        \\}};
    , .{
        vert.num_samplers,
        vert.num_storage_textures,
        vert.num_storage_buffers,
        vert.num_uniform_buffers,

        frag.num_samplers,
        frag.num_storage_textures,
        frag.num_storage_buffers,
        frag.num_uniform_buffers,
    });
}

I am guessing that @import("test.shader") interprets test.shader as a filename instead of a module name.
Try to rename your test.* modules to test_*.

Nope.

> zig build
adding module 'test_shader'
adding module 'textured_quad_shader'
import: 'test_shader'
import: 'textured_quad_shader'
install
└─ install zig_sdl3
   └─ compile exe zig_sdl3 Debug native 1 errors
.zig-cache/o/985e5c7166e90b332d9410871984f251/shader_info.generated.zig:3:29: error: no module named 'test_shader' available within module 'shaders_info_generated'
pub const @"test" = @import("test_shader");
                            ^~~~~~~~~~~~~
referenced by:
    spirvShader__anon_22672: src/shader_helper.zig:8:18
    main: src/main.zig:14:38
    4 reference(s) hidden; use '-freference-trace=6' to see all references

I originally just tried to import shaders in the wrappers directly instead of generating modules, but it complained that the root file that imports each wrapper exists in multiple modules. That’s why I try to generate modules instead. Not sure if approach with generating files is better, but at least i’m not importing shader files that compiles to spirv directly into my main exe code (but they still in the generator).

    const wf = b.addWriteFiles();
    const generated_file = wf.add("shader_info.generated.zig", shader_info_file.items);
    const shaders_info_module = b.createModule(.{ .root_source_file = generated_file, .target = target, .optimize = optimize });
    for (zig_shader_modules.items) |zip| {
        const name, const module = zip;
        const wrapper_module = b.createModule(.{ .root_source_file = b.path("src/wrappers/shader_info.zig"), .target = target, .optimize = optimize });
        wrapper_module.addImport("shader", module);
        shaders_info_module.addImport(b.fmt("{s}.shader", .{name}), wrapper_module);
        b.allocator.free(name);
    }
    for (modules) |module| {
        module.addImport("shaders_info_generated", shaders_info_module);
    }
> zig build
install
└─ install zig_sdl3
   └─ compile exe zig_sdl3 Debug native 1 errors
.zig-cache/o/9788381fafe38bea09f56d7e028d2e6c/shader_info.generated.zig:1:1: error: file exists in modules 'test.shader0' and 'textured_quad.shader0'
.zig-cache/o/9788381fafe38bea09f56d7e028d2e6c/shader_info.generated.zig:1:1: note: files must belong to only one module
.zig-cache/o/9788381fafe38bea09f56d7e028d2e6c/shader_info.generated.zig:1:1: note: file is the root of module 'test.shader0'
.zig-cache/o/9788381fafe38bea09f56d7e028d2e6c/shader_info.generated.zig:1:1: note: file is the root of module 'textured_quad.shader0'

Wrapper (src/wrappers/shader_info.zig):

//! a wrapper for generated shader info

const std = @import("std");
/// shader - on of src/shaders/*.shader.zig files
const shader = @import("shader");

pub const vert = struct {
    pub const num_samplers = @typeInfo(shader.vertex.samplers).@"struct".decls.len;
    pub const num_storage_textures = @typeInfo(shader.vertex.storage_textures).@"struct".decls.len;
    pub const num_storage_buffers = @typeInfo(shader.vertex.storage_buffers).@"struct".decls.len;
    pub const num_uniform_buffers = @typeInfo(shader.vertex.uniform_buffers).@"struct".decls.len;
};

pub const frag = struct {
    pub const num_samplers = @typeInfo(shader.fragment.samplers).@"struct".decls.len;
    pub const num_storage_textures = @typeInfo(shader.fragment.storage_textures).@"struct".decls.len;
    pub const num_storage_buffers = @typeInfo(shader.fragment.storage_buffers).@"struct".decls.len;
    pub const num_uniform_buffers = @typeInfo(shader.fragment.uniform_buffers).@"struct".decls.len;
};

Sorry, it might be a stupid question, but I really have never been exposed to code generation.

Why is struct {{ used here? The multi-line text I saw in the official documentation seems never to have used escape

Because it’s in print, and print uses { for arguments

1 Like

Thank you. It was indeed a stupid question :head_shaking_vertically:

You don’t need to generate anything.
You can create one module that imports all the shaders and then use this module.
In shaders.zig import all zig files:

pub const shader1 = @import("shader1.zig");
pub const shader2 = @import("shader2.zig");
// ...

All identifiers must be pub.

Declare shaders.zig as shader module and the use it:

const shader = @import("shader");
_ = shader.shader1; // the contents of shader1.zig (shader1 is the pub symbol in the module)

That’s pretty much what i’m doing anyway, I’m just automating the process.
I guess I’m trying to get the shader available as soon as I create a file, without a need to add another import manually. I will switch to this approach if the solution with code generation turns out to be too bloated or non-obvious, but for now I’m trying to understand how to achieve more or less automatic process.
The end result i’m trying to achieve is this function, so I guess I can replace shaders_info here with shaders struct for that imports each shader, select correct one with @field and then do that @typeinfo magic to count declarations. But for now I’m kinda just trying to learn.

const std = @import("std");
const sdl = @import("sdl3");

// this is a generated shader info by build.zig
const shaders_info = @import("shaders_info_generated");

pub fn spirvShader(comptime name: [:0]const u8, comptime stage: sdl.gpu.ShaderStage) sdl.gpu.ShaderCreateInfo {
    const info = @field(shaders_info, name);
    const t_info = switch (stage) {
        .vertex => info.vert,
        .fragment => info.frag,
    };
    return .{
        .props = .{ .name = name },
        .code = @embedFile(name ++ ".shader"),
        .format = .{ .spirv = true },
        .stage = stage,
        .entry_point = switch (stage) {
            .vertex => "vert",
            .fragment => "frag",
        },
        .num_samplers = t_info.num_samplers,
        .num_storage_textures = t_info.num_storage_textures,
        .num_storage_buffers = t_info.num_storage_buffers,
        .num_uniform_buffers = t_info.num_uniform_buffers,
    };
}