Lesson · 40 min · Free
Build Tools for Science: Make and CMake
Build Tools for Science: Make and CMake As your computational projects grow from single scripts into multi-file programs, manually compiling and linking code becomes tedious and error-prone. Imagine a genomics pipeline w
Build Tools for Science: Make and CMake
As your computational projects grow from single scripts into multi-file programs, manually compiling and linking code becomes tedious and error-prone. Imagine a genomics pipeline with dozens of source files, external libraries for sequence alignment, and different compilers on your university cluster versus your laptop. Rebuilding everything after changing one line wastes time, and forgetting to recompile a dependency produces subtle, hard-to-debug errors. Build automation tools solve this problem, and the two most important in scientific computing are Make and CMake . Make is a classic build tool that reads a file called a Makefile . A Makefile defines targets (files you want to produce, such as an executable), dependencies (files the target needs, such as source code or headers), and recipes (shell commands that build the target). Make is intelligent about timestamps: if you modify only one source file, Make recompiles just that file and everything downstream that depends on it, leaving the rest untouched. This incremental build behavior is why Make remains ubiquitous in bioinformatics software, where recompiling an entire analysis suite could take many minutes. One practical caveat for biologists: Make requires tab characters in recipes, not spaces. This is the single most common source of cryptic "missing separator" errors for newcomers. Also note that Make is language-agnostic — you can use it to orchestrate not just compilations but entire analysis workflows, such as running quality control, alignment, and variant calling in the correct order only when input files change. CMake addresses a different problem: portability. A Makefile written on Linux may not work on macOS or Windows because compilers, library locations, and system conventions differ. CMake is a build system generator : you write one platform-neutral CMakeLists.txt file, and CMake generates the appropriate native build files (Makefiles on Linux, Ninja or Visual Studio projects elsewhere). For pharmacy and biotech students, this matters because scientific software you download — from molecular dynamics packages like GROMACS to image analysis tools like ITK — is almost universally built with CMake. The typical installation workflow is: create a build directory, run cmake .. to configure, then run make (or cmake --build . ) to compile. A useful mental model: CMake generates build files; Make executes them. They are complementary, not competing. In modern practice, you describe your project once in CMake and let CMake handle compiler detection, dependency checking, and finding libraries like BLAS, HDF5, or Boost that your computational biology code may require.
Example 1: A Simple Makefile for a Genomics Utility
This Makefile builds a small program gc_counter that computes GC content from a FASTA file. Notice how changing sequence_utils.c triggers recompilation of only that file and relinking of the executable. # Makefile for gc_counter CXX = g++ CXXFLAGS = -Wall -O2 -std=c++17 TARGET = gc_counter OBJS = main.o sequence_utils.o fasta_parser.o $(TARGET): $(OBJS) $(CXX) $(CXXFLAGS) -o $(TARGET) $(OBJS) main.o: main.cpp sequence_utils.h fasta_parser.h $(CXX) $(CXXFLAGS) -c main.cpp sequence_utils.o: sequence_utils.cpp sequence_utils.h $(CXX) $(CXXFLAGS) -c sequence_utils.cpp fasta_parser.o: fasta_parser.cpp fasta_parser.h $(CXX) $(CXXFLAGS) -c fasta_parser.cpp clean: rm -f $(TARGET) $(OBJS) .PHONY: clean Run make to build the program and make clean to remove generated files. The .PHONY declaration tells Make that clean is a command label, not an actual file to create.
Example 2: A Minimal CMakeLists.txt for the Same Project
The equivalent project description in CMake is shorter and portable across operating systems: # CMakeLists.txt for gc_counter cmake_minimum_required(VERSION 3.16) project(gc_counter CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) add_executable(gc_counter main.cpp sequence_utils.cpp fasta_parser.cpp ) target_compile_options(gc_counter PRIVATE -Wall -O2) To build this project, run the following commands from the directory containing CMakeLists.txt . Keeping generated files in a separate build directory (the "out-of-source" build) keeps your source tree clean: mkdir build cd build cmake .. make ./gc_counter genome.fa
Key Takeaways
Make automates builds using targets, dependencies, and recipes, and performs incremental rebuilds based on file timestamps. Makefile recipes must be indented with tabs , not spaces — a frequent beginner pitfall. CMake is a build system generator that produces platform-specific build files from a single portable CMakeLists.txt . CMake and Make work together: CMake configures the project; Make (or Ninja) executes the compilation. Use out-of-source builds (a separate build/ directory) to keep source directories clean. Most scientific software (GROMACS, ITK, OpenMM) is installed via the cmake .. && make workflow, so fluency with these tools is essential for deploying research software on clusters. Make can also orchestrate multi-step data analysis pipelines, not just compiled programs.
Practice Exercise
Create a new directory containing three C++ source files: main.cpp , stats.cpp , and stats.cpp 's header stats.h . Have main.cpp call a function defined in stats.cpp that computes the mean and standard deviation of a vector of gene expression values. First, write a Makefile that compiles this program incrementally — verify with make that touching only stats.cpp recompiles just one file. Then, write a CMakeLists.txt for the same project and build it inside a build/ directory. Finally, add a clean target to your Makefile and test both build systems end-to-end, confirming your program runs correctly on a small test dataset of expression values.
Watch the full lesson — free
This topic is part of Computational Biomedicine: From Command Line to Single-Cell, a complete AI-narrated video course. Press play once and watch the entire lecture like a movie.
Start the course free →