We have several different tools to do such tasks. Rake, Grunt, Gulp, SCons and many others.
On the times of C, Make was the one. It is present on many projects. It is installed on lots of dev machines. It has small requirements to run. And it is very concise.
Make embraces the Unix style of communicating, providing pipes and calls to external commands like a shell would do to generate a file.
Let’s see how we can write a Makefile for your project, no matter which main language it is written in.
Writing your Makefile
While writing your Makefile, you may need to change the way you are used to defining your tasks. The idea is to define which files should be generated instead of defining the tasks themselves.
A Makefile is constructed of rules, each rule using the following format:
targets : prerequisites command 1 command 2 ...
We define a target file that will be the output of the build. Each target can specify prerequisites and a set of commands to build the target. The commands must be indented with tabs to be part of the rule.
Lets take the following rule as example:
target-file: source-file parse source-file --output target-file
source-file is newer than the
target-file the command on the rule will be executed to generate/update it.
The common way of thinking on some other tools is to define first the sources, and how it will produce an output. Instead, on Make, there is a reverse way of thinking, which you declare what is the file you would like to have generated out of the build, the target, and declare the prerequisites of the target, in order to be generated.
Making multiple files using the same rule
project ├── dist ├── index.html └── src ├── render.js └── request.js
We would like to generate minified files for each of those
A target can be multiple files,
with each of them mapping directly to a single prerequisite.
To define a relationship between the target file and its prerequisites,
we can use the
On the target,
% will match anything,
similar to file globbing on a shell,
and replace the
% character on the prerequisites side with what was matched previously.
We have defined a rule, where we want to generate one minified file on the
We now need to tell it how the file will be generated from it sources.
Make provides you with some Automatic variables. They are filled in with information from the rule it is running on.
We are going to use two variables,
[email protected] and
$<, the target name and the prerequisite name, respectively.
dist/%.min.js: src/%.js minify --output [email protected] $<
This rule will be expanded, and executed, as if you had written those lines:
dist/render.min.js: src/render.js minify --output dist/render.min.js src/render.js dist/request.min.js: src/request.js minify --output dist/request.min.js src/request.js
You can now run
make dist/render.min.js and
make dist/request.min.js and it will generate your minified files for you.
But this is still not ideal.
Ideally, we would like to ask Make to minify all scripts with one command.
Lets start defining defining a rule with the target of
requiring the minified files as prerequisites,
so we can run
So lets define a target:
minify: dist/render.min.js src/render.min.js dist/%.min.js: src/%.js minify --output [email protected] $<
There is no need do define a command to run for the rule, because the success definition for the target is to have the prerequisites satisfied.
As we talked before, Make expects you to define a file as the target, and it checks for the the last modified date of the target file to know if it needs to update.
As long as there is no file called
there won’t be a last modified date and it will assume it needs to create this allegedly
we don’t want a
minify file to be created as part of our rule,
nor would be ideal on the situation we create a file called
minify by mistake,
to stop our build to work because there is a modified date to check for now.
To solve this last issue we can make the target
minify a prerequisite to a special target called
which is the way to tell Make that the target is just a nice sound for a more complex set of requirements and not a file to be generated.
.PHONY: minify minify: dist/render.min.js src/render.min.js dist/%.min.js: src/%.js minify --output [email protected] $<
make minify will now generate the files for you and update the ones that need to be updated.
Calculating the generated files based on the source files
So far, our set of prerequisites of the
minify target is hardcoded.
Let’s make our set of prerequisites dynamic, based on our list of source files.
First, lets grab all the source files and store on a variable.
wildcard function expands to the list of files that exists on the
src folder, matching the pattern passed as argument.
src/*.js means any file ending with
.js in the
On our project, it would be expanded to the following.
We would like to have files with the same basename in a different folder,
following a pattern, substituting some parts of it.
Make has a function, called
patsubst, an abbreviation for “pattern substitution”.
patsubst function receives a pattern to match, a pattern to substitute and the paths to apply the substitutions.
The matching pattern can use the
% character to match anything,
similar to the
* character as we saw on the
while passing the grabbed value to the
% on the substitution pattern.
will expand to
Instead of using a single string on the end, we can substitute it with our previous list of files set on the
Putting it all together
After having our variables defined, we can tell the
minify target what are the prerequisites it has.
Whenever we add a new file,
make minify will add it as a prerequisite and generate it.
Parallelising the build
In this situation, our minified files can be generated in parallel.
Make accepts a job option,
where you can define the maximum number of jobs to run,
make -j 2 minify,
or let it use the maximum amount by not specifying a number,
make -j minify.
If you made this far, I hope you liked this quick intro to Make. If you are curious or want help on defining more complex tasks, the manual can provide more information.
Happy coding (: