Remember all the commands of a project with Makefile

We will see how to remember all command of a project & write documentation with Makefile !
Friday, January 14, 2022

Problematic

When we want to build our application, whatever the language, there are usually a ton of commands to remember.

Here is an example that we can find when we develop in PHP and Javascript:

Terminal window
composer install # Install the PHP dependencies
php artisan key:generate # Generate a secret key
php artisan test # Execute the PHP tests
php artisan test --testsuite=Feature --stop-on-failure # An other command of the tests
yarn # Install the NodeJS dependancies
yarn test # Execute the NodeJS tests
php artisan migrate # Migrate the database
php artisan db:seed # Populate the database with fake data
./node_modules/.bin/eslint my-file.js # Lint the code
# Format the code
# ...
# etc ...
# ...

The problem is: the more technologies we have, the more terminal commands we have to remember.

And if we have several projects at the same time, we run the risk of getting confused by forgetting the commands.

And now, imagine leaving this project for 2 months, and trying to re-understand how to compile your project :/

Makefile

One solution I use on a daily basis is to use a Makefile. It is an utility installed by default in all linux distributions, used first for the C language.

Basically, Makefile is a utility that scans files and runs commands whether files need to be recompiled or not. However, here, we can use it to “store” the important commands of our project.

In software development, Make is a build automation tool that automatically builds executable programs and libraries from source code by reading files called Makefiles which specify how to derive the target program. Though integrated development environments and language-specific compiler features can also be used to manage a build process, Make remains widely used, especially in Unix and Unix-like operating systems. Wikipedia

The basic structure

The usage is very simple. First, create a file named Makefile. The syntax inside is very simple:

Makefile
.PHONY: my_command_name
my_command_name: ## A little documentation here
echo "The real command behind"

You must indent with a tabulation and not 4 spaces!

Here is an example of a real case:

Makefile
.PHONY: install
install: ## Install the application
cp -n .env.example .env
pnpm install --workspace-root --recursive
.PHONY: serve
serve: ## Serve for development
docker-compose up

The help command

Paste this on the top of your Makefile:

Makefile
.DEFAULT_GOAL := help
.PHONY: help
help:
@egrep -h '\s##\s' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m %-30s\033[0m %s\n", $$1, $$2}'
.PHONY: serve
serve: ## Serve for development
docker-compose up

Then use the make command and press enter ! 🎉

Makefile tips

Use environment variables from .env file

First create a .env file in the root project directory and define your variables like this:

MY_VARIABLE="Hello World !"
MY_VARIABLE_2="Hello World 2 !"

Then, we can include the file and use the variable with the $(VARIABLE) syntax:

include .env
.PHONY: test
test: ## Display variables from ".env" file
echo $(MY_VARIABLE)

Hide commands:

In some cases, if we use password or API keys in our commands, we can write a @ in front of our commands like this:

.PHONY: deploy
deploy: ## Deploy the application
@deploy $(SSH_URL) $(SSH_KEY)

Here, the command @deploy $(SSH_URL) $(SSH_KEY) won’t be displayed on screen.

Define variables

We can define variables on top of the file like this:

pwd := $(dir $(abspath $(firstword $(MAKEFILE_LIST))))

Use global variables

If we want to export locally variables for our tasks, we can put this line on the top of the file:

export DOCKER_BUILDKIT=1

Note: the exported variables are only available in the Makefile context.

Full example

Here is a full example:

.env
name=alex

Makefile
include .env # Include environment file
# export DOCKER_BUILDKIT=1
# pwd := $(dir $(abspath $(firstword $(MAKEFILE_LIST))))
.DEFAULT_GOAL := help
.PHONY: help
help:
@grep -E -h '\s##\s' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m %-30s\033[0m %s\n", $$1, $$2}'
.PHONY: example
example: ## This is an example
echo "Hello World"
.PHONY: npm_u
npm_u: ## Install node_modules
ncu -i
.PHONY: echo_var_env_file
echo_var_env_file: ## Example
echo "Hello $(name)"
.PHONY: example_with_variable
example_with_variable: ## Example with variable
@NAME=$$(echo -n "Alex"); \
echo "Hello $$NAME"

Recommended articles