Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CMake based build system for CABLE offline #200

Merged
merged 4 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Fortran
*.mod
*.o
*.o
build
bin
171 changes: 171 additions & 0 deletions CMakeLists.txt
Copy link
Collaborator Author

@SeanBryan51 SeanBryan51 Jan 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO after this pull request:

  • We should list the source files in alphabetical order (the current order is so that we preserve binary equivalence with the Makefile build).
  • Use find_package(MPI REQUIRED) instead of specifying mpif90 when invoking cmake for MPI case.
  • Add simultaneous compilation of MPI and serial executables by creating a separate target for each executable.
  • Remove MPI option from build script as the same can be achieved by specifying --target cable-mpi when invoking CMake.

Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# Set minimum CMake version to latest version on Gadi:
cmake_minimum_required(VERSION 3.24.2)

project(
CABLE
LANGUAGES Fortran
)

option(CABLE_MPI "Build the MPI executable" OFF)

# third party libs
find_package(PkgConfig REQUIRED)
pkg_check_modules(NETCDF REQUIRED IMPORTED_TARGET "netcdf-fortran")

set(CABLE_INTEL_Fortran_FLAGS -fp-model precise)
set(CABLE_INTEL_Fortran_FLAGS_DEBUG -O0 -g -traceback -fpe0)
set(CABLE_INTEL_Fortran_FLAGS_RELEASE -O2)

# CMake $<...> syntax is explained here:
# https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html#introduction
set(CABLE_EXE_NAME "$<IF:$<BOOL:${CABLE_MPI}>,cable-mpi,cable>")
SeanBryan51 marked this conversation as resolved.
Show resolved Hide resolved

set(
CABLE_SRCS_SERIAL
src/offline/cable_driver.F90
)
set(
CABLE_SRCS_MPI
src/offline/cable_mpidrv.F90
src/offline/cable_mpicommon.F90
src/offline/cable_mpimaster.F90
src/offline/cable_mpiworker.F90
src/science/pop/pop_mpi.F90
)
set(
CABLE_SRCS_COMMON
src/science/casa-cnp/bgcdriver.F90
src/science/casa-cnp/biogeochem_casa.F90
src/offline/cable_abort.F90
src/science/misc/cable_air.F90
src/science/canopy/cable_canopy.F90
src/science/misc/cable_carbon.F90
src/offline/cable_checks.F90
src/science/misc/cable_climate.F90
src/util/cable_climate_type_mod.F90
src/util/cable_common.F90
src/offline/cable_cru_TRENDY.F90
src/offline/cable_define_types.F90
src/science/gw_hydro/cable_gw_hydro.F90
src/offline/cable_initialise.F90
src/offline/cable_input.F90
src/offline/cable_iovars.F90
src/offline/cable_LUC_EXPT.F90
src/params/cable_maths_constants_mod.F90
src/offline/cable_metutils.F90
src/offline/cable_namelist_input.F90
src/params/cable_other_constants_mod.F90
src/offline/cable_output.F90
src/offline/cable_parameters.F90
src/offline/cable_pft_params.F90
src/offline/cable_phenology.F90
src/params/cable_photo_constants_mod.F90
src/params/cable_phys_constants_mod.F90
src/offline/cable_plume_mip.F90
src/science/gw_hydro/cable_psm.F90
src/offline/cable_read.F90
src/science/roughness/cable_roughness.F90
src/util/cable_runtime_opts_mod.F90
src/offline/cable_site.F90
src/science/sli/cable_sli_main.F90
src/science/sli/cable_sli_numbers.F90
src/science/sli/cable_sli_roots.F90
src/science/sli/cable_sli_solve.F90
src/science/sli/cable_sli_utils.F90
src/offline/cable_soil_params.F90
src/offline/cable_weathergenerator.F90
src/offline/cable_write.F90
src/offline/casa_cable.F90
src/science/casa-cnp/casa_cnp.F90
src/science/casa-cnp/casa_dimension.F90
src/science/casa-cnp/casa_feedback.F90
src/science/casa-cnp/casa_inout.F90
src/offline/casa_ncdf.F90
src/offline/casa_offline_inout.F90
src/offline/CASAONLY_LUC.F90
src/science/casa-cnp/casa_param.F90
src/science/casa-cnp/casa_phenology.F90
src/science/casa-cnp/casa_readbiome.F90
src/science/casa-cnp/casa_rplant.F90
src/science/casa-cnp/casa_sumcflux.F90
src/science/casa-cnp/casa_variable.F90
src/science/albedo/cbl_albedo.F90
src/science/soilsnow/cbl_conductivity.F90
src/science/canopy/cbl_dryLeaf.F90
src/science/canopy/cbl_friction_vel.F90
src/science/canopy/cbl_fwsoil.F90
src/science/soilsnow/cbl_GW.F90
src/science/soilsnow/cbl_hyd_redistrib.F90
src/science/radiation/cbl_init_radiation.F90
src/science/canopy/cbl_latent_heat.F90
src/offline/cbl_model_driver_offline.F90
src/science/soilsnow/cbl_Oldconductivity.F90
src/science/canopy/cbl_photosynthesis.F90
src/science/canopy/cbl_pot_evap_snow.F90
src/science/canopy/cbl_qsat.F90
src/science/radiation/cbl_radiation.F90
src/science/soilsnow/cbl_remove_trans.F90
src/science/radiation/cbl_rhoch.F90
src/science/radiation/cbl_sinbet.F90
src/science/soilsnow/cbl_smoisturev.F90
src/science/soilsnow/cbl_snowAccum.F90
src/science/soilsnow/cbl_snow_aging.F90
src/science/albedo/cbl_snow_albedo.F90
src/science/soilsnow/cbl_snowCheck.F90
src/science/soilsnow/cbl_snowDensity.F90
src/science/soilsnow/cbl_snowl_adjust.F90
src/science/soilsnow/cbl_snowMelt.F90
src/science/albedo/cbl_soilColour_albedo.F90
src/science/soilsnow/cbl_soilfreeze.F90
src/science/soilsnow/cbl_soilsnow_data.F90
src/science/soilsnow/cbl_soilsnow_init_special.F90
src/science/soilsnow/cbl_soilsnow_main.F90
src/science/radiation/cbl_spitter.F90
src/science/soilsnow/cbl_stempv.F90
src/science/canopy/cbl_SurfaceWetness.F90
src/science/soilsnow/cbl_surfbv.F90
src/science/soilsnow/cbl_thermal.F90
src/science/soilsnow/cbl_trimb.F90
src/science/canopy/cbl_wetleaf.F90
src/science/canopy/cbl_within_canopy.F90
src/science/canopy/cbl_zetar.F90
src/params/grid_constants_cbl.F90
src/science/landuse/landuse3.F90
src/science/landuse/landuse_constant.F90
src/offline/landuse_inout.F90
src/util/masks_cbl.F90
src/science/pop/pop_constants.F90
src/science/pop/pop_def.F90
src/science/pop/pop_io.F90
src/science/pop/POPLUC.F90
src/science/pop/POP.F90
src/science/pop/pop_types.F90
src/science/roughness/roughnessHGT_effLAI_cbl.F90
src/offline/spincasacnp.F90
)
set(
CABLE_SRCS
"$<IF:$<BOOL:${CABLE_MPI}>,${CABLE_SRCS_MPI},${CABLE_SRCS_SERIAL}>"
${CABLE_SRCS_COMMON}
)

