Nested Make

| 2 mins read

Problem

Assume that we have the following directory structure in a project:

/
├── db/
│   └── gen/
│       ├── customer_table.go
│       └── product_table.go
└── Makefile

Where you might have some targets defined in the Makefile, and you also need to run a specific command that generates some code (in this case the contents of /db/gen/ directory). Also, one limitation you have with the generator command is that is must be executed in the /db/ directory. How to solve this?

Solution

There are some common solutions for this:

  1. Write a script inside /db/ directory, e.g., /db/generate.sh, that executes the command. And, when you’d want to run it, you have to manually cd into /db/, run this script, and cd back to where you were in the first place (usually project’s root directory). Something similar to:

    cd ./db
    bash ./generate.sh
    cd ..
    
  2. Add a target to Makefile such that it includes cd command before executing the actual generator command.

  3. Add a target that executes the generator command in /db/Makefile, and a similar target in the root Makefile that simply forwards the target command execution to the Makefile inside /db/ directory.

The last solution, I think, makes intentions clearer, compared to the 1st solution, which at some point might cause confusions about who is going to execute that shell script, the application itself, or developers? What if a developer executes the script directly from project’s root directory? How is the platform support for that shell script format? When compared to the 2nd option, it doesn’t require any explicit cd instructions, which might become a problem when it comes to cross-platform development support. Of course there are some drawbacks to this solution, but let’s have a demonstration of it.

Content of /Makefile:

.PHONY: gen
gen:
	$(MAKE) -C ./db gen

And, /db/Makefile:

.PHONY: gen
gen:
	somescript that --generates files

This way, all you need to do, is to run make gen in the root directory, and it will run the gen target defined in the nested Makefile.