#Is it possible to link a module without its dependencies?

1 messages · Page 1 of 1 (latest)

mental rivet
#

In C/C++ you can access a library's type definitions without linking the library by just including the header files. That way you can know what a library's types and definitions are and as long as you don't call any functions from the library it all works out. I sort of want to do the same thing.

I have two executables, a build tool that creates an asset pack file, and the actual game that then decodes the asset pack file. The asset packer needs a ton of heavy dependencies like glslang and zstbi and libvorbis and a gltf reader etc etc to parse the asset files and encode them, but decoding the assets needs no dependencies. And well, the whole point of the separate executable is so these libraries don't get linked into the actual game.

I am in a bit of a pickle though as I want to define every "process" of the asset pipeline (as in, hlsl to spirv, png to texture atlas, etc) in just one place. And the decoder needs to know at the very least what the packed data is and the metadata (shader reflection, texture width and height, animation speed, etc), which is defined in the process (in the module that links the dependencies)..

So far my ideas are:

  • Duplicate code, but this is bad for obvious reasons
  • Split off the process "definition" (types) and "implementation" into different modules, but this makes it all very annoying to code.
  • Build-time option that controls whether to link the libraries or not, if false, turns all code into stub functions, keeping only type definitions. But this still would fail at any kind of @import("glslang")-style line at the top of the file, as now the dependency isn't linked. Meaning I would have to @import the dependencies inside the implementation's function bodies? Not even sure if you can do that.

Would love to hear more ideas on how to deal with this!

trail mountain
#

Instead of duplicate code just have the "duplicated" code in a single zig file that you import where needed in both modules?

hushed root
trail mountain
#

I was unsure if it's what they meant or not. It seems to me that it would be a very natural thing for the shared structures to already be in their own file anyways. We might need more context here

#

to be fair I haven't gotten around to this for my engine yet so I might be missing something that makes it hard but my plan was basically to just define the asset data (runtime format) as part of the engine/game project and then import the relevant asset data zig files in the build tool. I might also make the build tool take a full dependency on the engine if it's complex enough but idk yet.

#

The build tool code would only really be responsible for serializing an asset to disk that the engine will be able to read, it's not really part of the asset's "implementation" at all

mental rivet
#

Well, there's essentially the "metadata" of a pipeline process:

pub const encoders = struct {
  pub const simple_texture = struct {
      pub const Metadata = struct {
          width: u32,
          height: u32,
      };
      pub const valid_inputs: []const []const u8 = &.{".png"};
  
      pub const input_type = Encoder.InputType.single;

      pub const encode_fn = encode_simple_texture;
  };

  // etc.....
}; 

Which is needed by both build tool and game.
And then, the actual implementation

// test encoder
fn encode_simple_texture(ctx: *const Encoder.Ctx, inputs: []const Encoder.Input) anyerror!void {
    // etc..... uses zstbi and such
}

Which is just needed by the build tool

I think for ease of reading having these in the same place would be very nice. Keeping them separate is a little annoying. I was wondering if there was a way of not doing that with some arcane Zig magic, but I guess I would be happy keeping it that way.

#

I want the "encoders" to be something you can plug into the build tool at comptime, so that it's very extensible and modular

dawn orbit
#

zig has lazy evaluation, so you can give the module build option whether to include deps or not and then guard imports like so: const dep = if (build_options.is_asset_packer) @import("dep") else unreachable;

#

you won't hit the unreachable if your non asset packer code won't ever hit the code paths that use the dep

#

having metadata on its own module would be the cleanest of course but above should work as well anyhow