Use CppADCodeGen with CMake FetchContent or ExternalProject

I am not good with CMake, and I cannot find good explanations about how to use its FetchContent functionality. Indeed, most repositories seem to require different treatment, and the rules of such treatment defy my comprehension.

That said, here is my problem. I would like to use CppADCodeGen in my project using CMake FetchContent. Here is my code:

include(FetchContent)

message(STATUS "Fetching eigen...")
FetchContent_Declare(
  eigen
  GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
  GIT_TAG 3.4.0
  GIT_SHALLOW TRUE
  GIT_PROGRESS TRUE)
option(EIGEN_BUILD_DOC OFF)
option(BUILD_TESTING OFF)
option(EIGEN_LEAVE_TEST_IN_ALL_TARGET OFF)
option(EIGEN_BUILD_PKGCONFIG OFF)
FetchContent_MakeAvailable(eigen)

message(STATUS "Fetching cppad...")
FetchContent_Declare(
  cppad
  GIT_REPOSITORY https://github.com/coin-or/CppAD.git
  GIT_TAG 20210000.8
  GIT_SHALLOW TRUE
  GIT_PROGRESS TRUE)
FetchContent_MakeAvailable(cppad)

message(STATUS "Fetching cppadcodgen...")
FetchContent_Declare(
  cppadcodgen
  GIT_REPOSITORY https://github.com/joaoleal/CppADCodeGen.git
  GIT_TAG v2.4.3
  GIT_SHALLOW TRUE
  GIT_PROGRESS TRUE)
FetchContent_MakeAvailable(cppadcodgen)

Here is the output and the errors I get:

[cmake] Not searching for unused variables given on the command line.
[cmake] -- The C compiler identification is GNU 11.1.0
[cmake] -- The CXX compiler identification is GNU 11.1.0
[cmake] -- Detecting C compiler ABI info
[cmake] -- Detecting C compiler ABI info - done
[cmake] -- Check for working C compiler: /bin/x86_64-linux-gnu-gcc-11 - skipped
[cmake] -- Detecting C compile features
[cmake] -- Detecting C compile features - done
[cmake] -- Detecting CXX compiler ABI info
[cmake] -- Detecting CXX compiler ABI info - done
[cmake] -- Check for working CXX compiler: /bin/x86_64-linux-gnu-g++-11 - skipped
[cmake] -- Detecting CXX compile features
[cmake] -- Detecting CXX compile features - done
[cmake] -- Fetching eigen...
[cmake] -- Performing Test EIGEN_COMPILER_SUPPORT_CPP11
[cmake] -- Performing Test EIGEN_COMPILER_SUPPORT_CPP11 - Success
[cmake] -- Performing Test COMPILER_SUPPORT_std=cpp03
[cmake] -- Performing Test COMPILER_SUPPORT_std=cpp03 - Success
[cmake] -- Performing Test standard_math_library_linked_to_automatically
[cmake] -- Performing Test standard_math_library_linked_to_automatically - Success
[cmake] -- Standard libraries to link to explicitly: none
[cmake] -- Performing Test COMPILER_SUPPORT_WERROR
[cmake] -- Performing Test COMPILER_SUPPORT_WERROR - Success
[cmake] -- Performing Test COMPILER_SUPPORT_pedantic
[cmake] -- Performing Test COMPILER_SUPPORT_pedantic - Success
[cmake] -- Performing Test COMPILER_SUPPORT_Wall
[cmake] -- Performing Test COMPILER_SUPPORT_Wall - Success
[cmake] -- Performing Test COMPILER_SUPPORT_Wextra
[cmake] -- Performing Test COMPILER_SUPPORT_Wextra - Success
[cmake] -- Performing Test COMPILER_SUPPORT_Wundef
[cmake] -- Performing Test COMPILER_SUPPORT_Wundef - Success
[cmake] -- Performing Test COMPILER_SUPPORT_Wcastalign
[cmake] -- Performing Test COMPILER_SUPPORT_Wcastalign - Success
[cmake] -- Performing Test COMPILER_SUPPORT_Wcharsubscripts
[cmake] -- Performing Test COMPILER_SUPPORT_Wcharsubscripts - Success
[cmake] -- Performing Test COMPILER_SUPPORT_Wnonvirtualdtor
[cmake] -- Performing Test COMPILER_SUPPORT_Wnonvirtualdtor - Success
[cmake] -- Performing Test COMPILER_SUPPORT_Wunusedlocaltypedefs
[cmake] -- Performing Test COMPILER_SUPPORT_Wunusedlocaltypedefs - Success
[cmake] -- Performing Test COMPILER_SUPPORT_Wpointerarith
[cmake] -- Performing Test COMPILER_SUPPORT_Wpointerarith - Success
[cmake] -- Performing Test COMPILER_SUPPORT_Wwritestrings
[cmake] -- Performing Test COMPILER_SUPPORT_Wwritestrings - Success
[cmake] -- Performing Test COMPILER_SUPPORT_Wformatsecurity
[cmake] -- Performing Test COMPILER_SUPPORT_Wformatsecurity - Success
[cmake] -- Performing Test COMPILER_SUPPORT_Wshorten64to32
[cmake] -- Performing Test COMPILER_SUPPORT_Wshorten64to32 - Failed
[cmake] -- Performing Test COMPILER_SUPPORT_Wlogicalop
[cmake] -- Performing Test COMPILER_SUPPORT_Wlogicalop - Success
[cmake] -- Performing Test COMPILER_SUPPORT_Wenumconversion
[cmake] -- Performing Test COMPILER_SUPPORT_Wenumconversion - Success
[cmake] -- Performing Test COMPILER_SUPPORT_Wcpp11extensions
[cmake] -- Performing Test COMPILER_SUPPORT_Wcpp11extensions - Failed
[cmake] -- Performing Test COMPILER_SUPPORT_Wdoublepromotion
[cmake] -- Performing Test COMPILER_SUPPORT_Wdoublepromotion - Success
[cmake] -- Performing Test COMPILER_SUPPORT_Wshadow
[cmake] -- Performing Test COMPILER_SUPPORT_Wshadow - Success
[cmake] -- Performing Test COMPILER_SUPPORT_Wnopsabi
[cmake] -- Performing Test COMPILER_SUPPORT_Wnopsabi - Success
[cmake] -- Performing Test COMPILER_SUPPORT_Wnovariadicmacros
[cmake] -- Performing Test COMPILER_SUPPORT_Wnovariadicmacros - Success
[cmake] -- Performing Test COMPILER_SUPPORT_Wnolonglong
[cmake] -- Performing Test COMPILER_SUPPORT_Wnolonglong - Success
[cmake] -- Performing Test COMPILER_SUPPORT_fnochecknew
[cmake] -- Performing Test COMPILER_SUPPORT_fnochecknew - Success
[cmake] -- Performing Test COMPILER_SUPPORT_fnocommon
[cmake] -- Performing Test COMPILER_SUPPORT_fnocommon - Success
[cmake] -- Performing Test COMPILER_SUPPORT_fstrictaliasing
[cmake] -- Performing Test COMPILER_SUPPORT_fstrictaliasing - Success
[cmake] -- Performing Test COMPILER_SUPPORT_wd981
[cmake] -- Performing Test COMPILER_SUPPORT_wd981 - Failed
[cmake] -- Performing Test COMPILER_SUPPORT_wd2304
[cmake] -- Performing Test COMPILER_SUPPORT_wd2304 - Failed
[cmake] -- Performing Test COMPILER_SUPPORT_STRICTANSI
[cmake] -- Performing Test COMPILER_SUPPORT_STRICTANSI - Failed
[cmake] -- Performing Test COMPILER_SUPPORT_Qunusedarguments
[cmake] -- Performing Test COMPILER_SUPPORT_Qunusedarguments - Failed
[cmake] -- Performing Test COMPILER_SUPPORT_ansi
[cmake] -- Performing Test COMPILER_SUPPORT_ansi - Success
[cmake] -- Performing Test COMPILER_SUPPORT_OPENMP
[cmake] -- Performing Test COMPILER_SUPPORT_OPENMP - Success
[cmake] -- Looking for a Fortran compiler
[cmake] -- Looking for a Fortran compiler - /usr/bin/f95
[cmake] -- The Fortran compiler identification is GNU 9.3.0
[cmake] -- Detecting Fortran compiler ABI info
[cmake] -- Detecting Fortran compiler ABI info - done
[cmake] -- Check for working Fortran compiler: /usr/bin/f95 - skipped
[cmake] -- Checking whether /usr/bin/f95 supports Fortran 90
[cmake] -- Checking whether /usr/bin/f95 supports Fortran 90 - yes
[cmake] -- Found unsuitable Qt version "5.12.8" from /usr/bin/qmake
[cmake] -- Qt4 not found, so disabling the mandelbrot and opengl demos
[cmake] -- Found CHOLMOD: /usr/include/suitesparse  
[cmake] -- Found UMFPACK: /usr/include/suitesparse  
[cmake] -- Found KLU: /usr/include/suitesparse  
[cmake] -- Performing Test SUPERLU_HAS_GLOBAL_MEM_USAGE_T
[cmake] -- Performing Test SUPERLU_HAS_GLOBAL_MEM_USAGE_T - Success
[cmake] -- Performing Test SUPERLU_HAS_CLEAN_ENUMS
[cmake] -- Performing Test SUPERLU_HAS_CLEAN_ENUMS - Success
[cmake] -- Performing Test SUPERLU_HAS_GLOBALLU_T
[cmake] -- Performing Test SUPERLU_HAS_GLOBALLU_T - Success
[cmake] -- Found SuperLU: /usr/include/superlu (found suitable version "5.0", minimum required is "4.0") 
[cmake] -- Checking for one of the modules 'hwloc'
[cmake] -- Performing Test HAVE_HWLOC_PARENT_MEMBER
[cmake] -- Performing Test HAVE_HWLOC_PARENT_MEMBER - Success
[cmake] -- Performing Test HAVE_HWLOC_CACHE_ATTR
[cmake] -- Performing Test HAVE_HWLOC_CACHE_ATTR - Success
[cmake] -- Performing Test HAVE_HWLOC_OBJ_PU
[cmake] -- Performing Test HAVE_HWLOC_OBJ_PU - Success
[cmake] -- Looking for hwloc_bitmap_free in hwloc
[cmake] -- Looking for hwloc_bitmap_free in hwloc - found
[cmake] -- A version of Pastix has been found but pastix_nompi.h does not exist in the include directory. Because Eigen tests require a version without MPI, we disable the Pastix backend.
[cmake] -- 
[cmake] -- Configured Eigen 3.4.0
[cmake] -- 
[cmake] -- Available targets (use: make TARGET):
[cmake] -- ---------+--------------------------------------------------------------
[cmake] -- Target   |   Description
[cmake] -- ---------+--------------------------------------------------------------
[cmake] -- install  | Install Eigen. Headers will be installed to:
[cmake] --          |     <CMAKE_INSTALL_PREFIX>/<INCLUDE_INSTALL_DIR>
[cmake] --          |   Using the following values:
[cmake] --          |     CMAKE_INSTALL_PREFIX: /usr/local
[cmake] --          |     INCLUDE_INSTALL_DIR:  include/eigen3
[cmake] --          |   Change the install location of Eigen headers using:
[cmake] --          |     cmake . -DCMAKE_INSTALL_PREFIX=yourprefix
[cmake] --          |   Or:
[cmake] --          |     cmake . -DINCLUDE_INSTALL_DIR=yourdir
[cmake] -- doc      | Generate the API documentation, requires Doxygen & LaTeX
[cmake] -- blas     | Build BLAS library (not the same thing as Eigen)
[cmake] -- uninstall| Remove files installed by the install target
[cmake] -- ---------+--------------------------------------------------------------
[cmake] -- 
[cmake] -- Fetching cppad...
[cmake] CMake Deprecation Warning at build/_deps/cppad-src/CMakeLists.txt:20 (CMAKE_MINIMUM_REQUIRED):
[cmake]   Compatibility with CMake < 2.8.12 will be removed from a future version of
[cmake]   CMake.
[cmake] 
[cmake]   Update the VERSION argument <min> value or use a ...<max> suffix to tell
[cmake]   CMake that the project does not need compatibility with older versions.
[cmake] 
[cmake] 
[cmake] CMake Warning (dev) at build/_deps/cppad-src/CMakeLists.txt:35 (PROJECT):
[cmake]   Policy CMP0048 is not set: project() command manages VERSION variables.
[cmake]   Run "cmake --help-policy CMP0048" for policy details.  Use the cmake_policy
[cmake]   command to set the policy and suppress this warning.
[cmake] 
[cmake]   The following variable(s) would be set to empty:
[cmake] 
[cmake]     PROJECT_VERSION
[cmake]     PROJECT_VERSION_MAJOR
[cmake]     PROJECT_VERSION_MINOR
[cmake]     PROJECT_VERSION_PATCH
[cmake] This warning is for project developers.  Use -Wno-dev to suppress it.
[cmake] 
[cmake] -- Performing Test cppad_cplusplus_201100_ok
[cmake] -- Performing Test cppad_cplusplus_201100_ok - Success
[cmake] -- cppad_cplusplus_201100_ok = 1
[cmake] -- cmake_install_datadir = share
[cmake] -- cmake_install_docdir = NOTFOUND
[cmake] -- cmake_install_includedirs = include
[cmake] -- cmake_install_libdirs = lib
[cmake] -- cppad_prefix = /usr/local
[cmake] -- cppad_postfix = NOTFOUND
[cmake] -- cppad_cxx_flags = 
[cmake] -- cppad_profile_flag = NOTFOUND
[cmake] -- cppad_testvector = cppad
[cmake] -- cppad_max_num_threads = 48
[cmake] -- cppad_tape_id_type = unsigned int
[cmake] -- cppad_tape_addr_type = unsigned int
[cmake] -- cppad_debug_which = debug_all
[cmake] -- include_eigen = false
[cmake] -- include_adolc = false
[cmake] -- include_ipopt = false
[cmake] -- include_cppadcg = false
[cmake] -- colpack_prefix = NOTFOUND
[cmake] -- sacado_prefix = NOTFOUND
[cmake] -- fadbad_prefix = NOTFOUND
[cmake] -- CMAKE_CXX_FLAGS_DEBUG = -g
[cmake] -- CMAKE_CXX_FLAGS_RELEASE = -O3 -DNDEBUG
[cmake] -- Found OpenMP_C: -fopenmp (found version "4.5") 
[cmake] -- Found OpenMP_CXX: -fopenmp (found version "4.5") 
[cmake] -- Found OpenMP: TRUE (found version "4.5")  
[cmake] -- Found Boost: /usr/lib/x86_64-linux-gnu/cmake/Boost-1.71.0/BoostConfig.cmake (found version "1.71.0") found components: thread 
[cmake] -- boost_prefix = /usr
[cmake] -- Found /usr/include
[cmake] -- Found /usr/lib
[cmake] -- Performing Test compiler_has_conversion_warn
[cmake] -- Performing Test compiler_has_conversion_warn - Success
[cmake] -- compiler_has_conversion_warn = 1
[cmake] -- cppad_boostvector = 0
[cmake] -- cppad_cppadvector = 1
[cmake] -- cppad_eigenvector = 0
[cmake] -- cppad_stdvector = 0
[cmake] -- cppad_cplusplus_201100_ok = 1
[cmake] -- Performing Test cppad_has_gettimeofday
[cmake] -- Performing Test cppad_has_gettimeofday - Success
[cmake] -- cppad_has_gettimeofday = 1
[cmake] -- Performing Test cppad_tape_id_type_is_unsigned
[cmake] -- Performing Test cppad_tape_id_type_is_unsigned - Success
[cmake] -- cppad_tape_id_type_is_unsigned = 1
[cmake] -- Performing Test cppad_tape_addr_type_is_unsigned
[cmake] -- Performing Test cppad_tape_addr_type_is_unsigned - Success
[cmake] -- cppad_tape_addr_type_is_unsigned = 1
[cmake] -- Performing Test cppad_max_num_threads_is_integer_ge_4
[cmake] -- Performing Test cppad_max_num_threads_is_integer_ge_4 - Success
[cmake] -- Performing Test cppad_has_mkstemp
[cmake] -- Performing Test cppad_has_mkstemp - Success
[cmake] -- cppad_has_mkstemp = 1
[cmake] -- Performing Test cppad_has_tmpnam_s
[cmake] -- Performing Test cppad_has_tmpnam_s - Failed
[cmake] -- cppad_has_tmpnam_s = 0
[cmake] -- soversion = 712.8
[cmake] -- Not Windows system so building shared cppad_lib
[cmake] -- make check_example_abs_normal: available
[cmake] -- make check_example_atomic_two: available
[cmake] -- make check_example_atomic_three: available
[cmake] -- make check_example_chkpoint_two: available
[cmake] -- make check_example_graph: available
[cmake] -- make check_example_general: available
[cmake] -- make check_example_get_started: available
[cmake] -- make check_example_json: available
[cmake] -- make check_example_multi_thread_openmp: available
[cmake] -- pthread library path = /usr/lib/x86_64-linux-gnu/libpthread.so
[cmake] -- Looking for pthread_barrier_wait in pthread
[cmake] -- Looking for pthread_barrier_wait in pthread - found
[cmake] -- make check_example_multi_thread_pthread: available
[cmake] -- Performing Test boost_multi_thread_ok
[cmake] -- Performing Test boost_multi_thread_ok - Success
[cmake] -- make check_example_multi_thread_bthread: available
[cmake] -- make check_example_multi_thread: available
[cmake] -- make check_example_optimize: available
[cmake] -- make check_example_print_for: available
[cmake] -- make check_example_sparse: available
[cmake] -- make check_example_utility: available
[cmake] -- make check_example: available
[cmake] -- make check_introduction: available
[cmake] -- make check_test_more_deprecated_atomic_two: available
[cmake] -- make check_test_more_deprecated_chkpoint_one: available
[cmake] -- make check_test_more_deprecated: available
[cmake] -- make check_det_by_minor_c: available
[cmake] -- make check_det_by_minor_cpp: available
[cmake] -- make check_test_more_compare_c: available
[cmake] -- make check_test_more_debug_rel: available
[cmake] -- make check_test_more_cppad_for_tmb: available
[cmake] -- make check_test_more_general: available
[cmake] -- make check_test_more: available
[cmake] -- make check_speed_cppad: available
[cmake] -- make check_speed_double: available
[cmake] -- make check_speed_example: available
[cmake] -- make check_speed_program: available
[cmake] -- make check_speed_xpackage: available
[cmake] -- make check_speed: available
[cmake] -- make check: avialable
[cmake] CMake Error at build/_deps/cppad-src/CMakeLists.txt:452 (ADD_CUSTOM_TARGET):
[cmake]   ADD_CUSTOM_TARGET cannot create target "uninstall" because another target
[cmake]   with the same name already exists.  The existing target is a custom target
[cmake]   created in source directory
[cmake]   "project/build/_deps/eigen-src".  See documentation for
[cmake]   policy CMP0002 for more details.
[cmake] 
[cmake] 
[cmake] -- Fetching cppadcodgen...
[cmake] CMake Deprecation Warning at build/_deps/cppadcodgen-src/CMakeLists.txt:18 (CMAKE_MINIMUM_REQUIRED):
[cmake]   Compatibility with CMake < 2.8.12 will be removed from a future version of
[cmake]   CMake.
[cmake] 
[cmake]   Update the VERSION argument <min> value or use a ...<max> suffix to tell
[cmake]   CMake that the project does not need compatibility with older versions.
[cmake] 
[cmake] 
[cmake] CMake Warning (dev) at build/_deps/cppadcodgen-src/CMakeLists.txt:20 (PROJECT):
[cmake]   Policy CMP0048 is not set: project() command manages VERSION variables.
[cmake]   Run "cmake --help-policy CMP0048" for policy details.  Use the cmake_policy
[cmake]   command to set the policy and suppress this warning.
[cmake] 
[cmake]   The following variable(s) would be set to empty:
[cmake] 
[cmake]     PROJECT_VERSION
[cmake]     PROJECT_VERSION_MAJOR
[cmake]     PROJECT_VERSION_MINOR
[cmake]     PROJECT_VERSION_PATCH
[cmake] This warning is for project developers.  Use -Wno-dev to suppress it.
[cmake] 
[cmake] -- Found PkgConfig: /usr/bin/pkg-config (found version "0.29.1") 
[cmake] -- Found CppAD: project/build/_deps/cppad-src/include (Required is at least version "20200000.1") 
[cmake] CMake Error at build/_deps/cppadcodgen-src/cmake/FindCppAD.cmake:86 (FILE):
[cmake]   FILE STRINGS file
[cmake]   "project/build/_deps/cppadcodgen-src/CPPAD_INCLUDE_DIR-NOTFOUND/cppad/configure.hpp"
[cmake]   cannot be read.
[cmake] Call Stack (most recent call first):
[cmake]   build/_deps/cppadcodgen-src/CMakeLists.txt:42 (FIND_PACKAGE)
[cmake] 
[cmake] 
[cmake] CMake Error at build/_deps/cppadcodgen-src/cmake/FindCppAD.cmake:99 (MESSAGE):
[cmake]   Found CppAD version '' but at least version '20200000.1' is required
[cmake] Call Stack (most recent call first):
[cmake]   build/_deps/cppadcodgen-src/CMakeLists.txt:42 (FIND_PACKAGE)
[cmake] 
[cmake] 
[cmake] -- Configuring incomplete, errors occurred!

