Charting the Path to Efficiency: The Significance of Makefiles in Software Development
❓The Significance of Makefiles
Have you ever wondered how to streamline your software development workflow and enhance your team's efficiency? One of the often-overlooked tools that can make a significant difference is the Makefile. In this article, we will explore the importance of Makefiles in software development and how they can help you save time and reduce errors in your projects.
📁 What is a Makefile?
A Makefile is a simple yet powerful script that automates the process of building, compiling, and managing software projects. It consists of rules and dependencies, allowing developers to define how source code files should be transformed into executable programs or other target files. Makefiles are widely used in Unix-like operating systems and are also supported on Windows through tools like GNU Make.
😇 Simplifying Compilation and Build Processes
One of the primary purposes of Makefiles is to simplify complex compilation and build processes. They help in managing dependencies, ensuring that only the necessary source files are recompiled when changes are made. This not only saves time but also reduces the risk of introducing errors into the codebase.
# Example Makefile for a C++ project
CXX = g++
CXXFLAGS = -std=c++11 -Wall
my_program: main.cpp utils.cpp
$(CXX) $(CXXFLAGS) -o my_program main.cpp utils.cpp
🧰 Examples
Here is a few examples of Makefiles i've built for myself
## ansible
run:
sh scripts/check_and_run.sh || true
playbook:
ansible-playbook ansible/playbooks/$(module)/$(playbook).yml -i ansible/inventory/$(inventory).ini
playbook-suite:
ansible-playbook ansible/suite/$(playbook).yml -i ansible/inventory/$(inventory).ini
# https://www.digitalocean.com/community/tutorials/how-to-access-system-information-facts-in-ansible-playbooks
facts:
ansible all -i ansible/inventory/$(inventory).ini -m setup
## aws
include config/aws.mk
describe:
aws ec2 describe-instances | jq '.Reservations[].Instances[] | {InstanceId, State, PublicIpAddress, PrivateIpAddress, Tags}'
# https://docs.aws.amazon.com/cli/latest/reference/ec2/run-instances.html
create:
aws ec2 run-instances \
--image-id $(image_id) \
--instance-type $(instance_type) \
--key-name $(key_name) \
--security-group-ids $(security_group_ids) \
--subnet-id $(subnet_id) \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=$(name)}]' \
--associate-public-ip-address
terminate:
aws ec2 terminate-instances --instance-id $(instance_id)
You can include variables with the include
keyword and the file .mk
as you may noticed that i'm using there.
It looks like this:
image_id ?= ami-0261755bbcb8c4a84
instance_type ?= t2.micro
key_name ?= jonathan@jonathan
security_group_ids ?= sg-0b156e283023b9fc2
subnet_id ?= subnet-0bcc53200daecb50e
vpc_id ?= vpc-074271020d1e20c8f
name ?= testing
If you don't expecify a value you can have a default one so I only type make create
and have a fast and reliable ec2 instance to test my playbooks
Then I can delete it with make terminate instance_id="<id>"
You can use it anywhere for example here is one really easy that I normally use in python apps built with pip
main: install
$(MAKE) start
install:
pip install -r requirements.txt
requirements:
pip freeze > requirements.txt
dev:
uvicorn main:app --reload --port 4500
start:
uvicorn main:app --host 0.0.0.0 --port 4500
build:
docker compose up -d --build
up:
docker compose up -d
👿 Complex Example
include config.mk
## Application
.PHONY: deploy
deploy:
$(MAKE) start action=apply environment=$(environment)
.PHONY: destroy
destroy:
$(MAKE) start action=destroy environment=$(environment)
.PHONY: list
list:
$(MAKE) start action=show environment=$(environment)
install:
@echo "Running installation script..."
chmod +x scripts/run.sh
scripts/run.sh
### S3
community:
@if ! ansible-galaxy collection list | grep -i 'community.aws'; then \
ansible-galaxy collection install community.aws; \
fi
@if ! pip show boto3; then \
pip install --user boto3; \
fi
@echo "Ansible Galaxy community.aws and boto3 already installed"
git_submodule:
git submodule add $(submodule) app/$(app_name)
build_app:
cd app/$(app_name) && npm i && npm run build
# Requires boto3 installed with pip
# https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html#installation
echo:
@echo "🪣 Bucket name: $(bucket_name)"
@echo "🍎 App name: $(app_name)"
@echo "📩 Environment: $(environment)"
ansible_list:
$(MAKE) echo
ansible-playbook ansible/playbook/s3_list.yml -e "bucket_name=$(bucket_name)" -e "access_key=$(access_key)" -e "secret_key=$(secret_key)" -i ansible/inventory/hosts
.PHONY: ansible_sync
ansible_sync:
$(MAKE) echo
ansible-playbook ansible/playbook/s3_sync.yml -e "bucket_name=$(bucket_name)" -e "app_name=$(app_name)" -i ansible/inventory/hosts
.PHONY: ansible_rm
ansible_rm:
$(MAKE) echo
ansible-playbook ansible/playbook/s3_remove.yml -e "bucket_name=$(bucket_name)" -i ansible/inventory/hosts
### Terraform
upgrade:
cd terraform && terraform init -upgrade
#### init - get - output ####
start_noargs:
@echo "Running terraform $(action)... with no args"
cd terraform && terraform $(action)
#### apply - destroy - plan - show - refresh ####
.PHONY: start
start:
ifeq ($(action), show)
cd terraform && terraform show -json -compact-warnings | jq -r '(.$(jsonnames) | tostring) + " | " + (.$(jsonvalues) | tostring)'
else
@echo "Running terraform $(action)..."
cd terraform && terraform $(action) -var-file="config/$(environment).tfvars" -compact-warnings
endif
This one contains conditional executions, .PHONY targets, scripting, and many other things to make it clear. It may not be immediately obvious, but if you start small and build it piece by piece, you will notice that it is not that difficult. Additionally, when you encounter a project and want to build it from source, you will likely need to examine the makefile and understand what is happening.
🏁 Conclusion
Makefiles are a valuable asset in software development. They simplify complex processes, automate tasks, and reduce errors by managing dependencies. The examples provided illustrate how Makefiles can enhance productivity, from AWS resource management to application deployment. Whether you're working on a small project or a large-scale development, Makefiles are a powerful tool to streamline your workflow, save time, and maintain code quality. Embrace Makefiles to automate and simplify your development tasks for improved project management.