ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
34.94k stars 2.55k forks source link

Unhelpful error message when building a shared library that does not have a root source file, but does have module imports #18890

Open kcbanner opened 9 months ago

kcbanner commented 9 months ago

Zig Version

0.12.0-dev.2701+d18f52197

Steps to Reproduce and Observed Behavior

This is a reduction of an issue I ran into in a project of mine:

const std = @import("std");

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

    const foo_module = b.addModule("foo", .{
        .root_source_file = .{ .path =  "src/foo.zig" },
    });

    const lib = b.addSharedLibrary(.{
        .name = "shared_no_root",
        .target = target,
        .optimize = optimize,
    });

    lib.linkLibC();
    lib.addCSourceFile(.{ .file = .{ .path = "src/lib.c" }, .flags = &.{} });
    lib.root_module.addImport("foo", foo_module);
    b.installArtifact(lib);
}
int foo() {
    return 42;
}

foo.zig is an empty file.

The result when building is:

C:\cygwin64\home\kcbanner\kit\zig\build-stage3-release\lib\zig\std\std.zig:198:22: error: unable to load '': IsDir
const root = @import("root");
                     ^~~~~~
error: the following command failed with 1 compilation errors:
C:\cygwin64\home\kcbanner\kit\zig\build-stage3-release\bin\zig.exe build-lib C:\cygwin64\home\kcbanner\temp\shared_no_root\src\lib.c -ODebug --dep foo -Mroot -Mfoo=C:\cygwin64\home\kcbanner\temp\shared_no_root\src\foo.zig -lc -femit-implib --cache-dir c:\cygwin64\home\kcbanner\temp\shared_no_root\zig-cache --global-cache-dir e:\dev\zig-cache --name shared_no_root -dynamic --listen=-
Build Summary: 0/3 steps succeeded; 1 failed (disable with --summary none)

This reduction is contrived, but I bumped into this in a real project. I build a shared library containing the C/C++ deps, and as part of this I call Package.link from zgui (one of the zig-gamedev libs), it does this:

pub const Package = struct {
    options: Options,
    zgui: *std.Build.Module,
    zgui_options: *std.Build.Module,
    zgui_c_cpp: *std.Build.Step.Compile,

    pub fn link(pkg: Package, exe: *std.Build.Step.Compile) void {
        exe.linkLibrary(pkg.zgui_c_cpp);
        exe.root_module.addImport("zgui", pkg.zgui);
        exe.root_module.addImport("zgui_options", pkg.zgui_options);
    }
};

I'm calling this to link the zgui_c_cpp shared library into my shared library, but it also adds imports. This is the part that causes the above issue, without adding zig imports, you don't hit the issue.

The issue can also be resolved by adding a blank .root_source_file to the shared library.

Expected Behavior

Either the zig imports are dropped (since there is no zig root source file), or a more helpful error message indicating the issue (no root source file exists, but there are zig imports).

jacobly0 commented 9 months ago

Two possible resolutions:

--- a/lib/std/Build/Module.zig
+++ b/lib/std/Build/Module.zig
@@ -244,6 +244,7 @@ pub fn create(owner: *std.Build, options: CreateOptions) *Module {

 /// Adds an existing module to be used with `@import`.
 pub fn addImport(m: *Module, name: []const u8, module: *Module) void {
+    assert(m.root_source_file != null);
     const b = m.owner;
     m.import_table.put(b.allocator, b.dupe(name), module) catch @panic("OOM");
--- a/src/main.zig
+++ b/src/main.zig
@@ -7950,7 +7950,9 @@ fn handleModArg(
     gop.value_ptr.* = .{
         .paths = p: {
             if (opt_root_src_orig) |root_src_orig| {
-                create_module.opts.have_zcu = true;
+                if (std.mem.eql(u8, mod_name, "root")) {
+                    create_module.opts.have_zcu = true;
+                }
                 const root_src = try introspect.resolvePath(arena, root_src_orig);
                 break :p .{
                     .root = .{