5 ms·
More nifty portable Make facts: - For portable recursive make(1) calls, use $(MAKE). This has the added advantage of BSD systems which can electively install G
by git-pull 9y ago
More nifty portable Make facts:
- For portable recursive make(1) calls, use $(MAKE). This has the added advantage of BSD systems which can electively install GNU Make as gmake being able to pass in the path to gmake to run GNU Makefiles [1]
- BSD's don't include GNU Make in base system. BSD's port and build system uses Make extensively, and has a different dialect [2]
- In addition to that, you will likely choose to invoke system commands in your Makefile. These also have the same GNU-specific features that won't work on BSD's. So keep your commands like find, ls, etc. POSIX-compliant [3]
- Part of the reasons tools like CMake exist is to abstract not only library/header paths and compiler extensions, but also the fact POSIX shell scripting and Makefile's are quite limited.
- Not only is there a necessity to use POSIX commands and POSIX compatible Make language, but the shell scripting must also not use Bash-isms and such, since there's no guarantee the system will have Bash.
- POSIX Makefiles have no conditionals as of 2017. Here's a ticket from the issue tracker suggesting it in 2013: http://austingroupbugs.net/view.php?id=805 http://austingroupbugs.net/view.php?id=805.
- You can do nifty tricks with portable Makefile's to get around limitations. For instance, major dialects can still use commands to grab piped information and put it into a variable. For instance, you may not have double globs across all systems, but you can use POSIX find(1) to store them in a variable:
FILES= find . -type f -not -path '*/\.*' | grep -i '.*[.]go$$' 2> /dev/null
Then access the variable:
if command -v entr > /dev/null; then ${WATCH_FILES} | entr -c $(MAKE) test; else $(MAKE) test entr_warn; fi
I cover this in detail in my book The Tao of tmux, available for free to read online. [4]
- MacOS comes with Bash, and if I remember correctly, GNU Make comes with the developer CLI tools as make.
- For file watching across platforms (including with respect for kqueue), I use entr(1) [5]. This can plop right into a Makefile. I use it to automatically rerun testsuites and rebuild docs/projeocts. For instance https://github.com/cihai/cihai/blob/cebc197/Makefile#L16 https://github.com/cihai/cihai/blob/cebc197/Makefile#L16 (feel free to copy/paste, it's permissively licensed).
[1] https://www.gnu.org/software/make/manual/html_node/MAKE-Variable.html#MAKE-Variable https://www.gnu.org/software/make/manual/html_node/MAKE-Vari...
[2] https://www.freebsd.org/cgi/man.cgi?query=make&apropos=0&sektion=1&manpath=FreeBSD+11.0-RELEASE+and+Ports&arch=default&format=html#end https://www.freebsd.org/cgi/man.cgi?query=make&apropos=0&sek...
[3] http://pubs.opengroup.org/onlinepubs/9699919799/utilities/find.html http://pubs.opengroup.org/onlinepubs/9699919799/utilities/fi...
[4] https://leanpub.com/the-tao-of-tmux/read#tips-and-tricks https://leanpub.com/the-tao-of-tmux/read#tips-and-tricks
[5] http://entrproject.org http://entrproject.org
- tannhaeuser 9y agoBut there's no way in POSIX make itself to assign a value to FILES dynamically - you have to assign the FILES environment var or supply via command line args. POSIX make will expand ${FILES} by the replacement value in commands (and prerequisites but not targets). It then merely happens to be interpreted as part of the command it's placed into.
- git-pull 9y agoUpdated, thank you. Assigning the results of a command to a variable is not POSIX Make. However, both FreeBSD's Make and GNU Make support it. There's the proposal to add it to POSIX Make: http://austingroupbugs.net/view.php?id=337 http://austingroupbugs.net/view.php?id=337 Another thing I wanted to add to the main post: since POSIX Make doesn't have conditionals, GNU Make and bmake both have different ways of doing them. If you look through FreeBSD's Make files, you'll see that conditionals exist, but they begin with dots: https://github.com/freebsd/freebsd/blob/c09174d99/share/mk/bsd.dep.mk#L53 https://github.com/freebsd/freebsd/blob/c09174d99/share/mk/b... There are going to be some compromises when writing portable scripts where it won't be POSIX, but could still be considered portable.
- wahern 9y agoPOSIX Make supports conditional constructs by way of recursive macro expansion. I've found it to be not much more clunky than GNU Make's conditional constructs. (Recently I've come to appreciate the simplicity and consistency of the BSD extensions, even though I don't use them. GNU Make syntax quickly becomes impenetrable.) And as you say, shell invocation from macros isn't yet supported by POSIX but _can_ be done portably. GNU Make supports $(shell COMMAND), everybody else supports $(COMMAND:sh), nobody supports both, and where not supported they expand to the empty string.[1] Here's a simplified example from my proof-of-concept library from https://github.com/wahern/autoguess/blob/config-guess/config.mk.guess https://github.com/wahern/autoguess/blob/config-guess/config... BOOL,true = true BOOL,1 = true BOOL,false = false BOOL,0 = false BOOL, = false OS.exec = uname -s | tr '[A-Z]' '[a-z]' OS = $(shell $(OS.exec))$(OS.exec:sh) OS.darwin.test,darwin = true OS.is.darwin = $(BOOL,$(OS.darwin.test,$(OS))) # Usage: $(SOFLAGS.shared) - for creating regular shared libraries SOFLAGS.shared.if.darwin.true = -dynamiclib SOFLAGS.shared.if.darwin.false = -shared SOFLAGS.shared = $(SOFLAGS.shared.if.darwin.$(OS.is.darwin)) SOFLAGS = $(SOFLAGS.shared) show: @echo "SOFLAGS=$(SOFLAGS)" I try to keep my stuff portable across AIX, FreeBSD, Linux/glibc, Linux/musl, macOS, NetBSD, OpenBSD, and Solaris. I used to just require GNU Make, with makefiles that looked like: .POSIX: all: +gmake -f GNUmakefile all .DEFAULT: +gmake -f GNUmakefile $< but now I'm moving my projects over to the more portable, dependency-less method. It makes things so much easier to be able to run `make test` without having to worry about first installing unnecessary dependencies, or often any dependencies whatsoever. When you're juggling a dozen VMs, trying to track the latest couple of releases of a platform, simplicity is key. Plus, more easily programmable environments encourage overly complex solutions which become maintenance burdens over months and years. With GNU Make I was always unable to resist the urge to turn every repetitious rule into a template, for example. Sticking to purely portable constructs means I'm forced to be smarter about how to approach something, including recognizing when something is best left un-automated. [1] It's nice that POSIX will be formalizing the "!=" construct, but unfortunately it's not supported by Solaris make, and macOS will be forever stuck on GNU Make 3.81 which also lacks support.