One line you should add to every makefile
blog.jgc.org
blog.jgc.org
Not puppies, but brains of the poor programmer who is confronted with a problem of building your bloatware on a non-supported platform (typically Windows).
:)
And a dependency-graph-of-thing-we-work-with to Makefile generator, like the output of gcc -M does.
In fact its a whole build system using gnu make, written from scratch.
Then I read and thoroughly grokked the O'Reilly book on make (which some douche stole from my desk; twenty plus years on, I am still bitter about that one... ...don't miss the Tannenbaum OS book or the Stevens networking books, but the make book, it burns, it burns....)
After that all I wrote all of my make files from scratch. Doing so made sure I understood my project, its dependencies, etc., and made sure I wasn't going to accidentally corrupt a build with some edge case from another one.
make is super straightforward once you get how it works. Sort of like git rebase, I suppose (still working on that one... :->).
Sorry, like I said, I don't think I really understand make.
I mean, I can understand that it's doing some checks to determine what commands to emit, but doesn't it all end up falling out as pretty much a straight "Yes? Do this. No? Do that. Do this and then this and then this. Do all these at the same time."
That's my mental model at the moment anyway.
x: y
z
x depends on y (meaning, if x is missing, or is a file that is older than y where y is also a file, it needs to be recreated).z is how to recreate x.
Makefiles are programs that construct a dependency graph.
The rest of makefile syntax is mostly about ways of generating variants of the above without being overly verbose, or reducing gruntwork when e.g. adding new header includes to existing source files. But this is the heart of it.
Root the dependency graph somewhere, and as long as the dependencies are complete and correct, the minimal set of commands, and potential parallelism, can be calculated.
If we didn't mind waiting for hours each time we build, then of course, a shell script that does `rm -rf target` then assembles everything from scratch would be significantly more straightforward and readable.
No, I was referring to implementing a replacement/frontend to Make.
The main thing I learn was that build systems are really, really hard. Simply coming up with an intuitive way of letting the user write down what problem they're trying to solve is painfully hard, let alone actually solving the problem. I have yet to see a build system that wasn't horrible.
GNU make is a hideous pile of recursive terribleness, but it is at least a ubiquitous hideous pile of recursive terribleness. That doesn't make it any less pleasant to use but it does at least make it more likely that people will be able to use the result. But for any non-trivial project make requires you to implement a build system in make, and as there are no debugging tools and the language is cryptic and inconsistent beyond words, the results are always buggy and hard to understand.
I dunno. I think what I'd really like is a reinvented GNU make that doesn't have all the many, many bad design decisions from the past in it. ninja, maybe? I've yet to actually try to use that...
In my EE/Physics department, lab reports from previous years were known as "newtons" (as in: "do you have a newton for the preservation-of-momentum experiment?"), the explanation being that Isaac Newton wrote a lab report from scratch, and everyone else has used an older lab report as reference to make sure they got the right result - the transitive closure of which having Newton as a boundary.
I guess the equivalent Makefile term would be a Feldman[0], as in: "Do you have a feldman that can combine ghc and dmd output? I need that for a project"
That said, I have written 2-5 Makefiles from scratch (move companies, need to create a new product, don't have access to old Makefiles). The rest are definitely copy/paste/modify.
e.g. if you create C program your-program.c with a main(), all you have to do is invoke
make your-program
and it is done.Not exactly production quality (no clean target) but can't beat it for simplicity :-)
But the linker script for ARM Cortex and the GNU linker is usually copied verbatim. You can find it everywhere. Open source projects, commercial development. Everywhere. Because STM ships it with their standard peripheral library.
Nobody cares that this linker script is (c) Atollic and only licensed for use with their IDE:
http://hackaday.com/2012/06/19/stm32-demo-code-carries-extra...
print-%: ; @echo $*=$($*)
include Makefile
Now you can use it the following way from any source directory: $ make -f ~/Makefile.debug print-SOURCE_FILES $ cat Makefile
FOO := bar
target:
Next: $ cat Makefile.debug
ifeq ($(MAKEFILE_INCLUDED),)
-include Makefile
MAKEFILE_INCLUDED := y
endif
REPL_COMMAND := $(shell read line; printf "%s\n" $$line)
$(eval $(REPL_COMMAND))
-include Makefile.debug
Now, here we go: $ make -f Makefile.debug
$(warning $(FOO)) <--- typed by me
Makefile.debug:8: bar
ABC := xyz <---
$(warning $(ABC)) <---
Makefile.debug:8: xyz
:)I might actually use this some time, god help me. I won't thank you, but I will remember you and curse a little every time I do.
tup[0] is a another implementation of the same idea, that does work on Windows (unlike memoize) and has a few other goodies.
I would also recommend having a look at djb's "redo", implemented by apenwarr - it is much easier than to do right than a makefile, but unfortunately you still have to get the dependencies right yourself (which tup and memoize do for you automatically).
# make -V SOURCE_FILES
$ foo() { echo 'foo: ; @ : ${'"$1"'}'; echo include Makefile; }
$ foo SRCS
foo: ; @ : ${SRCS}
include Makefile
$ foo OBJS | make -n -f- foo
: foo.o bar.o baz.oBack to the subject, I would also be interested to know a way to print out the value of a variable every time it changed in the course of a make system's execution.
I go back and forth on Make. It was where I started so there is some bias there but generally it has always been possible to do what I want with it. The places where it bites me were things that gmake added which added, to me, features which could be cleverly exploited but made things much more complicated and error prone. I flirted with SCons and other build systems, I was amazed at how flexible Google's was (and had to be given the complexity embodied in it) but for small projects (where small is perhaps a couple of hundred source files, and a half dozen libraries) it still is my goto build tool of choice.
print-%: ; @echo $*=$($*)
.PHONY: print-%
This way the rule continues to work even if such a file suddenly exists in your repo. .PHONY: FORCE
FORCE:
print-%: FORCE ; @echo $*=$($*)Their reputation has been tarnished by autotools, but in that case you're using autotools as your build system, not make.
The comment that I was replying to sounds like CMake or something has more magic thus power for a future unknown compiler.
The truth is that no matter what level you program at, you are depending on a long toolchain that you don't understand the details of. You are explicitly aware of not understanding autogenerated stuff at your level. But are ignoring how little you understand of what is underneath the level you are used to programming at.
Get used to it. A few years back I remember an article that started by diving into what actually happens between pressing a key on the keyboard and a letter appearing on the screen. I wish I could find it for you. It was a long article. And repeatedly got into too much detail, then narrowed down the scope and got into more detail. Over and over again.
You don't actually understand how your computer or code works. Instead you create a useful working model and proceed with that. Said working models can include both lower levels than your usual, or you can build up higher levels. Get used to it, take advantage of good tools, and you will accomplish more. The alternative is to get stuck in what you know, refuse to go outside of that boundary, and be less productive. The choice is yours.
I have a degree in electrical engineering, though these days I work on server / infrastructure software. Yeah I have a pretty good idea how stuff works at the c/c++ level and below.
https://plus.google.com/+JeanBaptisteQueru/posts/dfydM2Cnepe
That comes back to the complaint about the auto-build tools. Using the tools, by themselves, is perfectly reasonable. On the other hand, writing Makefiles by hand isn't particularly onerous. What is frustrating, however, is when I'm expected to edit a Makefile that was generated by a tool without having access to the script that generated said Makefile.
That said, if you find autogenerated "stuff", that is not evidence you can't work on the project. It is evidence that you need to understand something before doing so.
Advantages are it is easy to edit, cat, and the locality of the commands: I don't want to pollute my bashrc with commands that are useful only in a given context.
http://www.cl.cam.ac.uk/~srk31/blog/2014/11/19#writing-makefilesNope.
Eric (who wrote that blog you are referrring to is a friend), but I wrote this little trick up years before him: http://www.cmcrossroads.com/article/printing-value-makefile-... and now I'm rehashing my own writing 10 years later.
That being said, this trick could be handy for existing projects.
It's a very simple debugging addition. Hardly necessary, just convenient. The "built-in" echo works just fine as well.
I usually use Makefiles for my Ruby projects.
1 2 3 4 (echo)
and ["1", "2 3", "4"] (p) printf ' "%s"' $(numbers)
to output "1" "2 3" "4"