make-obj.txt (3212B) [raw]
1 # `make obj`: out-of-tree builds with BSD `make(1)` 2 3 _Published: September 28, 2021_ 4 5 If you've ever built parts of OpenBSD from source, you may know 6 that the sequence of commands recommended by 7 [`release(8)`](https://man.openbsd.org/release.8) is: 8 9 $ make obj 10 $ make 11 # make install 12 13 If, like me, you've forgotten the `make obj` step, you'll find 14 yourself with many derived files in the current directory of 15 whatever program you're building. By running `make obj` first, a 16 directory called _obj_ appears and the derived files (usually \*.o 17 files) are placed there instead. Cleverly, the _obj_ directory is 18 actually a symlink to another filesystem under _/usr/obj_, 19 making it truly an out-of-tree build. 20 21 Up until recently, I understood what the `obj` target did and why 22 it was useful. However, it wasn't until I tried to replicate it 23 with the build for text.alexkarle.com that I discovered how it 24 worked. I figured I'd document it here in case it helps anyone 25 else. 26 27 ## How it Works 28 29 My discovery of the inner workings of this target was a classic 30 lesson in RTFM. After 10-15 minutes of trying to parse the 31 makefiles in _/usr/share/mk_, I finally searched for _obj_ in the 32 [`make(1)` man page](https://man.openbsd.org/make.1), and sure 33 enough the answer was the first hit! I've copied it for 34 convenience below (licensed under the BSD-3 clause): 35 36 > *`.OBJDIR:`* 37 > Path to the directory where targets are built. At 38 > startup, make searches for an alternate directory to 39 > place target files. make tries to `chdir(2)` into 40 > `MAKEOBJDIR` (or obj if `MAKEOBJDIR` is not defined), and 41 > sets `.OBJDIR` accordingly. Should that fail, `.OBJDIR` 42 > is set to `.CURDIR`. 43 44 With this new knowledge, getting an out-of-tree build was almost as 45 simple as running `mkdir obj` before `make`! 46 47 The one catch was that, having chdir'd in, I had to canonicalize 48 the paths to any scripts used in the build recipes. For instance, 49 I have a genpost.sh script in the bin/ directory of this repo. To 50 call it from the obj directory, I needed to use its absolute path 51 via the .CURDIR variable: 52 53 $(.CURDIR)/bin/genpost.sh < $< > $@ 54 55 ## Portability 56 57 While I mostly build my site on OpenBSD, it's important to me that 58 it builds with GNU make too. 59 60 Unfortunately, the `.OBJDIR` chdir'ing appears to be an extension in 61 OpenBSD's make (and possibly NetBSD too). The good news is that, 62 with one more trick, GNU make support is easy to add (albeit 63 without out-of-tree builds). 64 65 The one final hack to support GNU make was to define a portable 66 version of `.CURDIR`. Since `.CURDIR` isn't defined in GNU make (which 67 uses `CURDIR` instead), I had to define the `DIR` variable that's the 68 concatenation of the two: 69 70 DIR = $(.CURDIR)$(CURDIR) 71 72 ## Conclusion 73 74 I hope this sheds some light on why `make obj` is common practice 75 on OpenBSD as well as how to add similar support to your own 76 projects! 77 78 While not as flexible as GNU make's pattern matching inference 79 rules (that allow builds in subdirectories), I find the chdir-ing 80 into obj a cleverly simple way to obtain a similar end result. 81 82 ## See Also 83 84 - [text.alexkarle.com's origin story](/blog/text-only.html) 85 - [writing the blog in mdoc(7)](/blog/my-old-man.html) 86 87 [Back to blog](/blog)