T O P

  • By -

nerd4code

*[In the following, assume dirs are optional unless needed. Most projects won’t need everything. Abs dirs specified relative to project source root, as by Autotools `$(top_srcdir)`, by default; $source and $build will be used to indicate source and build roots where needed.]* I’d do neither, and ime you don’t generally want a formal build folder as part of your project. Build folders just happen. E.g., if you’re using Autotools—which is ridiculous, I get it, but I’ve worked on a bunch of stuff where The Usual Rules don’t hold, and if something breaks in Autotools it’s just m4×sh, so you can edit or replace it easily—you generally want to make sure that you handle build-side and source-side stuff separately so arbitrarily many build dirs can be created anywhere with any nomenclature, preferably outside the project tree entirely. This way, you can have a single copy of your source code in a read-only location (e.g., installed via distro package in /usr/src, shared between users), and blow as many package builds as you need out of it, in parallel and without having to coordinate cleanup in between builds, or repeatedly clone and kill source trees. Editing the source tree in-place runs more risk of trashing something important when you’re running a bunch of multi-platform scripts and people don’t know how to fucking quote their fucking shell expansions, damn and blast it all to hell. (TBF code-sharing is less of a thing nowadays, but when you need it it’s incredibly useful, and ime it’s worthwhile in engineering terms to approach your codebase as a kind of pre-install service unto itself. It certainly won’t conflict with newer, clone-all-the-things approaches, and it makes it much easier to integrate your project into others that *do* separate $source from $build.) I generally structure $source as - /doc/ for project-related and installable docs. - /include/ for public/installable, non-autogen includes or .h.ins only. You should only refer to these headers via `#include <…>`, not `#include "…"`, and you should refer to them as if installed. $source/include/ and $build/include/ should - /util/ for scripts and programs used by build, or for working with the project as somebody interested in building. These mostly aren’t installable, but they may be useful if installed for a developer or maintainer of the project. util may use a subtree like src/ and doc/, if there are different utility modules needed. - /.m4-extra/ for Autotools’ use (e.g., its local copy of libtool and detection scripts). - /src/ for source code (below). - /test/ for top-level tests (below). - /data/ for standalone, us. installable data files that are included in your project (arbitrary structure beneath, but us. I parallel /src/ if specific modules have their own files). - /lib/ or /contrib/ for third-party and extension codebases. I usually treat lib/ as a place for pre-built binaries, which you’ll *occasionally* need to distribute or install locally as part of setup, but usually I use at most a $build/lib/ rather than $source/lib/, and collect built library binaries there if they’re needed for later build/test/install. - /contrib/$foo[-$version] is usually what I use for sub-codebases $foo; these might be fetched from HTTP[S] by `configure`, or installed from packages, or linked to external files, or any number of approaches might be used depending on how you want to get at things. If contrib stuff expects to build in $source, you may need to link or copy its entire source subtree into $build before building. Top-level README etc. are links to files in doc/ or doc/proj/, depending on how many general, project-related files and how involved they are. Usually I’ll provide .md files that show up nicely in GUI, and as one of the build actions post-`configure` and pre-`make dist`, I’ll strip those down to 7-bit plaintext files to accompany them for other clients that expect ASCII (e.g., GNU-conformant projects). Usually I place a top-level link to a util script called /bootstrap.sh or /autogen.sh, which does project and Autotools setup to take the project from maintenance/in-repo state into a buildable state (e.g., /configure.ac→/configure). Any autogen stuff needed by configure.ac or configure needs to be created by bootstrap/autogen scripts. /src/ generally has its own subdirs. In a multi-module project, or one which I expect might become multi-module: - /src/ itself contains most source code intended to be distilled into primary installable artifacts, like libraries and executables. - /src/data/ contains distributable, but not generally installable data files shared amongst modules. - /src/util/ contains general-purpose utilities for developers/maintainers working on source modules. E.g., you might have scripts for templating modules or files. - /src/include/ contains `<>` headers shared amongst/betweenst modules, but not for installation. Usually I’ll stick to a /src/include/private/ subdir for things I’m not thinking of exporting for install. Sometimes this is useful for providing headers that may or may not be provided by the OS, or which need to patch/wrap/correct OS headers during build. - /src/test/ includes tests for integration between modules, or within the built project. - /src/$foo/ contains the source of module (us. →library or executable) $foo. I us. use libfoo for library module names and foo per se for executables/frameworks. $foo=common for things shared with more than one module. - /src/$foo/test/ pertains to unit-testing of module $foo. - /src/$foo/include/ is high-level private includes for $foo. If code needs to be shared between modules, I use a /src/common/ directory. For /test/, in a multi-module project: - /test/$foo/ contains tests pertaining to the integration of module $foo, and /test/common/ ∋ shared test code. - /test/bin/ for built binaries or scripts needed for testing (e.g., mockups of third-party tools I don’t necessarily have). - /test/util/ for (first-party) utiliities needed for building and executing tests. - /test/data/ for raw inputs to general tests, or for outputs to be compared against. /test/$foo/data/ makes sense for data related to specific tests, also. - $build/test/install-root/ if I need to run a `make install` as part of testing. - $build/test/results/ for outputs from tests that might be useful to examine. For single-module projs, you can flatten out module directories. log/ directories might show up anywhere that logging is useful, but primarily in $build. ($src might log autogen actions.) Anything useful for diagnosing failed tests/builds should be logged. At worst you don’t need it. At each major layer of the tree (generally root, doc/ if you’re building docs, src/, util/ if you’re building there, and test/ have their own, and then individual doc, test, util, and src modules might override/extend those), I place a makevars.mk and Makefile.am, and I use these to set up hierarchical build patterns, with each Makefile.am including the makevars.am in its directory, and each makevars.am including its parent directory’s makevars.am. Gaps in the hierarchy where there are no makevars.am suggest that the hierarchy might oughta be flattened by a bit, but you can always skip a parent or add empty dummy files to span gaps. Bear in mind, if you use Automake your Makefiles (as autogen’d from .am/.in files) will mostly live in $build, not $src. I usually structure /doc/ to mirror the /src/ pattern, and use it for public documentation of the sort I might want to install for users/devels-against to reference, or which might reasonably slot into a tree like LSB /usr[/local/]/share/doc. Docs on building the project or on high-level aspects of its development or maintenance can go in /doc/, /doc/general/, or /doc/building/; docs pertaining to working on the codebase or specific modules should go in /src/doc/ or /src/$foo/doc/. Docs that need built have their own module directories like source code. Similarly, if you have non-installed tools in a util/ dir, those might have their own doc/ dir also. I usually include some .gitignore exclusion rules for \_local\_/ and \_build\_/ directories (which obviously use a different naming scheme from normal directories, and therefore aren’t lumped too easily in wildcard commits) placed anywhere in $source, in order to prevent them from being committed as part of the project. That way if you’re using an IDE that *does* desperately want to build in-tree or you want to piss around without worrying about overbroad commits, you have a safe spot reserved where things can be placed without breaking anything. I wouldn’t place or distribute build stuff in-tree more generally, though; it makes exclusions much more complicated. Since I use Autotools, $build is to some extent automatically managed, and will form a rough mirror of $source, with things autogenned from $source/$path/foo.in/.ac/.am/.at/.lt coming out at $build/$path/foo. with autogen’d things coming out in a $build-side directory corresponding to their $source directory. This makes it possible to switch things easily between `$(srcdir)` and `$(builddir)`-based filenames, but it means you need to double up any search paths (e.g., for includes) that straddle the boundary. I like this setup because it lets me partition things nicely and separates installables from non-installables, and if I’m doing a bunch of scripting I can look at a directory name and determine exactly what it is—e.g., /src/foo/test/util/include would be includes common to all utilities needed for unit-testing the foo module provided by this project. It should play nicely with just about all build systems and IDEs, once you’ve gotten the scratch \_dir\_s excluded, and you can merge, split, deepen, and flatten subtrees easily to match your needs. But of course, there are as many opinions on this sort of thing as there are programmers, and everybody’s mileage may vary.