Why you Need a Makefile on your Project
Martin Guillier4 min read
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 breaking 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! - You can change once to change everywhere
-> use your Makefile commands from several scripts, when you update your commands, all scripts will be up-to-date at once!
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:
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.