Is anyone able to achieve the above goal on their machine? If yes, would they kindly share how they did it? In the above error messages there are several errors, and I have already tried some solutions, but none of them worked — I will not list them because they are no use mentioning!

Answer

Problems Overview

As seen in the output you’ve provided, there are 2 problems:

  1. There is a target name conflict between probably CppAD and eigen. They both have the uninstall target. It can be seen here:
[cmake] CMake Error at build/_deps/cppad-src/CMakeLists.txt:452 (ADD_CUSTOM_TARGET):
[cmake]   ADD_CUSTOM_TARGET cannot create target "uninstall" because another target
[cmake]   with the same name already exists.  The existing target is a custom target
[cmake]   created in source directory
[cmake]   "project/build/_deps/eigen-src".  
  1. cppadcodgen requires an already installed version of CppAD. It can be seen in the error message which tells us that an invalid version was found:
[cmake] CMake Error at build/_deps/cppadcodgen-src/cmake/FindCppAD.cmake:99 (MESSAGE):
[cmake]   Found CppAD version '' but at least version '20200000.1' is required
[cmake] Call Stack (most recent call first):
[cmake]   build/_deps/cppadcodgen-src/CMakeLists.txt:42 (FIND_PACKAGE)

Solution

Unfortunately, currently we can’t use FetchContent to deal with those problems, because:

  1. There isn’t any mechanism that would allow us distinguishing conflicting targets (for example a “namespace” prefix could be a solution and then there could be 2 targets eigen:uninstall and cppad:uninstall).
  2. We can’t build the dependent target during configuration step.