add_executable(cable ${CABLE_SRCS})

set_target_properties(cable PROPERTIES OUTPUT_NAME ${CABLE_EXE_NAME})

if(CMAKE_Fortran_COMPILER_ID MATCHES "Intel")
target_compile_options(
cable
PRIVATE
${CABLE_INTEL_Fortran_FLAGS}
"$<$<CONFIG:Release>:${CABLE_INTEL_Fortran_FLAGS_RELEASE}>"
"$<$<CONFIG:Debug>:${CABLE_INTEL_Fortran_FLAGS_DEBUG}>"
)
endif()

target_link_libraries(
cable
PkgConfig::NETCDF
)

install(TARGETS cable RUNTIME)
80 changes: 80 additions & 0 deletions build.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#!/bin/bash

nproc_default=4

script_name=$(basename "${0}")

show_help() {
cat << EOF
Usage: ./$script_name [OPTIONS]

Build script wrapper around CMake. Supplied arguments that do not match the
options below will be passed to CMake when generating the build system.

Options:
--clean Delete build directory before invoking CMake.
--mpi Compile MPI executable.
-h, --help Show this screen.

Enabling debug mode:

The release build is default. To enable debug mode, specify the CMake option
-DCMAKE_BUILD_TYPE=Debug when invoking $script_name.

Enabling verbose output from Makefile builds:

To enable more verbose output from Makefile builds, specify the CMake option
-DCMAKE_VERBOSE_MAKEFILE=ON when invoking $script_name.

Parallel compilation:

By default, the number of parallel jobs used in the compilation is
$nproc_default. This value can be overwritten by setting the environment
variable CMAKE_BUILD_PARALLEL_LEVEL.

SeanBryan51 marked this conversation as resolved.
Show resolved Hide resolved
EOF
}

