16 comments

[ 2.8 ms ] story [ 47.2 ms ] thread
This example seems to have a few issues with ordering and what should/shouldn't have their own rules. In general, a good rule of thumb is that any file which is generated should have a coresponding rule to generate it, whether or not that's a generic rule or a specefic rule. Also important, rules should only generate the file they claim to generate (IE. If a rule generates '%.c' and also happens to generate '%.d', it definitely shouldn't act like it only generates '%.c' - An exception can be made for temporary files, but in general if they're not going to stick around you should remove them when the rule is run anyway, or give them a proper rule if they're sticking around). The biggest reasons for these rules is so that your rules are honest about what they create (And this makes 'make clean' much easier to implement), and the second is that at some point you'll need that generated file somewhere else and won't have a rule to generate it on it's own.

The examples break these rules more then a couple of times. Their very last examples won't work, because they generate the %.d file in the same rule that is only supposed to generate the %.css file. Because of this, 'make' has no way of figuring out what rule to run to create the %.d file, so the -include will fail from the .d files not existing in the first run (-include and include-file generation happen before the default rule is started, because it happens at parsing time). Providing a separate %.d rule would fix this issue (And also fix the issue that every time you compile your dependency info will be generated again, even if it hasn't changed.). For example, if you wrote a 'make clean' which only cleaned the %.css files, all of the %.d files would be generated again when you generate the %.css files, even though it's not nessisary. There's also a small error that 'all' isn't the default rule to run, the default is simply the first rule in the file. It's common to see 'all' defined high-up in a Makefile as something like 'all: real-all', where 'real-all' is defined below after other various things take place, and it does the actual heavy lifting of kicking off the compilation.

> Providing a separate %.d rule would fix this issue

Yes. The following is an example of how to handle C header dependencies. It could be adapted to build css from less and would be an improvement on the blog post technique.

    sources := $(wildcard *.c)

    -include $(subst .c,.d,$(sources))

    %.d: %.c
        $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
        sed 's,\($*\)\.o[ :]*,\ 1.o $@ : ,g' < $@.$$$$ > $@; \
        rm -f $@.$$$$
It uses the fact that if you include a file which doesn't exist but for which there is a rule to build it, GNU make will build it for you.
Yep. Though with newer versions of gcc (I don't know how new though) you can avoid the nasty sed stuff. I'm currently using this to generate my .d files for C files:

    $(objtree)/%.d: $(srctree)/%.c
        @$(CC) -MM -MP -MF $@ $(CPPFLAGS) $< -MT $(objtree)/$*.o -MT $@
That generates a dependency file in the form:

    $(objtree)/%.o $(objtree)/%.d: (dependency list)
    (dependency-list-entry-1):
    (dependency-list-entry-2):
    etc...
It's pretty handy. Avoiding all the sed stuff is nice.
> So, when there are so many build systems out there, why make? > * it is supported on almost all systems

Except the elephant in the room, Windows.

reminds me of the git monoculture article from a few days ago. Work best on Mac(tm)!
GNU Make can be used on Windows. Of course, it's easiest to use it on GNU/Linux or one of the other UNIX-like operating systems.
Yes but most Makefiles, including the one in this article, use commands that do not work on Windows.
There is gnu-make for windows and unless your is makefile super elaborate, it works.
The makefile in this example wouldn't work on Windows. lessc should be lessc.cmd
node_modules/".bin"/lessc works! There are some subtile things like dirnames with dots need to be quoted but quoted forward slashes given to mkdir creates path seperator fuck ups C:\first/meh\second..

Annoying. But I still don't miss grunt :)

I'm not a Windows computer right now, but I believe you can't execute Node globals without the .cmd, am I wrong? If you have lessc in your PATH can you just do lessc (as this article does) or must you do lessc.cmd?
cygwin.com clears up that problem.
I threw out all gulp/grunt fluff from my shelve after learning make and never looked back. Makefiles are way smaller (40 vs 300 lines) and straight forward. It reduced my npm install time from 40s to 10s. Builds may take 2 seconds longer than gulp though.
I generally like make and I got some nice tricks from the article.

However the resulting Makefile became a complete mess. Which unfortunately is a very common problem one when it comes to Makefiles.

I would rephrase that as "Which, unfortunately, is a very common problem when it comes to making software." The tool doesn't even seem to matter that much. Typically one tool does a decent job at something but then completely drops the ball on other things.

Which I think is what plays to make's advantage. The one thing it does is track dependencies between files and the shell commands it takes to make them. The rest is punted to the shell commands.

I really like make and use it when it makes sense but I'm failing to see the advantages to doing something like this (other than support for make being fairly ubiquitous).

To accomplish the same/similar thing with gulp in one of my projects:

    var gulp = require('gulp');
    var less = require('gulp-less');
    var concat = require('gulp-concat');
    var minifyCSS = require('gulp-minify-css');
    var rename = require('gulp-rename');

    gulp.task('styles', function() {
    	return gulp.src(['assets/styles/**/*.less'])
    		.pipe(less())
    		.pipe(concat('app.css'))
    		.pipe(rename({suffix: '.min'}))
    		.pipe(minifyCSS())
    		.pipe(gulp.dest('www/css/'))
    });

The make example is really cool but I'd be lost if I needed to modify it even ever so slightly. Maybe this speaks more to my lack of experience using the sed/tr/etc but make just seems more complicated to manage. That's just my two cents.