Instead we’ll use the ExternalProject module. Here’s a possible solution:

include(FetchContent)
message(STATUS "Fetching eigen...")
FetchContent_Declare(
eigen
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
GIT_TAG 3.4.0
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE)
option(EIGEN_BUILD_DOC OFF)
option(BUILD_TESTING OFF)
option(EIGEN_LEAVE_TEST_IN_ALL_TARGET OFF)
option(EIGEN_BUILD_PKGCONFIG OFF)
FetchContent_MakeAvailable(eigen)
include(ExternalProject)
ExternalProject_Add(cppad
GIT_REPOSITORY    https://github.com/coin-or/CppAD.git
GIT_TAG           20210000.8
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
CMAKE_CACHE_ARGS -Dcppad_prefix:STRING=${CMAKE_BINARY_DIR}/cppad
)
ExternalProject_Add(cppadcodgen
GIT_REPOSITORY    https://github.com/joaoleal/CppADCodeGen.git
GIT_TAG           v2.4.3
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
CMAKE_CACHE_ARGS -DCPPAD_HOME:STRING=${CMAKE_BINARY_DIR}/cppad/include -DGOOGLETEST_GIT:BOOL=ON -DCMAKE_INSTALL_PREFIX:STRING=${CMAKE_BINARY_DIR}/cppadcodegen
DEPENDS cppad
)

