A Makefile declares build targets, their dependencies, and the recipe (shell commands) to produce them, rebuilding only what’s stale based on file timestamps. Recipe lines must be indented with a tab, not spaces.


Building a Shared Library

CC=gcc
CFLAGS=-Wall -Werror -fPIC
LDFLAGS=-shared
SRC=mylib.c
OBJ=$(SRC:.c=.o)
LIB=libmylib.so
 
all: $(LIB)
 
$(LIB): $(OBJ)
	$(CC) $(LDFLAGS) -L. -o $@ $^ -l<dependency>
 
%.o: %.c
	$(CC) $(CFLAGS) -c -o $@ $<
 
clean:
	rm -f $(OBJ) $(LIB)

-fPIC generates position-independent code, required for anything going into a shared object. $@ is the target, $^ all prerequisites, $< the first prerequisite — the pattern rule %.o: %.c compiles any .c into its matching .o without repeating the recipe per file.

Detecting the Operating System

$(OS) is set by the environment on Windows but not on Unix, so it doubles as the branch condition. On non-Windows, uname -s/uname -p (captured via $(shell ...)) identify the OS and CPU architecture, letting the same Makefile set the right -D defines across platforms without a separate build file per OS.

ifeq ($(OS),Windows_NT)
	CCFLAGS += -D WIN32
	ifeq ($(PROCESSOR_ARCHITEW6432),AMD64)
		CCFLAGS += -D AMD64
	else
		ifeq ($(PROCESSOR_ARCHITECTURE),AMD64)
			CCFLAGS += -D AMD64
		endif
		ifeq ($(PROCESSOR_ARCHITECTURE),x86)
			CCFLAGS += -D IA32
		endif
	endif
else
	UNAME_S := $(shell uname -s)
	ifeq ($(UNAME_S),Linux)
		CCFLAGS += -D LINUX
	endif
	ifeq ($(UNAME_S),Darwin)
		CCFLAGS += -D OSX
	endif
	UNAME_P := $(shell uname -p)
	ifeq ($(UNAME_P),x86_64)
		CCFLAGS += -D AMD64
	endif
	ifneq ($(filter %86,$(UNAME_P)),)
		CCFLAGS += -D IA32
	endif
	ifneq ($(filter arm%,$(UNAME_P)),)
		CCFLAGS += -D ARM
	endif
endif