Simplify build system usage
Goal
I would like to see a very simplified usage of dune both for new users and developers workflow.
git clone https://.../dune-foo.git
cd dune-foo
mkdir build && cd build
cmake ..
In my view, this should work in three (ordered) cases:
- Dependent modules are installed on the system: CMake finds such modules and consumes all their dependencies
- Source code of other dune dependent modules are present on the same folder as
dune-foo
source code: CMake adds them as sub-projects and fulfils dependencies - No dependencies are found: CMake fetches a default compatible source code from the internet.
Find or Fetch pattern in CMake
Now, how do we achieve such a simple usage? Assume you provide
-
DEP_NAME
: Name of dependency or package name -
DEP_FIND
: Arguments to find an installed dependency in your system (e.g. version or components) -
DEP_FETCH
: Arguments to fetch source code of dependency from the internet (git repository, commit, etc.) -
DEP_REQUIRED
: Boolean describing whether the dependency needs to be fulfilled or may be optional.
Then, the following find or fetch pattern can provide you with a module:
# Handle required and recommended dependencies
option(DUNE_DEFAULT_FETCHCONTENT "Default value of <package>_FETCHCONTENT for non-required packages declared with `dune_find_or_fetch` function" OFF)
cmake_dependent_option(
${DEP_NAME}_FETCHCONTENT
"If package ${DEP_NAME} is not found on the system, this option will ensure to provide the package by fetching content from the internet"
${DUNE_DEFAULT_FETCHCONTENT}
"NOT ${DEP_REQUIRED}"
ON
)
# 1. Try to find a dependency on the current system quietly (find is already guarded against repeated calls)
find_package(${DEP_NAME} QUIET ${DEP_FIND})
# If library is not installed, we build it from source
if(NOT ${DEP_NAME}_FOUND AND ${DEP_NAME}_FETCHCONTENT)
string(TOLOWER ${DEP_NAME} lcName)
string(TOUPPER ${DEP_NAME} upName)
FetchContent_GetProperties(${lcName})
# Guard double fetch to same package
if(NOT ${lcName}_POPULATED)
string(REPLACE "${CMAKE_PROJECT_NAME}" ${lcName} ${lcName}_SOURCE_DIR "${CMAKE_SOURCE_DIR}")
if(EXISTS "${${lcName}_SOURCE_DIR}")
# 2. If source project is besides a dir with package name, we take it as a fetch content source
set(FETCHCONTENT_SOURCE_DIR_${upName} "${${lcName}_SOURCE_DIR}" CACHE PATH "Source directory of ${lcName}")
endif()
# 3. Declare and fetch content from the internet
FetchContent_Declare(${lcName} ${DEP_FETCH})
FetchContent_Populate(${lcName})
# Add source code of dependency as sub-project
add_subdirectory("${${lcName}_SOURCE_DIR}" "${${lcName}_BINARY_DIR}")
endif()
endif()
(See it as a function here)
Requirements
This does not come for free though. This requires that your dependencies have
- A findable CMake config file that correctly configures upstream dependencies. This is already kind of true for dune.
- A CMake build a system that can be used as a subproject. This is one of the goals of !848.
Fortunately, these are within the objectives of the current refactor of the build system.
Proof of concept
dune-concepts
find or fetch dune-grid
while dune-grid
find or fetch dune-geometry
.
Discussion
dune-common
)
How about repeated dependencies (e.g. Both find
and fetch
parts are guarded against double inclusion of the dependency. Additionally, leaf modules only declare their immediate dependencies. This means that the first module that declares the dependency will make sure that it will be provided to downstream modules regardless of whether downstream modules need it.
How to handle optional dependencies?
I believe one can guard the fetch part with CMake options, but I think this needs some discussion. See #261 (comment 84680)
Do we need to write the whole pattern for every module?
Yes and no. If the first dependency provides a find_or_fetch
facility (e.g. a function in dune-common
), then the rest of the needed dependencies may use it in basically one line. But the problem remains: how to get the first dependency? I think writing it down once is okayish.
CMake should provide that!
Yes ..and they are on their way!. But they just started to discuss that and it might take quite some time until is usable to us (discussion -> proof of concept -> implementation -> release -> LTS distribution).