3 ms·
The idea (as I understand it...) is to structure the codebase as a directory tree and have position in the tree determine whether some module can call into anot
by JonChesterfield 2y ago
The idea (as I understand it...) is to structure the codebase as a directory tree and have position in the tree determine whether some module can call into another or not. Something along the lines of a function can call into one defined in source at the same level or below, but not above.
I did that for a while in C. It does sort of work. Layout amounted to:
foobar.h
foobar/foobar.c
foobar/misc.h
foobar/misc.c
foobar/submod.h
foobar/submod/submod.h
foobar/submod/other.c
The scheme there was #include to get at peer or submodule source, where not using "../" in the path avoided trying to access parent modules.
Within a simple header/source pair make the declared functions global and all the others static. For the header/directory pair, compile the source files, link them, internalise the symbols that aren't external in the file with the name matching the directory.
I still broadly like that approach. The llvm-link && internalise applied at submodule scope has the side effect of optimising the modules individually. You can start with a header/source pair and convert it to a header/submodule pair without disturbing the rest of the codebase.
What doesn't work so well, and also isn't addressed in the "firewalling" post, is that inevitably the dependency tree wants to pick up a cycle. Some dependency of some module looks like it would be useful to another module and include "../../foobar/misc.h" suddenly appears in the scheme and promptly fails to link because misc.c's symbols are hidden inside foobar.
The "fix" is either to abandon this scheme, start putting functions as static inline in header files as a "temporary" workaround, or to move the multiply-used dependency up through the tree until it is accessible to everything that wants it. "Misc" is prone to ending up at the top level.
I think that pattern died when I disabled the internalise step to help debug something where I wanted to pull in code from elsewhere and then never re-enabled the internalisation. It still seems like a broadly good idea but it does shine a clear light on the dependency structure of the codebase which obstructs the natural descent into a ball of mud. These days I think I'd want the internalise / strict separation enforced in CI builds and not in dev builds (much like unused variables and similar).
Nice to be reminded of that pattern. Thanks for posting the site.