OK, I'm not sure how I can explain the problem exactly, but basically, in my Project X's CMakeLists.txt, I'm building external library dependencies which are separate projects in itself (say, Project A, Project B, Project C, Project D) - then I build using catkin_make.
I use CMake's ExternalProject_Add() for each projects A, B, C, D; followed by a declaration of add_dependencies() to make projects A, B, C, D build first before the main Project X.
Now, I particularly have problems with Project D, having the need to set multiple paths to CMAKE_PREFIX_PATH (eg, CMAKE_PREFIX_PATH=/addrA;/addrB;addrC).
- Error #1: If I use
ExternalProject_Add(), it recognizes the semicolon path separator ';' as "end of the command", and thus, it cuts the cmake command through the first path (onlyCMAKE_PREFIX_PATH=/addrAis recognized). I tried a lot of stuff already - usage ofLIST_SEPARATOR, settingCMAKE_PREFIX_PATHbefore that usingset(), or even using$<SEMICOLON>- nothing worked, same problem. - Error #2: What I did next - is instead of using
ExternalProject_Add()to do both download and build of Project D - I usedExternalProject_Add()to just do download (eg, BUILD_COMMAND will be just set to ""), and then do the build separately viaadd_custom_target(). Now the cmake command from there also didn't work - because for some reason/limitation I don't know about, it didn't properly map theCMAKE_INSTALL_*flags, and even though theCMAKE_PREFIX_PATHwas correct, it didn't really recognize the bin/lib/include directories that should've been automatically recognized inside the paths insideCMAKE_PREFIX_PATH. When I try to replace the correct values of these flags in the resultingCMakeCache.txtand rebuild viacatkin_make, it works fine. But if I don't do that, like if I rely only on the initialCMakeCache.txtit generated, it won't work.
Please do take note, that when I try to run cmake of Project D in the terminal, I don't encounter any issues at all.
As a workaround, I did an execute_process() that creates a bash script containing the cmake and make command for Project D, and then I just call that bash script either at the ExternalProject_Add()'s BUILD_COMMAND or the add_custom_target()'s COMMAND. Either of these works - which just shows, that the cmake executed within a COMMAND of either ExternalProject_Add() or add_custom_target() is somehow different than executing cmake in a terminal or through a bash script.
I've searched around everything here but I couldn't find anyone having the same problem as me, thus my workaround for now.
Here is the rough code that works (the one with my workaround):
### Project A
include(ExternalProject)
ExternalProject_Add(ProjectA
PREFIX /tmp/ProjectA
GIT_REPOSITORY https://github.com/ProjectA
GIT_TAG v0.0.1
CONFIGURE_COMMAND ""
BUILD_COMMAND cmake -DWITH_GFLAGS=off -DCMAKE_INSTALL_PREFIX=/opt/ProjectA <SOURCE_DIR>
COMMAND sudo cmake --build . --target install
INSTALL_COMMAND ""
TEST_COMMAND ""
)
### Project B
include(ExternalProject)
ExternalProject_Add(ProjectB
PREFIX /tmp/ProjectB
GIT_REPOSITORY https://github.com/ProjectB
GIT_TAG v0.0.1
CONFIGURE_COMMAND ""
BUILD_COMMAND cmake -DLWS_STATIC_PIC=ON -DCMAKE_INSTALL_PREFIX=/opt/ProjectB <SOURCE_DIR>
COMMAND sudo cmake --build . --target install
INSTALL_COMMAND ""
TEST_COMMAND ""
)
### Project C
include(ExternalProject)
ExternalProject_Add(ProjectC
PREFIX /tmp/ProjectC
GIT_REPOSITORY https://github.com/ProjectC
GIT_TAG v0.0.1
CONFIGURE_COMMAND ""
BUILD_COMMAND cmake -Dprotobuf_BUILD_TESTS=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_INSTALL_PREFIX=/opt/ProjectC <SOURCE_DIR>
COMMAND sudo cmake --build . --target install
INSTALL_COMMAND ""
TEST_COMMAND ""
)
### Project D
execute_process (
COMMAND bash -c "echo '#!/bin/bash\ncmake -DCMAKE_PREFIX_PATH=\"/opt/ProjectA;/opt/ProjectB;/opt/ProjectC\" /tmp/ProjectD/src/ProjectD\nmake' > /tmp/projectD_build.sh"
COMMAND bash -c "chmod 777 /tmp/projectD_build.sh"
)
include(ExternalProject)
ExternalProject_Add(ProjectD
PREFIX /tmp/ProjectD
GIT_REPOSITORY https://github.com/ProjectD
GIT_TAG master
CONFIGURE_COMMAND ""
BUILD_COMMAND /tmp/projectD_build.sh
INSTALL_COMMAND ""
TEST_COMMAND ""
)
### Set dependencies to force build Project A, B, C, D before Project X is build
add_library (ProjectX SHARED IMPORTED)
set_target_properties (ProjectX PROPERTIES IMPORTED_LOCATION ${ProjectX_LIBS})
add_dependencies(ProjectX ProjectA)
add_dependencies(ProjectX ProjectB)
add_dependencies(ProjectX ProjectC)
add_dependencies(ProjectX ProjectD)
### Project X build commands here (snipped)
These are the cmake + make commands inside /tmp/ProjecD_build.txt:
cmake -DCMAKE_PREFIX_PATH="/opt/ProjectA;/opt/ProjectB;/opt/ProjectC" /tmp/ProjectD/src/ProjectD
make
And if I replace the whole "### Project D" section with this, I encounter the Error #1 that error I've discussed above (problem with multiple path CMAKE_PREFIX_PATH):
### Project D
include(ExternalProject)
ExternalProject_Add(ProjectD
PREFIX /tmp/ProjectD
GIT_REPOSITORY https://github.com/ProjectD
GIT_TAG master
CONFIGURE_COMMAND ""
BUILD_COMMAND cmake -DCMAKE_PREFIX_PATH="/opt/ProjectA;/opt/ProjectB;/opt/ProjectC" /tmp/ProjectD/src/ProjectD
INSTALL_COMMAND make
TEST_COMMAND ""
)
... or add a post-ProjectD like this, it still doesn't work and causes Error #2 I discussed above (CMAKE flags not properly set as seen in CMakeCache.txt). Note that with this solution, I the Project D ExternalProject_Add()'s BUILD_COMMAND will be just set to "" since the build will be done by the added add_custom_target() instead:
ExternalProject_Get_Property(ProjectD install_dir)
ExternalProject_Get_Property(ProjectD source_dir)
ExternalProject_Get_Property(ProjectD binary_dir)
message(STATUS "install_dir is: " ${install_dir})
message(STATUS "source_dir is: " ${source_dir})
message(STATUS "binary_dir is: " ${binary_dir})
add_custom_target(ProjectD_part2 ALL
cmake -DCMAKE_PREFIX_PATH="/opt/ProjectA;/opt/ProjectB;/opt/ProjectC" ${source_dir}
COMMAND make
WORKING_DIRECTORY ${binary_dir}
)
add_dependencies(ProjectD_part2 ProjectA)
add_dependencies(ProjectD_part2 ProjectB)
add_dependencies(ProjectD_part2 ProjectC)
add_dependencies(ProjectD_part2 ProjectD)
The workaround works. But is there a better way of doing this? Thank you.