cmake_args=(-DCMAKE_BUILD_TYPE=Release)

# Argument parsing adapted and stolen from http://mywiki.wooledge.org/BashFAQ/035#Complex_nonstandard_add-on_utilities
while [ $# -gt 0 ]; do
case $1 in
--clean)
rm -r build
;;
--mpi)
mpi=1
cmake_args+=(-DCABLE_MPI="ON")
cmake_args+=(-DCMAKE_Fortran_COMPILER="mpif90")
SeanBryan51 marked this conversation as resolved.
Show resolved Hide resolved
;;
-h|--help)
show_help
exit
;;
?*)
cmake_args+=("$1")
;;
esac
shift
done

if hostname -f | grep gadi.nci.org.au > /dev/null; then
. /etc/bashrc
module purge
module add cmake/3.24.2
module add intel-compiler/2019.5.281
module add netcdf/4.6.3
# This is required so that the netcdf-fortran library is discoverable by
# pkg-config:
prepend_path PKG_CONFIG_PATH "${NETCDF_BASE}/lib/Intel/pkgconfig"
if [[ -n $mpi ]]; then
module add intel-mpi/2019.5.281
fi
fi

export CMAKE_BUILD_PARALLEL_LEVEL="${CMAKE_BUILD_PARALLEL_LEVEL:=$nproc_default}"

cmake -S . -B build "${cmake_args[@]}" &&\
cmake --build build &&\
cmake --install build --prefix .
34 changes: 34 additions & 0 deletions documentation/docs/developer_guide/other_resources/build_system.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
For CABLE offline applications, the underlying build system is [CMake](https://cmake.org) based. CMake is widely used in the C, C++ and Fortran communities and there are many great resources out there on the web, in particular:

- [Mastering CMake](https://cmake.org/cmake/help/book/mastering-cmake/index.html)
- [Effective Modern CMake](https://gist.github.com/mbinna/c61dbb39bca0e4fb7d1f73b0d66a4fd1)
- [Creating a CMake project (with Fortran)](https://fortran-lang.org/learn/building_programs/build_tools/#creating-a-cmake-project)
- [An Introduction to Modern CMake](https://cliutils.gitlab.io/modern-cmake/)

## Adding source files

Source files can be added easily by adding to the list of source files in [CMakeLists.txt][CMakeLists.txt].

## Setting compiler flags

The recommended compiler flags for debug and release builds are set in the [CMakeLists.txt][CMakeLists.txt] file via [`target_compile_options`](https://cmake.org/cmake/help/latest/command/target_compile_options.html). These can be modified if required.

???+ warning
Compiler flags should be guarded by a check on the compiler ID as they are generally compiler specific. See [`CMAKE_<LANG>_COMPILER_ID`](https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER_ID.html) for a list of supported compiler IDs.

Alternatively, compiler flags can be specified when invoking `cmake` by specifying `-DCMAKE_Fortran_FLAGS=<flags>` when generating the project, however the per configuration default flags may take precedence (see [`CMAKE_<LANG>_FLAGS`](https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_FLAGS.html) for more details).

## Adding external dependencies (libraries)

When linking against external third-party libraries, we strongly recommend not to hard code library paths, include paths, link flags or compiler flags in the CMakeLists.txt file. This ensures that the build system is portable and insulated from changes to library versions and dependencies.

Instead, CMake should query the required flags for a given library from the library itself. To do this, we recommend using either CMake's [`find_package` mechanism](https://cmake.org/cmake/help/book/mastering-cmake/chapter/Finding%20Packages.html) or a tool such as [`pkg-config`](https://en.wikipedia.org/wiki/Pkg-config) (see the [FindPkgConfig](https://cmake.org/cmake/help/latest/module/FindPkgConfig.html) module for using `pkg-config` with CMake.). The dependencies should provide exported targets which can be used via [`target_link_libraries`](https://cmake.org/cmake/help/latest/command/target_link_libraries.html).
SeanBryan51 marked this conversation as resolved.
Show resolved Hide resolved

For an example, see how the netcdf-fortran dependency was added in [CMakeLists.txt][CMakeLists.txt].

If these approaches are not supported by the external library, please report this as a bug to its maintainers. If the library is an open-source project, consider sending a patch.

???+ warning
For most cases, CMake's `find_package` should be used in **Config** mode. `find_package` in **Module** mode should only be used if the library is part of the [CMake module distribution](https://cmake.org/cmake/help/latest/manual/cmake-modules.7.html#manual:cmake-modules(7)).

[CMakeLists.txt]: https://github.com/CABLE-LSM/CABLE/blob/main/CMakeLists.txt
Loading
Loading