Enabling Languages
CMake supports multiple programming languages through the project() and enable_language() commands. Each language enables compiler detection, flag management, and linking support.
The LANGUAGES Keyword
# Enable languages at project declaration
project(HybridApp
VERSION 1.0.0
LANGUAGES C CXX Fortran CUDA
)
# Supported LANGUAGES values:
# C, CXX, CUDA, OBJC, OBJCXX, Fortran, HIP, ISPC, ASM, Swift
enable_language()
Use enable_language() to conditionally enable languages after project():
project(MyProject LANGUAGES C CXX)
# Conditionally enable CUDA if toolkit is found
include(CheckLanguage)
check_language(CUDA)
if(CMAKE_CUDA_COMPILER)
enable_language(CUDA)
message(STATUS "CUDA enabled: ${CMAKE_CUDA_COMPILER_VERSION}")
else()
message(STATUS "CUDA not available — GPU kernels disabled")
endif()
# Conditionally enable Fortran
check_language(Fortran)
if(CMAKE_Fortran_COMPILER)
enable_language(Fortran)
endif()
flowchart TD
A[CMakeLists.txt] -->|"LANGUAGES C CXX CUDA"| B[Compiler Detection]
B --> C[C Compiler: gcc/clang]
B --> D[C++ Compiler: g++/clang++]
B --> E[CUDA Compiler: nvcc]
C --> F[.c files → .o objects]
D --> G[.cpp files → .o objects]
E --> H[.cu files → .o objects]
F --> I[Linker]
G --> I
H --> I
I --> J[Final Binary]
C and C++ Interop
extern "C" Linkage
C++ name mangles symbols; C does not. To call C functions from C++ (or expose C++ functions to C), use extern "C":
// mathlib.h — C-compatible header
#ifndef MATHLIB_H
#define MATHLIB_H
#ifdef __cplusplus
extern "C" {
#endif
// These functions use C linkage (no name mangling)
double fast_sqrt(double x);
int matrix_multiply(const double* A, const double* B, double* C, int n);
void initialize_library(void);
#ifdef __cplusplus
}
#endif
#endif // MATHLIB_H
# CMakeLists.txt — mixing C and C++ sources
cmake_minimum_required(VERSION 3.21)
project(MathLib LANGUAGES C CXX)
# C sources compiled with C compiler
add_library(mathlib_c STATIC
src/fast_sqrt.c
src/matrix_ops.c
)
target_include_directories(mathlib_c PUBLIC include)
# C++ wrapper calling C code
add_library(mathlib_cpp STATIC
src/math_wrapper.cpp
)
target_link_libraries(mathlib_cpp PUBLIC mathlib_c)
target_compile_features(mathlib_cpp PUBLIC cxx_std_17)
# Executable linking both
add_executable(app src/main.cpp)
target_link_libraries(app PRIVATE mathlib_cpp)
Name Mangling and Calling Conventions
C++ compilers mangle function names to encode parameter types. On Windows, you must also consider calling conventions:
// Windows DLL export with C linkage
#ifdef _WIN32
#define MYLIB_API __declspec(dllexport)
#else
#define MYLIB_API __attribute__((visibility("default")))
#endif
extern "C" {
MYLIB_API int compute(int x, int y);
MYLIB_API void* create_context(const char* config);
MYLIB_API void destroy_context(void* ctx);
}
Link a C Library from C++
cmake_minimum_required(VERSION 3.21)
project(Interop LANGUAGES C CXX)
# Pure C library (compiled with C compiler)
add_library(fast_math STATIC src/fast_math.c)
target_include_directories(fast_math PUBLIC include)
set_target_properties(fast_math PROPERTIES
C_STANDARD 11
C_STANDARD_REQUIRED ON
)
# C++ application calling C functions via extern "C" header
add_executable(calculator src/main.cpp)
target_link_libraries(calculator PRIVATE fast_math)
target_compile_features(calculator PRIVATE cxx_std_20)
cmake -S . -B build && cmake --build build
./build/calculator
Fortran Integration
Scientific computing often requires calling Fortran routines (BLAS, LAPACK) from C/C++. CMake handles Fortran/C interop through the ISO_C_BINDING module:
project(SciCompute LANGUAGES C CXX Fortran)
# Fortran numerical library
add_library(numerics STATIC
src/solver.f90
src/integrator.f90
)
# C++ driver linking Fortran
add_executable(simulation src/main.cpp src/c_interface.c)
target_link_libraries(simulation PRIVATE numerics)
// Fortran function declaration in C (matches ISO_C_BINDING)
extern "C" {
// Fortran: subroutine solve_system(A, b, x, n) bind(C, name="solve_system")
void solve_system(double* A, double* b, double* x, int* n);
}
CUDA Support
CMake has first-class CUDA support since CMake 3.8:
cmake_minimum_required(VERSION 3.21)
project(GpuAccel LANGUAGES CXX CUDA)
# Set CUDA architecture (compute capability)
set(CMAKE_CUDA_ARCHITECTURES 75 80 86)
# CUDA library
add_library(gpu_kernels STATIC
src/kernels/vector_add.cu
src/kernels/reduction.cu
)
target_compile_features(gpu_kernels PUBLIC cuda_std_17)
set_target_properties(gpu_kernels PROPERTIES
CUDA_SEPARABLE_COMPILATION ON
CUDA_RESOLVE_DEVICE_SYMBOLS ON
)
# Host C++ code linking CUDA device code
add_executable(gpu_app src/main.cpp)
target_link_libraries(gpu_app PRIVATE gpu_kernels)
# Enable CUDA language extensions in .cpp files (optional)
set_source_files_properties(src/main.cpp PROPERTIES LANGUAGE CUDA)
CMAKE_CUDA_ARCHITECTURES explicitly. The default may not match your GPU. Use nvidia-smi to find your compute capability, then set it (e.g., 86 for RTX 3080, 89 for RTX 4090).
Python Extensions
Python3_add_library
CMake's FindPython3 module provides Python3_add_library() for building C/C++ extension modules:
cmake_minimum_required(VERSION 3.21)
project(PyExtension LANGUAGES CXX)
find_package(Python3 REQUIRED COMPONENTS Interpreter Development.Module)
# Create Python extension module
Python3_add_library(mymodule MODULE
src/bindings.cpp
src/algorithms.cpp
)
target_compile_features(mymodule PRIVATE cxx_std_17)
# Install to Python site-packages
install(TARGETS mymodule
LIBRARY DESTINATION ${Python3_SITEARCH}
)
Cython Integration
# Build Cython .pyx files through CMake
find_package(Python3 REQUIRED COMPONENTS Interpreter Development.Module)
find_program(CYTHON_EXECUTABLE cython REQUIRED)
# Generate C from .pyx
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/fast_module.c
COMMAND ${CYTHON_EXECUTABLE} -3
${CMAKE_CURRENT_SOURCE_DIR}/src/fast_module.pyx
-o ${CMAKE_CURRENT_BINARY_DIR}/fast_module.c
DEPENDS src/fast_module.pyx
)
Python3_add_library(fast_module MODULE
${CMAKE_CURRENT_BINARY_DIR}/fast_module.c
)
Build a Python Module with CMake
cmake_minimum_required(VERSION 3.21)
project(FastMath LANGUAGES CXX)
find_package(Python3 REQUIRED COMPONENTS Interpreter Development.Module)
Python3_add_library(fastmath MODULE src/fastmath_module.cpp)
target_compile_features(fastmath PRIVATE cxx_std_17)
# Verify import works
add_test(NAME test_import
COMMAND Python3::Interpreter -c "import fastmath; print(fastmath.add(2, 3))"
WORKING_DIRECTORY $<TARGET_FILE_DIR:fastmath>
)
cmake -S . -B build
cmake --build build
cd build && python3 -c "import fastmath; print(fastmath.add(2,3))"
# Output: 5
SWIG Wrapping
SWIG generates bindings for Python, Java, C#, and more from a single interface file:
cmake_minimum_required(VERSION 3.21)
project(SwigDemo LANGUAGES CXX)
find_package(SWIG 4.0 REQUIRED)
find_package(Python3 REQUIRED COMPONENTS Interpreter Development)
include(UseSWIG)
# The C++ library to wrap
add_library(geometry STATIC src/geometry.cpp)
target_include_directories(geometry PUBLIC include)
# SWIG interface generates Python bindings
set_source_files_properties(swig/geometry.i PROPERTIES
CPLUSPLUS ON
SWIG_MODULE_NAME geometry
)
swig_add_library(geometry_python
TYPE MODULE
LANGUAGE python
SOURCES swig/geometry.i
)
target_link_libraries(geometry_python PRIVATE geometry Python3::Module)
target_include_directories(geometry_python PRIVATE include)
Object Library Mixing
Object libraries let you compile sources once and reuse the objects in multiple targets — even across languages:
# Compile C utilities once as an object library
add_library(utils_obj OBJECT
src/utils.c
src/memory_pool.c
)
target_include_directories(utils_obj PUBLIC include)
# Use objects in C++ static library
add_library(engine STATIC src/engine.cpp)
target_link_libraries(engine PRIVATE utils_obj)
# Also use same objects in C++ shared library
add_library(engine_shared SHARED src/engine.cpp)
target_link_libraries(engine_shared PRIVATE utils_obj)
set_target_properties(utils_obj PROPERTIES POSITION_INDEPENDENT_CODE ON)
flowchart TD
A[utils.c + memory_pool.c] -->|OBJECT library| B[utils_obj]
B --> C[engine STATIC]
B --> D[engine_shared SHARED]
B --> E[test_runner executable]
C --> F[app1]
D --> G[plugin.so]
GPU-Accelerated C++ Application
cmake_minimum_required(VERSION 3.21)
project(GpuDemo LANGUAGES CXX CUDA)
set(CMAKE_CUDA_ARCHITECTURES 75)
# CUDA kernels
add_library(kernels STATIC src/vector_add.cu)
target_compile_features(kernels PUBLIC cuda_std_17)
# C++ host code
add_executable(gpu_demo src/main.cpp)
target_link_libraries(gpu_demo PRIVATE kernels)
target_compile_features(gpu_demo PRIVATE cxx_std_17)
cmake -S . -B build
cmake --build build
./build/gpu_demo
Conclusion & Next Steps
CMake makes mixed-language projects manageable by handling compiler detection, flag propagation, and linking across language boundaries. Key takeaways:
- Use
check_language()+enable_language()for optional language support extern "C"prevents C++ name mangling for C-compatible interfacesPython3_add_library(MODULE)builds importable Python extensions- SWIG generates multi-language bindings from a single interface file
- Object libraries (
OBJECT) compile sources once for reuse across multiple targets