1

I want to integrate CasADi into a CMake-based C++ codebase as an ExternalProject. For this purpose, I would like to use pre-compiled libraries because building from source is not recommended. So far, I have only managed to write the following:

ExternalProject_Add(
  casadi-3.5.5
  URL https://github.com/casadi/casadi/releases/download/3.5.5/casadi-linux-py39-v3.5.5-64bit.tar.gz
  CONFIGURE_COMMAND ""
  BUILD_COMMAND ""
  INSTALL_COMMAND ""
  PREFIX ${CMAKE_BINARY_DIR}/external/casadi)

and I noticed that all the binaries are correctly downloaded in the specified folder. However, I do not know how to link my targets to CasADi, nor how to find the package.

fdev
  • 127
  • 12
  • https://cmake.org/cmake/help/v3.0/command/target_link_libraries.html – gftea Mar 18 '22 at 19:20
  • Could you provide a complete answer (like this one: https://stackoverflow.com/a/69715401/15826299)? I cannot understand the CMake documentation because it lacks thorough examples, and those I find on the internet are unrelated, or I am not capable of adapting them to my specific use case. I think that this problem should be easy to solve for an experienced CMake user! – fdev Mar 19 '22 at 08:11

1 Answers1

1

There is a natural problem with ExternalProject_Add:

ExternalProject_Add executes commands only on build.

Hence, download will not happen at the configure stage of your project which makes it difficult to use find_package, because the files cannot be found during your first configure run.

Take this CMakeLists.txt:

cmake_minimum_required(VERSION 3.21)
project(untitled)

set(CMAKE_CXX_STANDARD 17)

add_executable(untitled main.cpp)

include(ExternalProject)
ExternalProject_Add(
        casadi-3.5.5
        URL https://github.com/casadi/casadi/releases/download/3.5.5/casadi-linux-py39-v3.5.5-64bit.tar.gz
        CONFIGURE_COMMAND ""
        BUILD_COMMAND ""
        INSTALL_COMMAND ""
        PREFIX ${CMAKE_BINARY_DIR}/external/casadi)

find_package(casadi HINTS ${CMAKE_BINARY_DIR}/external/casadi/src/casadi-3.5.5/casadi)

target_link_libraries(untitled casadi)

In order to use it you have to do the following:

  1. Configure your project
mkdir build
cd build
cmake ..
  1. Build (download) casadi-3.5.5
cmake --build . --target casadi-3.5.5
  1. Reconfigure your project, because now find_package will find the needed files
cmake ..
  1. Build your targets
cmake --build .

If you want a one step build, there are ways to get around this problem


Here is an example for the second option, which might be better since FetchContent doesn't have the full functionality of ExternalProject.

  • main.cpp
#include <casadi/casadi.hpp>

int main()
{
    casadi_printf("This works!");
    return 0;
}
  • CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(untitled)

set(CMAKE_CXX_STANDARD 17)

# some default target
add_executable(untitled main.cpp)

# Configure and build external project
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/external)
execute_process(
        COMMAND ${CMAKE_COMMAND} ${CMAKE_SOURCE_DIR}/external
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/external
)
execute_process(
        COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}/external
)

# find and link externals
find_package(casadi REQUIRED HINTS ${CMAKE_BINARY_DIR}/external/external/casadi/src/casadi-3.5.5/casadi)
target_link_libraries(untitled casadi)
  • external/CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(external)

include(ExternalProject)
ExternalProject_Add(
        casadi-3.5.5
        URL https://github.com/casadi/casadi/releases/download/3.5.5/casadi-linux-py39-v3.5.5-64bit.tar.gz
        CONFIGURE_COMMAND ""
        BUILD_COMMAND ""
        INSTALL_COMMAND ""
        PREFIX ${CMAKE_BINARY_DIR}/external/casadi)

The point is to have another cmake project under external/CMakeLists.txt, which gets configured and build via execute_process calls from the main cmake project. Do note, that you can now have find_package(casadi REQUIRED ...) at configure stage, because the download will happen just before.

image357
  • 451
  • 5
  • 11
  • Thanks for this clear answer! However, I was looking for a one-step build, and I have tried `execute_process` to no avail: would you be so kind to provide a working example doing what you suggested? I thought that this could only be done by resorting to a separate `CMakeLists.txt.in` input file. – fdev Mar 20 '22 at 16:04
  • 1
    @fdev I've added an example for the second option via `execute_process`. – image357 Mar 20 '22 at 20:55
  • Thanks a lot for your great answer, now it works! As a side question, I used to think that one could not simply use `find_package` for libraries downloaded with `ExternalProject_Add`, and the only way to do it was by "rescanning" the project CMake file (e.g., https://stackoverflow.com/a/17473508/15826299). Is the approach you suggested general? And/or has it any caveats? – fdev Mar 22 '22 at 06:40
  • I have tried to compile the example https://github.com/casadi/casadi/blob/master/docs/examples/cplusplus/nlp_codegen.cpp, but I got errors like `undefined reference to 'casadi::nlpsol...'`. I cannot manage to fix them (I expected IPOPT to be automatically included in the `casadi` package), do you have a solution for this? – fdev Mar 22 '22 at 11:30
  • 1
    @fdev The only difference is that the dependent targets (created by ExternalProject_Add) are being build in a standalone project, hence you cannot use the same variables as your main project. You can still pass them via command line arguments in your `execute_process` calls, though. – image357 Mar 22 '22 at 16:53
  • @fdev concerning the linking error: does this help https://github.com/casadi/casadi/issues/2092? – image357 Mar 22 '22 at 16:58
  • Thank you very much again! I have already seen that GitHub issue, but it did not help as it is about an installation from source. In my case, the IPOPT library should be shipped with the CasADi binaries (see https://web.casadi.org/docs/#nonlinear-programming), therefore I expected the example I linked to work using your instructions. Do you understand what could be the problem here? – fdev Mar 22 '22 at 17:06
  • 1
    @fdev it seems that the binary distribution of casadi only has an incomplete cmake installation. `find_package` casadi will only find `libcasadi.so` but not any of the other targets/libraries. You should file a bug report with casadi. For the time being you can use the FindXXX.cmake scripts present in the source distribution of casadi https://github.com/casadi/casadi/tree/master/cmake, or add them manually via [find_library](https://cmake.org/cmake/help/latest/command/find_library.html) calls. – image357 Mar 22 '22 at 22:15
  • 1
    @fdev opened the issue: https://github.com/casadi/casadi/issues/2910 – image357 Mar 22 '22 at 22:30
  • Now everything looks clear. Thanks a lot for all the help you provided! – fdev Mar 23 '22 at 07:46