I used the following commands to test it:

mkdir cmake-build
cd !$
cmake ..
cmake --build .

Explanation

Most of the parameters are exactly the same as used in FetchContent. The only differences are:

  • We install cppad to the build directory instead of a system wide directory, so the whole process will be self-contained. It is achieved by setting the cppad_prefix cache variable to ${CMAKE_BINARY_DIR}/cppad in the CMAKE_CACHE_ARGS option.

  • We make sure that cppadcodgen succeeds finding CppAD by setting the CPPAD_HOME cache variable to the include directory (which is ${CMAKE_BINARY_DIR}/cppad/include) and also setting this target to run after CppAD is installed using the DEPENDS option.

  • cppadcodgen sets the test targets as well and it will fail if it cannot find Google Test. Fortunately they provide an option to download Google Test using the GOOGLETEST_GIT option which we set to ON.

  • Finally, we set to install cppadcodgen inside the build directory, by setting CMAKE_INSTALL_PREFIX variable.

Note that I haven’t touched eigen because I don’t know what would be the exact usage and it didn’t fail the build. It could be easily modified to be ExternalProject as written above if needed.

More information on ExternalProject can be found in Cmake’s documentation.

Consuming the Dependencies

In order to consume the dependencies we have to perform some modifications. Let’s take as example the main file from CppADCodeGen wiki page and try to compile it with our solution:

