Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
C++ Clojure C
Fetching latest commit…
Cannot retrieve the latest commit at this time.
|Failed to load latest commit information.|
There are _packages_ named like foo/bar. Each package lives in a Makefile (one Makefile can have multiple packages). Packages can export: - jars / classpath entries - compilation flags - shared objects / linker flags - executables / executable names - headers All exports except headers are expressed as makefile variables: - jars: $(foo/bar/cp) - compilation flags: $(foo/bar/cflags) - sos: $(foo/bar/ldflags) - exes: $(foo/bar/baz) Headers are used by referring to the full path: foo/bar/.../baz.h Additionally each package exports a variable $(foo/bar) that you must depend on to access any of the other variables. This variable must depend on all the artefacts the package has so that dependent packages get recompiled. All things must depend on $(root) and their own package Makefile - that way you'll rebuild if command line flags change. Environment variables should not affect builds. root.mk defines a variable OUT for the build output. All actual output files should go under $(OUT)/package ($(OUT)/foo/bar). Each Makefile can be given as a -f argument to make; files that only make sense when included somewhere else should be named *.mk. A package can (of course) use something else than make to actually build its exports, but it should have the correct dependencies to integrate into the build (and should not build unnecessarily either). --- Practical guidelines - for each Makefile it's convenient to define a shortcut to the directory (package) it lives in so you don't have to keep spelling it out, e.g., foo/bar/Makefile: D := foo/bar; this should be set after all includes - for each package it's convenient to define a variable that holds that package's dependencies and use it as a dependency in all your rules foo/bar/Makefile: .deps := $(quuz) $(root) $(D)/Makefile $(OUT)/$(D)/exe: exe.cpp $(.deps) g++ $(root/cflags) -o $@ $< - to manage the scope of variables the following three things are allowed: - you can define any variable prefixed with your package name - any name beginning with a leading dot (.deps) can be defined by any makefile and used locally - any target-specific variable will not affect any other targets and you can use them freely - note that since all Makefiles will use the $(D) variable, you can't use it in recipes (since they are only expanded during execution) - always use := to assign to variables so that the right-hand-side is expanded when defining --- Notable things: - non-recursive makefiles - hence all paths are relative to root directory - and you run make always from the root directory - dependencies are between packages, not between files (simple graph, easier to get right, always correct, most of the performance benefits of the dependency graph) - inside a package, you should prob. depend on all of your headers rather than use fancy dependency generation (simpler, less work at graph creation time, possibly some extra compilation) - not using a directory stack for the D variable, instead setting it explicitly after includes - outputs go outside the source tree - not using templates or fragments for the makefiles, just plain include --- Missing: - build-time vs. run-time dependencies - hygienic output directory: each package should have its own $(OUT) and you could only use artefacts from packages you've explicitly included, not anything in $(OUT) - no example of maintaining license compliance, though compliance can quite easily be layered on top of this --- Credits: - obviously influenced by other non-recursive make implementations like https://github.com/aostruszka/nonrec-make and http://evbergen.home.xs4all.nl/nonrecursive-make.html - consciously modelled after Google's internal build system, though missing many things (see 'Missing' above)