Passing args past make

Posted: 26 May, 2023 Category: backend Tagged: make

I was using Gnu Make as a task runner. Specifically, I was trying to invoke my hand-rolled, go migrator to create a new migration file. This meant passing in a name for the db migration, and I was having a heck of a time passing this command-line arg past make, and through to my fledgling go program. I kept getting *** No rule to make target ... errors: Make would encounter the first commandline token after the target, assume it was a target, try to build it, realise that it didn't know how to, then faceplant.

You can pass a named arg right on the command line, silly!

Yeah, I know I could've done name="add a column to foo table" but I did not want to be bothered with typing name=, each time, okay? Honestly I'm miffed I even have to type make <target> .... But we don't have telepathic interfaces to our machines, do we? So minimal commandlines are the next best thing emoji-smile.

OK well have you heard of .PHONY targets? Duh!

Yeah well they didn't work for my use case, so DOUBLE DUH?!! The arguments in this case aren't fixed / known ahead of time. Migration names can be almost anything (well, within reason). But point is, it's not a fixed token.

What actually worked

In the end after trawling through make info online, and performing a small test locally, this is the combination of things that worked:

  1. I needed a catch-all target which built/did nothing
  2. The MAKECMDGOALS var: it's basically the entire kitchen sink of arguments on the command line that make was invoked with.

Given a test script foo.js:

// a silly something that needs args passed down to it: just a stub mimicking my go program
const passedIn = process.argv.slice(3) // strip out nodejs invocation and make target name
console.log(`raw args of interest:[${passedIn}]`)

Turns out this is how you can slip past make:

# a match anything (else) rule, that does nothing, for those "*** No rule to make target ..." complaints.
# note: it will affect EVERY rule, so now you will be relying on each rule to fail in some other way when
# its dependencies aren't there / valid.

# ---- Failures that are still caught. Thank God! -----  
# will fail because command not found
	gecho "this is a cat\n"

# will fail because cats fails
bar: cats
	echo "hello world"

# ---- But we can still suppress the "no rule to make target" complaint! -----  
# This will pass because ALL the cmd line args, though parsed as targets by make, will fall into the "match anything else" trap.
	node ./foo.js $(MAKECMDGOALS)

So now I can type:

> make foo bob has all the burgers

...and the target app can pick up ["bob", "has", "all", "the", "burgers"], which is exactly what I want.

In other words, I can now do things like make create new index for the blah table and my migrator has a chance in hell of creating a migration file with a nice kebab-cased name and a unique identifier. Sweet!