Swipe left or right to navigate to next or previous post
Makefile is a special text based file that is used by make utility. It automates the process of compiling and linking programs. It contains list of a command that can be quickly executed by running the corresponding make command. It contains sets of rules to build a target command or set of command. Makefile is easy to execute a long list of commands and functions using a single command.
Makefile are commonly used in software development projects to manage complex build processes.
The simple and high level syntax for Makefile target is as follows:
ruleA: ruleB ruleC command1 command2
A Makefile mainly consists of a set of rules, each of which has a list of zero or more prerequisites, and then zero or more commands. At a very high level, when a rule is invoked, Make first invokes any prerequisite rules (if needed), which may have their own stanzas defined elsewhere) and then runs the designated commands.
The high level syntax with specific key word for Make target is as follows:
target: prerequisites command command command
A target can be the name of the executable or an object file. It can also be the name of a rule or action.
A prerequisite is an action or file used as an input for the target. These files/actions need to exist before the commands for the target are run. These are also called dependencies. We can declare multiple dependencies for a target by separating them with blank space. Some rules may not require any dependencies.
A command is a single unit of work that needs to be done in order to make the target(s). These need to start with a tab character.
The basic example of the Makefile is:
say_hello: echo make says hello
To install the GNU Make command, please run the following commands
sudo apt update sudo apt install make
For other Distros, please follow GNU Make
To create the configuration file for make command, Just create the file name called Makefile without any file extensions
touch Makefile
To add contents in Makefile. Open the file with your favorite text editor and put make command as shown in the following example
say_hello: echo "Hello world" say_welcome: echo "Welcome guys"
To run the make command, just type make followed with the rule name like say_hello or say_welcome. If you just type make command, the default rule will run. We will explore about default rule later in this post. If default rule is missing, it will run the first rule in the Makefile.
make {replace_with_command_from_the_make_file} make say_hello make say_welcome
runserver: python manage.py runserver makemigrations: python manage.py makemigrations migrate: python manage.py migrate createsuperuser: python manage.py createsuperuser
By default, when we run the make command, the first rule is executed. To execute some other rules of our choice, we need to set .DEFAULT_GOAL variable in the Makefile. For example, if you want to execute runserver by default, define .DEFAULT_GOAL :=runserver. Now, when we run the make command, it will run the runserver command.
Alternately use can use default keyword. For example, if you want to execute runserver by default, define .default:runserver. Now, when we run the make command, it will run the runserver command.
.DEFAULT_GOAL :=runserver runserver: python manage.py runserver makemigrations: python manage.py makemigrations migrate: python manage.py migrate createsuperuser: python manage.py createsuperuser
build: docker compose build; test-build-nocache: docker compose -f docker-compose.yml build --no-cache; deploy: docker-compose build docker-compose up -d run-server: docker compose up; test-run-server-d: docker compose -f docker-compose.yml up -d; migrations: docker compose exec django python manage.py makemigrations; migrate: docker compose exec django python manage.py migrate; shell: docker compose exec django python manage.py shell; down: docker compose down;
Instead of typing the following commands one by one.
virtualvenv -p python3 venv source venv/bin/activate pip install -r requirements.txt python3 manage.py makemigrations python3 manage.py migrate python3 manage.py collectstatic python3 manage.py runserver
It would be easier to run something like that run all the above commands.
make start
Before that, let's learn about the creating and accessing the variables in the Makefile
Variables can be defined inside a Makefile to hold the information about files, arguments or even parts of the command. Variables are written in UPPER_CASE followed by colons and is equals to sign (:=) and its value. Generally, variables are declared at the top of the Makefile.
PYTHON:=python3.12 MANAGE:=venv/bin/python manage.py ACTIVATE:=venv/bin/activate # Django Configuration PORT:= 8000
Variables that are declared in Makefile can be access as a part of the command. The variables can ve access using $ sign followed by curly braces like ${VARIABLE_NAME} where VARIABLE_NAME is the name of the variable whose value you want to access.
PYTHON:=python3.12 MANAGE:=venv/bin/python manage.py ACTIVATE:=venv/bin/activate # Django Configuration PORT:= 8000 install: virtualenv @echo "-> Installing Dependencies" @${ACTIVATE} pip3 install -r requirements.txt migrate: ${MANAGE} makemigrations @echo "-> Apply database migrations" ${MANAGE} migrate run: ${MANAGE} runserver ${PORT} superuser: @${MANAGE} createsuperuser
In this example, the ACTIVATE variable as been accessed using ${ACTIVATE}
VENV := venv BIN := $(VENV)/bin PYTHON := $(BIN)/python3 SHELL := /bin/bash include .env help: ## Show this help @egrep -h '\s##\s' ${MAKEFILE_LIST} | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' venv: ## Make a new virtual environment virtualvenv -p python3 venv && source ${BIN}/activate install: venv ## Make venv and install requirements ${BIN}/pip install --upgrade -r requirements.txt freeze: ## Pin current dependencies ${BIN}/pip freeze > requirements.txt migrate: ## Make and run migrations ${PYTHON} manage.py makemigrations ${PYTHON} manage.py migrate db-up: ## Pull and start the Docker Postgres container in the background docker pull postgres docker-compose up -d db-shell: ## Access the Postgres Docker database interactively with psql. Pass in DBNAME={name}. docker exec -it container_name psql -d ${DBNAME} test: ## Run tests ${PYTHON} manage.py test application --verbosity=0 --parallel --failfast run: ## Run the Django server ${PYTHON} manage.py runserver start: install migrate run ## Install requirements, apply migrations, then start development server
The above Makefile contains .env as include .env. It was included to ensure the access to environment variables stored in an .env file. This allows Make to use these variables in its commands. For example, the name of virtual environment or to pass in ${DBNAME} to psql
“##” comment syntax in a Makefile gives you a handy description of command-line aliases you can look in to your Django project. It’s very useful so long as you’re able to remember what all those aliases are. The help command above, which runs by default, prints a helpful list of available commands when you run make or make help:
The help command above, which runs by default, prints a helpful list of available commands when you run make or make help:
help Show this help venv Make a new virtual environment install Make venv and install requirements migrate Make and run migrations db-up Pull and start the Docker Postgres container in the background db-shell Access the Postgres Docker database interactively with psql test Run tests run Run the Django server start Install requirements, apply migrations, then start development server
.PHONY is actually itself a target for the make command. It denotes labels that do not represent actual files of the project.
Look at the basic example of Makefile
install: echo "installing dependencies"
Everytime when you run the make install command, it will execute the specified command from the Makefile (in our case echo "installing dependencies"). However, if we have the file name called install, the make command would bound to run the install file instead of the command in the Makefile. To override this behaviour, .PHONY is used to make install command to execute no matter of the presence of a file named build.
.PHONY: install install: echo "installing dependencies"