Based on jsoverson's answer, I managed to cobble together a proof-of-concept that defers dependency tracking to the Gruntfile, so I can add Makefile rules that invoke the grunt bits to build the project. This project uses coffee-script (use http://js2coffee.org/ to convert to js if you want to reuse this for some non-coffee project), so in my Gruntfile.coffee I added

gruntGetPaths = (fn) -&gt; -&gt;
  taskConfig = grunt.config @args.join '.'
  grunt.task.normalizeMultiTaskFiles(taskConfig)
    .forEach fn ? (files) -&gt;
      files.src.forEach (path) -&gt;
        console.log path

grunt.registerTask 'src', gruntGetPaths
grunt.registerTask 'dst', gruntGetPaths (files) -&gt; console.log files.dest


giving me the grunt src:... and grunt dst:... rules that produce grunt-junk-wrapped file lists.

It seems the junk is guaranteed to be colorized / adds a trailing empty line (at least with grunt v0.4.1 / grunt-cli v0.1.9), so chopping that off by piping their output to egrep -v '\e|^$' worked.

Near the top of my Makefile I added some macros for that:

define GRUNT
    $(shell grunt --no-write $1 | egrep -v '\e|^$$')
endef
define SRC
    $(call GRUNT,src:$1)
endef
define DST
    $(call GRUNT,dst:$1)
endef


...and then rules that borrow knowledge from the Gruntfile:

$(call DST,stylus:compile): coffee $(call SRC,stylus:compile)
    grunt stylus

$(call DST,coffee:glob_to_multiple): coffee $(call SRC,coffee:glob_to_multiple)
    grunt coffee

$(call DST,uglify:my_target): coffee $(call SRC,uglify:my_target)
    grunt uglify

coffee:
    npm install 2&gt;&amp;1 | tee $@


...which has corresponding setup looking like this:

  @initConfig
    pkg: grunt.file.readJSON "package.json"

    stylus:
      compile:
        options:
          paths: ["src/stylus/"]
          import: ["nib"]
        files:
          "stylesheets/foo.css": "src/stylus/foo.styl"
          "stylesheets/foo-dev.css": ["src/stylus/foo.styl", "src/stylus/foo-dev.styl"]

    coffee:
      glob_to_multiple:
        expand: true
        cwd: 'src/coffee/'
        src: ['*.coffee']
        dest: 'javascripts/'
        ext: '.js'

    uglify:
      my_target:
        files:
          "javascripts/foo.min.js": ["javascripts/foo.js"]


This works, but is slow. Given a bare grunt stylus run that takes 2.94s to run, running these make rules to regenerate css takes another 5.41s of pure overhead, which is sort of horrible - and if I nuke all the generated files and try to regenerate the min.js, there is no dependency resolution as the glob rules can't be traced back to find all intermediate files.

So while it's possible to do this, it did not end up being a solution to the problem "running grunt is slow and stupid, when no source files have changed", as running grunt stylus coffee uglify in this project takes 3.25 seconds to reproduce what was already there, and a bare make run that just resolves dependencies and finds nothing relevant changed takes over five.

It would of course be great if grunt had its own dependency management to know when it can exit immediately, like our grandfather's tools would. :-)

How do I list all files a given grunt task depends on for a Makefile?
I quite often find I have a need to insert a blank line either below or above the current line when editing in vim. o and O will do this, but they subsequently switch into insert mode, which is annoying. Is there any built-in command to do this which will remain in normal mode?
