Warning: another rant approaching, but Makefiles *really* piss me off... this isn't an attack on anyone, I just hate the things.
notaz said:
Makefiles are fine, people just tend to make them needlessly complex. Here is one I use for my latest top-secret project:
Simple Makefiles are fine for simple things. There's no static compilation in your example, or inclusion of libraries (apart from z which is pretty impact-less and has no dependencies), or path management, or proper code dependencies. Yes, it'll work if your include paths are already all correct, but otherwise it'll do nothing - now maintain two versions of the program compiled with two sets of options, two different include paths, etc. And most of the time I end up cross-compiling or using different compilers, libraries and includes - even *I'm* not always completely sure where my compiler / includes / library objects are for a particular project sometimes so that means either hard-coding them or trying to auto-detect them. If you have to specify CC etc. in environment variables, then you could have done that in a bash script. An equivalent bash script to your Makefile would be shorter and easier to understand what happens in what order. Most of what's contained in any sizeable Makefile is valid bash-isms anyway. Hell "make clean" is hardly ever anything but "rm *.o".
That's not including things like: "make: *** No rule to make target `main.o', needed by `top_secret_project'. Stop." which is what you'll get from running that example as it is. So you'd have to create another line for every file, or just put in some generic line that compiles any .c file to a .o - Kinda spoils of the point of having a Makefile when it just compiles every C file into an object and then links the object - that's a *three* line bash script. It's fine if you want to hand-compile main.c into main.o each time, but a pain in the backside otherwise.
Then you have that "8 spaces isn't a tab" crap, which is just annoying when you copy and paste such examples, and has no practical reason other than to piss off people who don't care about the difference between a tab and a number of spaces.
Any decent-size project uses autoconf, not Makefiles directly, which creates some of the most unreadable Makefiles known to man. Recent example - Cygwin discontinued use of their "-mno_cygwin" flag. It doesn't affect anything, you just have to use MinGW directly if you want a non-Cygwin executable, or just remove the -mno_cygwin flag from a project that has it and live with compiling a program that will have Cygwin dependency. For a basic Cygwin development environment that's never going to leave my computer, I don't really care about a library on my personal computer having a Cygwin dependency because it'll get replaced with non-cygwin, non-x86 libraries once I target the real device I'm programming for. The configure script won't know how to accept such a statement unless the person who wrote it was *really* forward-thinking, so it took me 30 minutes once to purge a library's Makefiles of no_cygwin statements because they were scattered all over the place. Simple job, made more difficult by the obscurity of Makefiles. I couldn't even do a simple sed on the files because of the way the Makefile was organised.
The only "advantage" of Makefiles is that they follow your prescribed dependency chain all the way through the project. That makes some things easier, but very few projects actually require such an optimal and minimal compilation scheme nowadays. Nobody really worries about accidentally including a particular object file twice, because the linker is smart enough to strip it all out for you - has been since they were first invented - it just adds a tiny little piece of time / I/O to the compilation. But miss out a single definition when compiling each c file manually via a Makefile and the compiler just freaks. It's basically all hand-managed anyway because you have to specify those prerequisites. Almost everything can just be lumped into the compiler/linker at once, and it'll sort out the duplicates / missing stuff for you. And I hate having a Makefile in every subdirectory because it's then a nightmare to track the things - most tools that do that will automate it for you anyway, but it's totally unnecessary.
Makefiles aren't easily human-readable. They also don't need to be 99% of the time. Thus it doesn't matter *what* they are, and they could easily be anything other than a Makefile that would also be easily read by a programmer who's *NOT* familiar with yet-another-syntax.
I find it telling that Eclipse's default is to let it handle the make process rather than use your Makefile. Eclipse is a huge project used for absolutely everything and yet the default is to just ignore all Makefiles and build its own. I also find it telling that Eclipse does this by basically throwing every C file that you haven't specifically excluded into an object using a simple, generic "c -> o" statement, then mass-including a list of them into a main Makefile that does nothing more than link them all together. This is what I did and still do, because it's easier to let the linker sort everything out than it is to fine-tune dependencies. And the only real advantage of a Makefile *was* its dependency resolution, so it's basically just a bash script now, but which requires another program to operate and a horrible syntax to boot. I suppose conditional compilation might be a case for some places but then I've seen some horrible hacks like having to build a program as the first half-dozen lines of the Makefile which then generates the dependencies for the remainder of it. I've seen a few large, famous projects using that kind of setup.
Also, Makefiles age and aren't particularly portable. What's valid in a Makefile today will be junk in the future, not because it's invalid syntax, but because the Makefile just isn't designed to be easily modified at all and so the code gets out of date quickly. If you don't make everything configurable, things will fall over when libraries change etc. If everything is configurable, there's nothing you couldn't have done with a bash script in that case. And if you want to hand a Makefile to another person, you better hope it's autoconf / CMake because editing a Makefile to change paths is an absolute nightmare and one better off done with an automated detection tool. Distributing Makefiles is a pain because they are inherently locked to a single config or have to be regenerated for *every* change in config. I might as well just distribute a batch file with "CC=" "INCLUDEDIR=" etc. in it - at least people stand half a chance of being able to edit it to their filesystem preferences.
The main generated Eclipse Makefile for a huge project of mine is maybe 20-30 lines of code that is mostly comments, environment variables and echo's, and the subdirectories just have a "sources.mk" that lists each c file with a generic compilation at the bottom for all of them. It's the cleanest Makefile I've ever used on a big project, I've never had to touch it, and it always works perfectly but yet to update even such a simple thing manually would be a nightmare - every time you add a c file, you add another line(s) or (more likely) forget and then have to edit the Makefile halfway through the build process. To be honest, I don't really care what it contains because I've never had to edit it. Eclipse could quite easily have been doing the dependency resolution and compilation using a DOS batch file for all I care, it just works. I would never distribute them, either, because although they'd work it would *still* be locked to my config. Thus Makefiles aren't giving Eclipse any advantage either.
Makefile's are badly realised bash scripts that people have got into the habit of using because they like to be able to just type "make" as a standard command. If you're using autoconf, then what/how autoconf does the job with is entirely out of the programmer's hands anyway - so you're not using Makefiles, you're using autoconf which could be churning out perl scripts to actually compile the code as far as you or your users are concerned (and, yes, I've seen things do just that). If you're hand-editing then any Makefile over about 10-20 lines soon becomes a pain in the arse, and you'd be better off with a virtually identical file, with a more usual syntax, and just compile the damn things via a bash script.
I used the GP2X Makefile that was on the wiki when I first started on here over a year ago. I'd programmed in C in the past but I'd always done manual compilation / linking because it was only for small projects and I was brought up that way - Makefiles weren't used when I was being taught C, or FORTRAN, or Java, or any of the other stuff I was taught. We were taught to manually compile things in order to understand what the compiler/linker were actually doing and it was never a big chore to just create a script to automate that process a little. Even with the GUI development environments available in uni, everyone was taught to hand-compile things. It was too easy for something to work in a GUI and then not work when you gave it to someone else because you'd missed something the GUI was using.
When I first "restarted" C programming, I had a bash script for compiling for PC and one for compiling for GP2X. The project I was basing on had a Makefile. Several of them. If and when they worked, I needed to edit them. When it came to stripping out PC dependencies and adding in SDL, it was easier to just write my own scripts and ignore the Makefiles entirely. Eventually I did write a Makefile, a simple one, that did the job. I did one *massive* overhaul on the Makefile to get it working how I liked and combine both GP2X and PC versions into one file. I tested it, it worked, but it still gave me more problems than the two bash scripts would have if I'd combined them. I started using Eclipse, more out of interest than anything else, and I haven't touched that Makefile since. I doubt it does anything at all any more. After that, at least two people picked up my code and compiled it for different architectures - each time they just wiped out the Makefile and started again from what I can see - I know that's what I did when I tried to compile some ancient software a while back - it was just quicker to glance at the Makefile for anything weird, wipe it out and then keep compiling/linking until there were no errors. I started taking the Makefiles off my source repositories because they were out of date and keeping them up-to-date was just an administrative chore that I didn't want.
But just point Eclipse at the folder and hey presto, it "just" compiles and "just" works. And I don't have to worry about maintaining a Visual C++ Makefile, or a MinGW one, or a Cygwin one, or a GP2X one, or some combined monstrosity. Yeah, you can't just download and type "make" but the chances of that actually just working on someone else's machine were slim without autoconf anyway.
Makefiles are just an historical artefact of Unix programming - at one point they had a purpose, now they are just a single, badly chosen tool with which the user never interacts directly beyond typing "configure; make; make install" and thus they could be *anything* - the tool that generates them is more important and has more control than the files themselves. They aren't particularly suited to the job, they aren't easy to read or easy to understand, or easy to write. They aren't portable (but the programs that generate them are), they break easily, and they *must* be kept up-to-date. Hence an automated tool is the only way to manage them, and if your automated tool did things using "makefiles" written in BASIC, or Perl, or DOS batch files, or C programs themselves, you wouldn't even notice or care so long as it compiled. Hell, even the Linux kernel people have been trying to replace the whole complicated build process with a Perl program for several years - the only objection to that seems to be that the replacement is written in Perl which adds a different dependency to normal. If the "Makefile" was some simple one-file program written in C that the scripts compiled first and then let it get on with the job of configuring everything else, nobody would even have cared or objected.
"make" itself is historic, and unnecessarily complicated. Entire projects exist to make using it a little simpler, and most of those do so by adding tons of complexity. It's not worth the effort on a modern machine. Makefiles cost me orders-of-magnitude more time than they save me, no matter what I seem to be doing.