main.cpp

#include <iosfwd>
#include <vector>
#include <cppad/cg.hpp>
using namespace CppAD;
using namespace CppAD::cg;
int main() {
// use a special object for source code generation
typedef CG<double> CGD;
typedef AD<CGD> ADCG;
/***************************************************************************
*                               the model
**************************************************************************/
// independent variable vector
CppAD::vector<ADCG> x(2);
x[0] = 2.;
x[1] = 3.;
Independent(x);
// dependent variable vector 
CppAD::vector<ADCG> y(1);
// the model
ADCG a = x[0] / 1. + x[1] * x[1];
y[0] = a / 2;
ADFun<CGD> fun(x, y); // the model tape
/***************************************************************************
*                        Generate the C source code
**************************************************************************/
/**
* start the special steps for source code generation for a Jacobian
*/
CodeHandler<double> handler;
CppAD::vector<CGD> indVars(2);
handler.makeVariables(indVars);
CppAD::vector<CGD> jac = fun.SparseJacobian(indVars);
LanguageC<double> langC("double");
LangCDefaultVariableNameGenerator<double> nameGen;
std::ostringstream code;
handler.generateCode(code, langC, jac, nameGen);
std::cout << code.str();
}

Note that ExternalProject_Add command runs on build time, and we would like to find our dependencies already during Cmake reload time. So unfortunately we’ll have to do a dirty trick to force ExternalProject_Add to run on Cmake reload time:

  1. We’ll move the ExternalProject_Add commands into a helper, CMakeLists.txt.in file.
  2. We’ll configure the CMakeLists.txt.in so the output paths of the libraries will remain inside our build directory.
  3. We’ll invoke execute_process (which run on cmake reload time) to execute the build process of the helper CMakeLists.txt.in
  4. We’ll find our libraries and consume them. Note that we can’t use the modern Cmake targets syntax, as the whole process is being run in a sub-process and we don’t have access to the created target. But even if we didn’t run it inside a sub process, ExternalProject targets’ type is utility which can’t be used in commands such as target_link_libraries. So we’ll use the custom paths we created for the dependencies and pass them to target_link_libraries and target_include_directories commands.

