Skip to main content

One post tagged with "Makefile"

View All Tags

Commander of Shell: Makefile

· 3 min read

In the world of software development and DevOps, Makefile is an underrated yet powerful tool. It not only automates the compilation process but also serves as a versatile shell task manager, handling testing, deployment, and even system administration. When you need to organize and execute a sequence of shell commands efficiently, Makefile acts as the commander, ensuring a structured and streamlined execution.

What is Makefile?

Originally designed for Unix systems to manage code compilation via the make command, Makefile has evolved far beyond its initial purpose. Today, it is widely used as a powerful automation tool in DevOps workflows.

Why Use Makefile?

  1. Organized Command Execution: Define and reuse a series of shell commands to avoid manual input.
  2. Incremental Execution: make manages dependencies and only executes necessary tasks, improving efficiency.
  3. Cross-Platform Compatibility: Works on macOS, Linux, and Windows (via WSL or MinGW).
  4. Team Collaboration: A unified Makefile enables team members to execute development, testing, and deployment processes effortlessly.

Basic Makefile Syntax

A Makefile consists of a target, dependencies, and commands, following this structure:

target: dependencies
command

Example:

build:
echo "Starting compilation..."
gcc main.c -o main

Running make build triggers the echo and gcc commands.

Advanced Syntax

1. Suppressing Command Output with @

By default, Makefile prints executed commands. To suppress output, prefix commands with @:

echo_test:
@echo "This is a hidden command"

Executing make echo_test displays only This is a hidden command without showing echo itself.

2. Using Functions

Makefile includes built-in functions like shell, which executes shell commands and returns results:

CURRENT_DIR := $(shell pwd)

echo_dir:
@echo "Current directory: $(CURRENT_DIR)"

3. Built-in Variables and Pattern Rules

  • $@: Represents the target name
  • $^: All dependencies
  • $<: The first dependency

Example:

%.o: %.c
gcc -c $< -o $@

This rule compiles all .c files into .o files automatically.

In this rule, .c is the dependency (another line of instruction), and .o is the executed command. You are correct—commands can be named after file names.

.PHONY: build

That is why you may see a .PHONY section, which informs make that build is a command, not a generated file. make checks whether a target has already been created to avoid redundant execution.

Makefile in DevOps

1. Automating Development Setup

setup:
apt update && apt install -y python3
pip install -r requirements.txt

2. Running Tests & CI/CD

test:
pytest tests/

3. Deployment & Version Management

deploy:
scp main user@server:/app/
ssh user@server "systemctl restart app"

Conclusion

Makefile is one of the best tools for managing shell commands, making software development and DevOps workflows more structured and efficient. By utilizing @ for cleaner output, built-in functions for flexibility, and variables for automation, you can enhance readability and performance. If your project hasn’t adopted Makefile yet, start today and let it be your automation commander!