Why you Need a Makefile on your Project

For one of Theodo’s clients, we built a complex website including a catalog, account management and the availability to order products.
As a result, the project was complex, with several languages (symfony, vue, javascript), utilities (docker, aws, webpack) and microservices (for the search of products, the management of accounts, the orders).

The impact of this complexity for the development teams was the numerous commands they had to use every day, and particularly to install the project.

Thus, and following Symfony 4 best practices, we decided to use make on the project to solve these problems.
And it worked!

What is make

Make is a build automation tool created in 1976, designed to solve dependency problems of the build process.
It was originally used to get compiled files from source code, for instance for C language.

In website development, an equivalent could be the installation of a project and its dependencies from the source code.

Let me introduce a few concepts about make that we will need after.

Make reads a descriptive file called Makefile, to build a target, executing some commands, and depending on a prerequisite.

The architecture of the Makefile is the following:

target: [prerequisite]
    command1
    [command2]

And you run make target in your terminal to execute the target commands. Simple, right?

Use it for project installation

What is mainly used to help project installation is a README describing the several commands you need to run to get your project installed.

What if all these commands were executed by running make install?
You would have your project working with one command, and no documentation to maintain anymore.

I will only describe a simple way to build dependencies from your composer.json file

vendor: composer.json
    composer install

This snippet will build the vendor directory, running composer install, only if the vendor directory does not exist. Or if composer.json file has changed since the last time you built the vendor directory.
Note that if you don’t want to check the existency of a directory or a file named as your target, you can use a Phony Target. It means adding the line .PHONY: target to your Makefile.

There is much more you can do, and I won’t talk about it here.
But if you want a nice example to convert an installation README into a Makefile, you can have a look at these slides, that are coming from a talk at Paris Symfony Live 2018.

Use it for the commonly used commands you need on your project

After the project installation, a complexity for the developer is to find the commands needed to develop features locally.
We decided to create a Makefile to gather all the useful commands we use in the project on a daily basis.

What are the advantages of this:

  • The commands are committed and versioned
  • All developers of the team are using the same, reviewed commands. -> there is no risk to forget one thing before executing a command line and break something
  • It is language agnostic -> which means you can start php jobs, javascript builds, docker commands, …
  • It’s well integrated with the OS -> for instance there is autocompletion for targets and even for options
  • You can use it for continuous improvement -> when a command fails for one dev, modify that command and commit the new version. It will never fail anymore!

Auto-generate a help target

But after a long time, we started having a lot of commands.
It was painful to find the one you wanted in the file, and even to know the one that existed

So we added a new target help, in order to automatically generate a list of available commands from the comments in the Makefile.

The initial snippet we used is:

.DEFAULT_GOAL := help
help:
    @grep -E '(^[a-zA-Z_-]+:.*?##.*$$)|(^##)' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}{printf "\033[32m%-30s\033[0m %s\n", $$1, $$2}' | sed -e 's/\[32m##/[33m/'

If you add the following target and comments in your Makefile:

## Example section
example_target: ## Description for example target
        @does something

It would give this help message:
makeHelpExampleOutput

This generic, reusable snippet has another advantage: the documentation it generates is always up to date!
And you can customize it to your need, for instance to display options associated to your commands.


You liked this article? You'd probably be a good match for our ever-growing tech team at Theodo.

Join Us