This library has multiple backends depending on what system you’re on, and these backends are NOT supposed to be user-facing.
My question is, when creating a library for others to use, how do you hide non user-facing code/imports?
In C, you would simply compile your library, and then provide a header file with the function declarations that are for the user. But zig doesn’t use compiled libraries for native Zig code.
I’ve had folks tell me to use addModule in the build.zig file, but this doesn’t seem correct as that’s for creating a compilable object (such as a .a/.so or executable binary).
and as an example, I would only want users to import src/core/window.zig. Does this have to do with how I define my build.zig.zon, defining importable modules?
I don’t think that’s completely right, addModule is for creating modules that can be accessed from other packages, those then can be depended on by further/other modules, they can contain both Zig code and c source files and you can link artifacts to them, you can create packages that allow others to use those modules from their own modules/packages, which are then used as the root module for an executable or compiled library artifact, or they can be used as imports on other modules.
The advice you got is almost correct, the usual way to create Zig libraries is to create a package that provides a module (which is essentially a bundle of source code and related sources, include pathes, etc.) which you then use while writing your own code, all those modules then get compiled into a single compilation unit, going beyond a single compilation unit is for more advanced use cases, where people have some special reason to use more than one compilation unit and it requires creating multiple artifacts (executables, c-style-libraries).
So using modules is the right advice, but for those modules that should stay internal to a package you would use createModule instead of addModule the difference is that the latter also makes it accessible from outside the package, while createModule only creates the module (so it can be used for modules that are supposed to be used solely internally).
So you would expose the library with a module that uses addModule and the internal backends would use createModule and only add an import of that module to your api-module which then uses it access the backend functionality.
The piece that I was missing is that discarding the pointer of addModule still keeps it in the build tree for reference.
So based on this, I could just do _ = b.addModule() on the user-facing api, and when a user decides to use this library, they’ll only see what’s exposed by that module?
What addModule does differently is irrelevant to this behaviour.
The build API allocates all ‘steps’ on the heap as they need to live longer than the build function, as the actual running of the build happens after that.
I don’t think that is relevant to what OP is asking. createModule also allocates the Module on the heap, but it is definitely not in the build tree or can be referenced by client/user packages.
Mentioning heap allocation here will only confuse OP because there has been zero questions about how the steps are allocated in this thread. The questions are always about if the module created is exposed elsewhere, even if you don’t use it to addExecutable / addLibrary in the same build.zig of the library.