The C for this function (without includes and export directives):
char *
func_equals (const char *func_name, unsigned int argc, char **argv)
{
char *result = NULL;
if (strcmp(argv[0], argv[1]) == 0) {
result = gmk_alloc(strlen(argv[0]) + 1); /* not handling failure for simplicity */
strcpy(result, argv[0]);
}
return result;
}
This can be done with a macro but it's ugly and verbose. Macros also slow makefile parsing a lot and for a large build like e.g. an operating system this makes a big difference - it's a penalty you pay every time you run "make" even if you only changed 1 file.
There are plenty of things you cannot do with macros too. $(shell) is a getout card but it drastically slows down large makefiles.
Your module has a setup function which gets called when it's loaded and this adds the function into gmake:
Things that are hard/slow to do with macros like arithmetic - comparing, adding and so on are even better candidates. A hash function is great for generating intermediate target names that aren't too long for the filesystem.
My favorite one that I've done is embedding a python interpreter into make - this is very convenient as it's MUCH faster than running a process from $(shell) and it keeps state between uses which can be useful.
Autotools is designed to solve one very important problem: how do you build the GNU tools in the first place if all you have is some obscure Unix from the 1980s. If you already have gnu make, gnu bash, gnu binutils, gnu coreutils, etc. installed then autotools is pointless.
I have yet to find evidence of cmake solving a problem (or even having design), though I guess `ccmake` would be kind of cool if it weren't full of nonsense?
I see Autotools as sort of cool if you're porting to a new platform - the tests find out what works and if that's not enough then you have to make some effort to get around it. If you're lucky, however, you put your autotooled code on some completely new OS/hardware and it just builds.
Nowadays the proportion of people who are porting the code is probably much smaller but it's still a way of keeping code working on unix with various compiler, architecture variations.
IMO cmake just includes windows more effectively - for autotools you'd probably be forced down the cygwin route. I find it a bit easier to work with but it's still a nightmare from hell sometimes.
Make's BIG problem (IMO of course) is that the commands are executed in the system shell.
If make supplied it's own shell language, a simplified one, then everything would be fantastic.
For one thing, cross platform builds would be much easier to get working as there would be no issue about "is the default shell bash or ash or sh or dash or ksh or whatever" and on Windows there would be no need to use cygwin.
The other thing is there would not need to be such a huge clash between the way expansion works in shells versus make which is very confusing when you combine the two.
I have written some large build systems entirely in Make. More complex things tend to rely on templates, but you can build arbitrary things, with two main limitations:
The error messages are awful, particularly if using templates. "Unexpected separator. Stop" is a classic, with no indication where in your 2k lines of Make it might be.
You can't have file or folder names with spaces in (usually including any parent folder to where your code is checked out). A "list" in Make is a series of strings separated by spaces. There are various workarounds that people suggest, but none of them work consistently across platforms. You just have to avoid spaces. At least this is less bad since Windows stopped using "Documents and Settings" for "home" folders.
Even then the problem with doing complicated stuff in Make is that it's very hard to reproduce the environment that triggered the bug in the first place.
I came to the conclusion that you need to treat all of the build system as a linear process, which in Make would mean for example not using "=" at all except to define functions, only use ":=". With this kind of discipline I never really needed a debugger, but really Make is not the right language to write complex logic.
Personally I am a fan of (and contributor to) Meson. It's not perfect but it strikes a good balance between what is in the basic package and what you can do in your build script, and by keeping the build phases separate it really helps with keeping things understandable. The lack of functions can be annoying (and in general I wish it could use Starlark as the language), but it doesn't hurt if you follow the same principle and treat the build script as a data flow process, with each phase producing data structures for the next one. So I think it's generally a good principle to follow.
I’ve written a fair amount of Makefiles and bounced off of cmake. Recently I’ve started using zig to build some C++ projects and will not be switching back.
Having your build tool just be a library in a good general purpose language is the right move. Why use unergonomic hacks like the OP when you can use a sane language? My build.zig files have LSP completions and similar syntax (or the same) as what I’m building.
I put make solidly in the pile of tools that are only still around because of network effects. We’d have switched to something better if it weren’t defacto installed by every distro and had to make justifications to sys admins to install alternatives.
OP FYI: US copyright law doesn't recognize or require a range of years, only the date of first publication. Many organizations have decided to omit (the) year(s) altogether. https://blog.ntpsec.org/2020/02/15/copyright-year.html
I wonder how thread-safe the memoisation and/or ALists are? Make's version of parralel processing is a very fun little thing, but has a few quirks around recursion limits that can bite you when going off the beaten path.
GNU make now has a load directive which lets you load up functions written in C
Your makefile can contain instructions to build my_module.o and they will be automatically triggered.For example you can create an equality test that works in an expression (ifeq can't be used as part of an expression obviously). For example
The C for this function (without includes and export directives): This can be done with a macro but it's ugly and verbose. Macros also slow makefile parsing a lot and for a large build like e.g. an operating system this makes a big difference - it's a penalty you pay every time you run "make" even if you only changed 1 file.There are plenty of things you cannot do with macros too. $(shell) is a getout card but it drastically slows down large makefiles.
Your module has a setup function which gets called when it's loaded and this adds the function into gmake:
Things that are hard/slow to do with macros like arithmetic - comparing, adding and so on are even better candidates. A hash function is great for generating intermediate target names that aren't too long for the filesystem.My favorite one that I've done is embedding a python interpreter into make - this is very convenient as it's MUCH faster than running a process from $(shell) and it keeps state between uses which can be useful.
GNU Make also embeds GNU Guile, a criminally underused feature:
https://www.gnu.org/software/make/manual/html_node/Guile-Int...
In practice, Guile is usually not compiled in. Whereas I've never seen a version of make without `load` and its supporting infrastructure.
TIL thank you!
I sometimes wonder if we would even have autotools or cmake if people just knew about this one simple trick
Autotools is designed to solve one very important problem: how do you build the GNU tools in the first place if all you have is some obscure Unix from the 1980s. If you already have gnu make, gnu bash, gnu binutils, gnu coreutils, etc. installed then autotools is pointless.
I have yet to find evidence of cmake solving a problem (or even having design), though I guess `ccmake` would be kind of cool if it weren't full of nonsense?
I see Autotools as sort of cool if you're porting to a new platform - the tests find out what works and if that's not enough then you have to make some effort to get around it. If you're lucky, however, you put your autotooled code on some completely new OS/hardware and it just builds.
Nowadays the proportion of people who are porting the code is probably much smaller but it's still a way of keeping code working on unix with various compiler, architecture variations.
IMO cmake just includes windows more effectively - for autotools you'd probably be forced down the cygwin route. I find it a bit easier to work with but it's still a nightmare from hell sometimes.
Make's BIG problem (IMO of course) is that the commands are executed in the system shell.
If make supplied it's own shell language, a simplified one, then everything would be fantastic.
For one thing, cross platform builds would be much easier to get working as there would be no issue about "is the default shell bash or ash or sh or dash or ksh or whatever" and on Windows there would be no need to use cygwin.
The other thing is there would not need to be such a huge clash between the way expansion works in shells versus make which is very confusing when you combine the two.
Not for Windows
That's great. Thanks for pointing it out.
I have written some large build systems entirely in Make. More complex things tend to rely on templates, but you can build arbitrary things, with two main limitations:
The error messages are awful, particularly if using templates. "Unexpected separator. Stop" is a classic, with no indication where in your 2k lines of Make it might be.
You can't have file or folder names with spaces in (usually including any parent folder to where your code is checked out). A "list" in Make is a series of strings separated by spaces. There are various workarounds that people suggest, but none of them work consistently across platforms. You just have to avoid spaces. At least this is less bad since Windows stopped using "Documents and Settings" for "home" folders.
GNU Make now has a debugger (`apt install remake`) that eases your first pain point a lot
Even then the problem with doing complicated stuff in Make is that it's very hard to reproduce the environment that triggered the bug in the first place.
I came to the conclusion that you need to treat all of the build system as a linear process, which in Make would mean for example not using "=" at all except to define functions, only use ":=". With this kind of discipline I never really needed a debugger, but really Make is not the right language to write complex logic.
Personally I am a fan of (and contributor to) Meson. It's not perfect but it strikes a good balance between what is in the basic package and what you can do in your build script, and by keeping the build phases separate it really helps with keeping things understandable. The lack of functions can be annoying (and in general I wish it could use Starlark as the language), but it doesn't hurt if you follow the same principle and treat the build script as a data flow process, with each phase producing data structures for the next one. So I think it's generally a good principle to follow.
I’ve written a fair amount of Makefiles and bounced off of cmake. Recently I’ve started using zig to build some C++ projects and will not be switching back.
Having your build tool just be a library in a good general purpose language is the right move. Why use unergonomic hacks like the OP when you can use a sane language? My build.zig files have LSP completions and similar syntax (or the same) as what I’m building.
I put make solidly in the pile of tools that are only still around because of network effects. We’d have switched to something better if it weren’t defacto installed by every distro and had to make justifications to sys admins to install alternatives.
I guess this is here because it's been 20 years and I blogged about it on Monday: https://blog.jgc.org/2025/02/twenty-years-of-gnu-make-standa...
Make is just the epitome of software development:
Started as a simple idea
Required more sophistication
Became something no one person really understands
Did you try reading the manual?
Certainly not! That would be a faux pas.
OP FYI: US copyright law doesn't recognize or require a range of years, only the date of first publication. Many organizations have decided to omit (the) year(s) altogether. https://blog.ntpsec.org/2020/02/15/copyright-year.html
Check the first comment on the linked page for counterpoint.
Also note that copyright laws exist outside the US and may differ.
[flagged]
Good to know. I tend to use them as markers of "I started working on this in year XXXX and I last worked on it in year YYYY".
I wonder how thread-safe the memoisation and/or ALists are? Make's version of parralel processing is a very fun little thing, but has a few quirks around recursion limits that can bite you when going off the beaten path.
[0] https://www.gnu.org/software/make/manual/html_node/Options_0...
Okay who will be doing this year's Advent of Code in GNU Make?
What we really need is a C to GNU Make transpiler written in C just so it can translate itself.
But what system would you to build that ?
GNU Make and GCC, of course.