The end results are displayed below:

CMakeLists.txt.in

cmake_minimum_required(VERSION 2.8)
project(deps-download NONE)
include(ExternalProject)
ExternalProject_Add(cppad
GIT_REPOSITORY    https://github.com/coin-or/CppAD.git
GIT_TAG           20210000.8
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
CMAKE_CACHE_ARGS -Dcppad_prefix:STRING=${CMAKE_BINARY_DIR}/cppad
)
ExternalProject_Add(cppadcodgen
GIT_REPOSITORY    https://github.com/joaoleal/CppADCodeGen.git
GIT_TAG           v2.4.3
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
CMAKE_CACHE_ARGS -DCPPAD_HOME:STRING=${CMAKE_BINARY_DIR}/cppad/include -DGOOGLETEST_GIT:BOOL=ON -DCMAKE_INSTALL_PREFIX:STRING=${CMAKE_BINARY_DIR}/cppadcodegen
DEPENDS cppad
)

CMakeLists.txt

include(FetchContent)
message(STATUS "Fetching eigen...")
FetchContent_Declare(
eigen
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
GIT_TAG 3.4.0
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE)
option(EIGEN_BUILD_DOC OFF)
option(BUILD_TESTING OFF)
option(EIGEN_LEAVE_TEST_IN_ALL_TARGET OFF)
option(EIGEN_BUILD_PKGCONFIG OFF)
FetchContent_MakeAvailable(eigen)
# Configure the helper CMakeLists with the dependencies
configure_file(CMakeLists.txt.in ${CMAKE_BINARY_DIR}/deps-build/CMakeLists.txt)
# Build it
execute_process(COMMAND ${CMAKE_COMMAND} .
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/deps-build)
execute_process(COMMAND ${CMAKE_COMMAND} --build .
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/deps-build)       
# Find cppad_lib's path
find_library(cppad_lib cppad_lib HINTS ${CMAKE_BINARY_DIR}/cppad/lib REQUIRED)
# Create our target
add_executable(main
main.cpp)
target_link_libraries(main ${cppad_lib})
target_include_directories(main PUBLIC 
${CMAKE_BINARY_DIR}/cppad/include
${CMAKE_BINARY_DIR}/cppadcodegen/include)

As before, I used the following commands to test it:

mkdir cmake-build
cd !$
cmake ..
cmake --build .
./main

The solution is based on this post.