Hacer Cheatsheet
Sinopsis
GNU Hacer es una herramienta de automatización de construcción que construye automáticamente programas ejecutables y bibliotecas del código fuente leyendo archivos llamados Makefiles que especifican cómo derivar el programa de destino.
Conceptos básicos
Estructura del fichero
target: dependencies
command
command
Términos clave
Target # File to be created or action to be performed
Dependency # Files that target depends on
Recipe # Commands to create target
Rule # Target + dependencies + recipe
Variable # Named value that can be reused
Basic Makefile
Ejemplo simple
# Simple C program build
program: main.o utils.o
gcc -o program main.o utils.o
main.o: main.c
gcc -c main.c
utils.o: utils.c
gcc -c utils.c
clean:
rm -f *.o program
Con variables
CC = gcc
CFLAGS = -Wall -g
OBJECTS = main.o utils.o
TARGET = program
$(TARGET): $(OBJECTS)
$(CC) -o $(TARGET) $(OBJECTS)
main.o: main.c
$(CC) $(CFLAGS) -c main.c
utils.o: utils.c
$(CC) $(CFLAGS) -c utils.c
clean:
rm -f $(OBJECTS) $(TARGET)
.PHONY: clean
Variables
Definición variable
# Simple assignment
CC = gcc
CFLAGS = -Wall -g
# Recursive assignment (evaluated when used)
SOURCES = $(wildcard *.c)
# Simple assignment (evaluated immediately)
OBJECTS := $(SOURCES:.c=.o)
# Conditional assignment (only if not already defined)
CC ?= gcc
# Append to variable
CFLAGS += -O2
Variables incorporadas
# Automatic variables
$@ # Target name
# Make Cheatsheet
## Overview
GNU 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.
## Basic Concepts
### Makefile Structure
```makefile
objetivo: dependencias
comando
comando
Key Terms
Objetivo # Archivo que se creará o acción que se realizará
Dependencia # Los archivos que apuntan dependen de
Receta # Comandos para crear el objetivo
Regla # Meta + dependencias + receta
Valor variable # Nombre que se puede reutilizar
Basic Makefile
Simple Example
# Construcción del programa C simple
programa: main.o utils.o
gcc -o programa principal.o utils. o
main.o: main.c
gcc -c principal. c
utils.o: utils.c
gcc -c utils. c
limpio:
rm -f *.o program
With Variables
CC = gcc
CFLAGS = -Wall -g
OBJETOS = main.o utils.o
TARGET = programa
$(TARGET): $(OBJECTS)
$(CC) -o $(TARGET) $(OBJECTS)
main.o: main.c
$(CC) $(CFLAGS) -c principal. c
utils.o: utils.c
$(CC) $(CFLAGS) -c utils. c
limpio:
rm -f $(OBJECTS) $(TARGET)
. PHONY: limpio
Variables
Variable Definition
# Una asignación sencilla
CC = gcc
CFLAGS = -Wall -g
# Cesión recuperada (valorada cuando se usa)
SOURCES = $(wildcard *.c)
# Una asignación simple (evaluada inmediatamente)
OBJETOS := $(SOURCES:.c=.o)
# Asignación condicional (sólo si no está ya definida)
CC ?= gcc
# Apéndice a variable
CFLAGS += -O2
Built-in Variables
`< # First dependency $^ # All dependencies $? # Dependencies newer than target $* # Stem of pattern rule match
Example usage
%.o: %.c $(CC) $(CFLAGS) -c $< -o $@
### Medio ambiente
```makefile
# Use environment variables
HOME_DIR = $(HOME)
PATH_VAR = $(PATH)
# Override environment variables
export CC = gcc
export CFLAGS = -Wall
Normas modelo
Patrones básicos
# Pattern rule for C files
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# Pattern rule for C++ files
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
# Multiple patterns
%.o: %.c %.h
$(CC) $(CFLAGS) -c $< -o $@
Patrones avanzados
# Static pattern rule
$(OBJECTS): %.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# Pattern with subdirectories
build/%.o: src/%.c
@mkdir -p build
$(CC) $(CFLAGS) -c $< -o $@
Funciones
Funciones de cuerda
# Substitution
SOURCES = main.c utils.c
OBJECTS = $(SOURCES:.c=.o)
# or
OBJECTS = $(patsubst %.c,%.o,$(SOURCES))
# Word functions
FIRST_WORD = $(firstword $(SOURCES))
WORD_COUNT = $(words $(SOURCES))
NTH_WORD = $(word 2,$(SOURCES))
# Filter functions
C_FILES = $(filter %.c,$(SOURCES))
NOT_MAIN = $(filter-out main.c,$(SOURCES))
Funciones de archivo
# Wildcard
SOURCES = $(wildcard src/*.c)
HEADERS = $(wildcard include/*.h)
# Directory functions
SRC_DIRS = $(dir $(SOURCES))
BASE_NAMES = $(notdir $(SOURCES))
BASENAMES = $(basename $(SOURCES))
SUFFIXES = $(suffix $(SOURCES))
# Path manipulation
ABS_PATH = $(abspath relative/path)
REAL_PATH = $(realpath symlink/path)
Funciones condicionales
# If function
RESULT = $(if $(DEBUG),debug,release)
# Or function
COMPILER = $(or $(CC),gcc)
# And function
VALID = $(and $(CC),$(SOURCES))
Declaraciones condicionales
ifeq/ifneq
ifeq ($(DEBUG),1)
CFLAGS += -g -DDEBUG
else
CFLAGS += -O2 -DNDEBUG
endif
ifneq ($(OS),Windows_NT)
CFLAGS += -fPIC
endif
ifdef/ifndef
ifdef DEBUG
CFLAGS += -g
endif
ifndef CC
CC = gcc
endif
Asignación condicional
# Set if not already defined
CC ?= gcc
# Set based on condition
CFLAGS = $(if $(DEBUG),-g -O0,-O2)
Características avanzadas
Incluye archivos
# Include other makefiles
include config.mk
include $(wildcard *.d)
# Silent include (no error if file doesn't exist)
-include optional.mk
Generación de dependencia
# Automatic dependency generation
DEPDIR := .deps
DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.d
%.o: %.c $(DEPDIR)/%.d|$(DEPDIR)
$(CC) $(DEPFLAGS) $(CFLAGS) -c $< -o $@
$(DEPDIR):
@mkdir -p $@
DEPFILES := $(SOURCES:%.c=$(DEPDIR)/%.d)
$(DEPFILES):
include $(wildcard $(DEPFILES))
Sólo los requisitos de orden
# Create directory before building objects
$(OBJECTS):|$(OBJDIR)
$(OBJDIR):
mkdir -p $(OBJDIR)
Objetivos falsos
Popular Phony Targets
.PHONY: all clean install test help
all: $(TARGET)
clean:
rm -f $(OBJECTS) $(TARGET)
install: $(TARGET)
cp $(TARGET) /usr/local/bin/
test: $(TARGET)
./$(TARGET) --test
help:
@echo "Available targets:"
@echo " all - Build the program"
@echo " clean - Remove build files"
@echo " install - Install the program"
@echo " test - Run tests"
Multi-Target Builds
Múltiples programas
PROGRAMS = client server
COMMON_OBJS = utils.o network.o
all: $(PROGRAMS)
client: client.o $(COMMON_OBJS)
$(CC) -o $@ $^
server: server.o $(COMMON_OBJS)
$(CC) -o $@ $^
clean:
rm -f *.o $(PROGRAMS)
.PHONY: all clean
Edificio de la Biblioteca
LIBNAME = libutils
STATIC_LIB = $(LIBNAME).a
SHARED_LIB = $(LIBNAME).so
LIB_OBJS = utils.o network.o
all: $(STATIC_LIB) $(SHARED_LIB)
$(STATIC_LIB): $(LIB_OBJS)
ar rcs $@ $^
$(SHARED_LIB): $(LIB_OBJS)
$(CC) -shared -o $@ $^
%.o: %.c
$(CC) $(CFLAGS) -fPIC -c $< -o $@
Uso de la línea de comandos
Comandos básicos
# Build default target
make
# Build specific target
make clean
make install
# Build with variables
make DEBUG=1
make CC=clang
# Parallel builds
make -j4
make -j$(nproc)
# Dry run (show commands without executing)
make -n
# Silent mode
make -s
# Keep going on errors
make -k
Debugging
# Print variables
make -p
# Debug mode
make -d
# Print database
make --print-data-base
# Trace execution
make --trace
Ejemplos complejos
C++ Proyecto
CXX = g++
CXXFLAGS = -std=c++17 -Wall -Wextra -g
LDFLAGS = -lpthread
SRCDIR = src
OBJDIR = obj
SOURCES = $(wildcard $(SRCDIR)/*.cpp)
OBJECTS = $(SOURCES:$(SRCDIR)/%.cpp=$(OBJDIR)/%.o)
TARGET = myprogram
.PHONY: all clean
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(CXX) $(OBJECTS) -o $@ $(LDFLAGS)
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp|$(OBJDIR)
$(CXX) $(CXXFLAGS) -c $< -o $@
$(OBJDIR):
mkdir -p $(OBJDIR)
clean:
rm -rf $(OBJDIR) $(TARGET)
# Dependency generation
-include $(OBJECTS:.o=.d)
$(OBJDIR)/%.d: $(SRCDIR)/%.cpp|$(OBJDIR)
$(CXX) -MM -MT $(@:.d=.o) $< >`` $@
Multi-Directory Project
# Project structure
PROJECT_ROOT = .
SRC_DIRS = src src/utils src/network
INC_DIRS = include
OBJ_DIR = build
BIN_DIR = bin
# Find all source files
SOURCES = $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.c))
OBJECTS = $(SOURCES:%.c=$(OBJ_DIR)/%.o)
TARGET = $(BIN_DIR)/myapp
# Compiler settings
CC = gcc
CFLAGS = -Wall -g $(addprefix -I,$(INC_DIRS))
LDFLAGS = -lm
.PHONY: all clean dirs
all: dirs $(TARGET)
dirs:
@mkdir -p $(OBJ_DIR) $(BIN_DIR)
@mkdir -p $(foreach dir,$(SRC_DIRS),$(OBJ_DIR)/$(dir))
$(TARGET): $(OBJECTS)
$(CC) $^ -o $@ $(LDFLAGS)
$(OBJ_DIR)/%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -rf $(OBJ_DIR) $(BIN_DIR)
Buenas prácticas
Makefile Organization
# Variables at the top
CC = gcc
CFLAGS = -Wall -g
TARGET = myprogram
# Default target first
all: $(TARGET)
# Pattern rules
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# Specific rules
$(TARGET): $(OBJECTS)
$(CC) $^ -o $@
# Phony targets at the end
.PHONY: all clean install
clean:
rm -f *.o $(TARGET)
Manejo de errores
# Check for required tools
ifeq ($(shell which $(CC)),)
$(error Compiler $(CC) not found)
endif
# Validate variables
ifndef SOURCES
$(error SOURCES variable is not defined)
endif
# Conditional compilation
ifeq ($(shell pkg-config --exists gtk+-3.0; echo $?),0)
CFLAGS += $(shell pkg-config --cflags gtk+-3.0)
LDFLAGS += $(shell pkg-config --libs gtk+-3.0)
else
$(warning GTK+ 3.0 not found, building without GUI)
endif
Consejos de rendimiento
# Use order-only prerequisites for directories
$(OBJECTS):|$(OBJDIR)
# Minimize shell calls
SOURCES := $(shell find src -name '*.c')
# Use built-in rules when possible
# Instead of:
# %.o: %.c
# $(CC) $(CFLAGS) -c $< -o $@
# Just use the built-in rule
# Parallel-safe directory creation
$(OBJDIR):
@mkdir -p $@
Integración
Con Control de Versión
# Get version from git
VERSION = $(shell git describe --tags --always --dirty)
CFLAGS += -DVERSION=\"$(VERSION)\"
# Include git info
GIT_COMMIT = $(shell git rev-parse HEAD)
BUILD_DATE = $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
CFLAGS += -DGIT_COMMIT=\"$(GIT_COMMIT)\" -DBUILD_DATE=\"$(BUILD_DATE)\"
Con Administradores de Paquetes
# pkg-config integration
CFLAGS += $(shell pkg-config --cflags libssl)
LDFLAGS += $(shell pkg-config --libs libssl)
# Check dependencies
check-deps:
@pkg-config --exists libssl||(echo "libssl not found" && exit 1)
Solución de problemas
Cuestiones comunes
# Missing separator error
# Make sure recipes use tabs, not spaces
# Target not found
make: *** No rule to make target 'foo'. Stop.
# Check target name and dependencies
# Circular dependency
make: Circular dependency dropped.
# Review dependency chain
# Debug makefile
make -p|grep target_name
make -d
Consejos de depuración
# Print variable values
debug:
@echo "CC = $(CC)"
@echo "CFLAGS = $(CFLAGS)"
@echo "SOURCES = $(SOURCES)"
@echo "OBJECTS = $(OBJECTS)"
# Check if file exists
check-file:
@test -f myfile.c && echo "File exists"||echo "File missing"
Recursos
- Manual oficial: gnu.org/software/make/manual
- GNU Make: gnu.org/software/make
- Tutorial: makefiletutorial.com
- Las mejores prácticas: clarkgrubb.com/makefile-style-guide