Skip to main content

Ding Zhiyu Week 2 Study Report: Deep Dive into CMakeLists for Medium-Large Projects

​ Author: Ding Zhiyu, Date: 2024.1.28 ]

Main Content

Common CMake Syntax for Medium-Large Projects (Quick Reference)

Basic Project Settings

  1. Specify minimum CMake version requirement:

    cmake_minimum_required(VERSION 3.12.0 FATAL_ERROR)
  2. Project declaration:

    project(roms VERSION 3.9 LANGUAGES Fortran)
  3. Set variables and CMake module path:

    set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
  4. Set build type:

    set(CMAKE_BUILD_TYPE Release)

Compiler and Linker Configuration

  1. Add compiler options:

    add_compile_options(-Wall -Wextra)
  2. Set target-specific compile definitions:

    target_compile_definitions(roms_executable PRIVATE -DUSE_NETCDF4)
  3. Set target-specific compile options:

    target_compile_options(roms_executable PRIVATE -O2)
  4. Set target-specific link options:

    target_link_options(roms_executable PRIVATE -L/path/to/lib)

Environment Detection and Conditional Branching

  1. Check environment variables:
    if(DEFINED ENV{ECBUILD_MODULE_PATH})
    # 做一些特定于ecbuild的配置
    endif()

Including Other Scripts and Modules

  1. Include custom CMake scripts:

    include(roms_functions)
  2. Find and link dependency libraries:

    find_package(NetCDF REQUIRED COMPONENTS Fortran)

Source Code and Resource Management

  1. Add subdirectory:

    add_subdirectory(src)
  2. Specify header file and library directories:

    include_directories(${NetCDF_INCLUDE_DIRS})
    link_directories(${NetCDF_LIBRARY_DIRS})
  3. Set source file properties:

    set_property(SOURCE file.F90 PROPERTY COMPILE_FLAGS "-special-flag")
  4. Auto-generate source code:

    add_custom_command(
    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated_source.f90
    COMMAND generate_source_script
    DEPENDS script_input.dat
    )
  5. Handle files and directories:

    file(GLOB MY_SOURCES "src/*.cpp")
    file(COPY ${CMAKE_SOURCE_DIR}/data/ DESTINATION ${CMAKE_BINARY_DIR}/data)

Target Definition and Linking

  1. Create object library:

    add_library(Objects OBJECT ${All_f90s})
  2. Create static and shared libraries:

    add_library(ROMS_static STATIC $<TARGET_OBJECTS:Objects>)
    add_library(ROMS_shared SHARED $<TARGET_OBJECTS:Objects>)
  3. Build executable:

    add_executable(roms_executable ${master_f90})
    target_link_libraries(roms_executable ${NetCDF_LIBRARIES})

Installation and Testing

  1. Install targets and files:

    install(TARGETS roms_executable DESTINATION bin)
    install(FILES readme.txt DESTINATION share/doc)
    install(DIRECTORY docs/ DESTINATION share/doc)
  2. Enable and add tests:

    enable_testing()
    add_test(NAME MyTest COMMAND TestExecutable)

Packaging and Project Export

  1. Set up CPack packaging:

    set(CPACK_PACKAGE_NAME "MyApp")
    include(CPack)
  2. Export and install targets:

    export(TARGETS roms_executable FILE ROMSExecutableTargets.cmake)
    install(EXPORT ROMSExecutableTargets FILE ROMSExecutableTargets.cmake DESTINATION lib/cmake/ROMS)

Custom Build Steps and Cleanup

  1. Add custom build commands and targets:

    add_custom_target(clean-all COMMAND ${CMAKE_MAKE_PROGRAM} clean)
    add_custom_command(TARGET roms_executable POST_BUILD COMMAND ${CMAKE_COMMAND} -E echo "Build complete!")
  2. Provide toggle options:

    option(USE_MY_LIB "Use the provided library" ON)
  3. Set CMake policies:

    cmake_policy(SET CMP0074 NEW)

CMakeLists Analysis Practice

For the example project, see the 示例项目cmakelists+详细注释.txt file in the folder

Analysis of Example Project CMakeLists Overall Structure and Approach

  1. Basic configuration:

    • Set the minimum required CMake version.
    • Define module paths for searching custom CMake modules.
    • Check whether running under the ecbuild build system, and adjust subsequent configuration accordingly.
  2. Project definition:

    • Use the project() command to define a project named "roms", specifying the version number and the primary programming language as Fortran.
    • Under the ecbuild environment, enable Fortran and set related build options.
  3. ROMS-related CMake file inclusion:

    • Include a series of custom CMake functions, compiler flags, and configuration files.
  4. Dependency management:

    • Detect the NetCDF library location based on environment variables and link it.
    • If under the ecbuild environment, use ecbuild's built-in MPI detection functionality and add MPI Fortran library linking.
    • Based on SCORPIO's enabled status, add the corresponding header file directories.
  5. ROMS source code organization:

    • Add each subdirectory as a sub-build directory. These subdirectories contain the core components, drivers, and various functional modules of ROMS.
    • Set common include and link directories.
    • Collect all source files into a list, and based on options like ADJOINT, REPRESENTER, TANGENT, decide whether to include source files from corresponding subdirectories.
  6. ROMS-specific rules:

    • Set special preprocessor definitions and compile flags for specific source files.
    • For files that exceed standard Fortran line length limits, set free-format compilation flags.
  7. Clean up dependencies:

    • Use a Perl script to clean up CMake-generated dependency information files to ensure correct dependencies.
    • Create a custom target "fix" and make the Objects target depend on it.
  8. Object library creation:

    • Use the preprocess_fortran function to process source code, generate .f90 files, and compile them as an OBJECT library target "Objects".
    • Based on the LIBSHARED and LIBSTATIC options, generate shared libraries (ROMS_shared) and static libraries (ROMS_static) respectively, and install them.
  9. Build executable:

    • When ROMS_EXECUTABLE is enabled, build the executable from the master.F file in the Master subdirectory.
    • Set appropriate link directories and libraries, including NetCDF, ARPACK (if needed), and SCORPIO (if parallel I/O is enabled).
    • Link the Objects target, ROMS library, and other dependencies into the final executable, and install to the appropriate location.

Example Project CMakeLists + Detailed Comments

See the 示例项目cmakelists+详细注释.txt file in the folder