Using imports via special root import

My PinePhone OS is growing fast, and I’m wondering how to better organise the project structure. So far almost everything has been living in the root folder (excl. fonts). The project doesnt fit a package based approach. So I use a filename prefix in place of folders, and that has allowed me to avoided some complexity from some cross-file dependencies I have, but mainly because I really dislike relative folder imports.

However, with ~100 source files it is getting a bit unwieldy. So today I finally started to move things into sub-folders, and straight away I have this ugly mess:

const app = @import("../os-app.zig");
const ctx = @import("../../ui-drawing.zig");
const colors = @import("../../ui-colors.zig");
const perf = @import("../../api-performance.zig");
const button = @import("../../ui-button.zig");
const fonts = @import("../../ui-fonts.zig");

I just had an idea on how to clean this up, but I would like some feedback before I change everything please. I recently learned that we can access the special ‘“root”’ import from anywhere, so I was thinking to create an all.zig in the root folder, which would have public imports for all 100 source files. Making the above read like this:

const all = @import("root").all;
const app = all.app;
const ctx = all.ctx;
const colors = all.colors;
const perf = all.perf;
const button = all.button;
const fonts = all.fonts;

Maybe I spent too many years with Java, but this feels a little cleaner? It allows for simple refactoring, just a single import to change in all.zig, and could also be further grouped along architectural lines if that also gets too cumbersome.

const root = @import("root");
const ui = root.ui;
const api = root.api;
const hal = root.hal;

It seems I lose code completion in my editor (Kate) or maybe that is related to LSP/ZLS, although I thought I’d disabled that.

Are there any other considerations that I am missing with this approach please? e.g. Any cost with importing every file into every file?

Here’s how I do it in one of my projects:

It’s quite similar to your idea except that I don’t use a single root module, but one module per ‘system’ and those modules are set up in build.zig.

(code completion in VSCode via the ‘official’ Zig Language extension also seems to work fine)

3 Likes

That would be my null hypothesis: the looks need some time to getting used to, but its the most straightforward and direct way to do the thing. I’d change that only if I have a strong positive reason to (eg, I am exporting a package where I have to have dotted paths).

1 Like

Thanks for the feedback/input. So I decided to give it a try, and an unexpected benefit is that the import naming is now much more consistent, which suits my OCD :slight_smile:

The refactoring is also already much easier. I can freely move source files around with just a single import to change (plus a fossil mv).

At a glance, it is no longer obvious that these are imports/dependencies, even though I keep them at the top of the file. So I have settled on this convention for now:

const imp = @import("root").imports;
const app = imp.app;
const ctx = imp.ctx;
const colors = imp.colors;
const button = imp.button;

What’s also interesting is that this should make testing easier, since I can easily mock any import/dependency in a single place.

@floooh Yes I lost code completion, but better that than go back to VSCode :wink:

2 Likes