Saltar a contenido

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

.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