How to not make install step when building external project with cmake - cmake

I'm building dependency project with cmake ExternalProject_Add command:
include(ExternalProject)
...
set(COMMON_BASE_PROJECT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../CommonBase)
ExternalProject_Add(CommonBaseProject
SOURCE_DIR ${COMMON_BASE_PROJECT_DIR}
BINARY_DIR ${COMMON_BASE_PROJECT_DIR}/build
INSTALL_COMMMAND ""
)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
include_directories(${COMMON_BASE_PROJECT_DIR}/include)
add_library(
${LIBRARY_NAME}
SHARED
${SRC_FILES}
${INCLUDE_FILES}
)
target_link_libraries (Bios ${COMMON_BASE_PROJECT_DIR}/build/libCommonBase.dll)
add_dependencies(Bios CommonBaseProject)
but i get error:
[100%] Linking CXX shared library libCommonBase.dll
[100%] Built target CommonBase
[ 50%] Performing install step for 'CommonBaseProject'
make[3]: *** No rule to make target 'install'. Stop.
I don't need to make install step, so my question is: how to disable it?

You almost had it: Instead of INSTALL_COMMAND "" put something like
INSTALL_COMMAND cmake -E echo "Skipping install step."

You can generate a target for the build step with STEP_TARGETS build and add dependency on this particular target. The step targets are named <external-project-name>-<step-name> so in this case the target representing the build step will be named CommonBaseProject-build.
You probably also want to exclude the CommonBaseProject from the "all" target with EXCLUDE_FROM_ALL TRUE.
ExternalProject_Add(CommonBaseProject
SOURCE_DIR ${COMMON_BASE_PROJECT_DIR}
BINARY_DIR ${COMMON_BASE_PROJECT_DIR}/build
STEP_TARGETS build
EXCLUDE_FROM_ALL TRUE
)
add_dependencies(Bios CommonBaseProject-build)

Not relevant to your question, which it was already answered, but in my case I had the following ExternalProject_Add directive:
ExternalProject_Add(external_project
# [...]
# Override build/install command
BUILD_COMMAND ""
INSTALL_COMMAND
"${CMAKE_COMMAND}"
--build .
--target INSTALL # Wrong casing for "install" target
--config ${CMAKE_BUILD_TYPE}
)
In this case cmake quits with very similar error (*** No rule to make target 'INSTALL'), but in this case it's the external project that is looking for incorrect uppercase INSTALL target: correct case is install instead. Apparently, that worked in Windows with MSVC but fails in unix operating systems.

Since at least CMake 3.10 the empty string is sufficient to suppress the install step:
Passing an empty string as the <cmd> makes the install step do nothing.
The same goes for the other stages; see the docs for more.
If you're still building with CMake <3.10 then you need to update CMake ;)

Related

CMake run target_link_libraries after dependencies are built from external project

I am using ncurses in a project and would like the project to pull ncurses from git and to be used in the project. The external project is set up like so:
ExternalProject_Add(ncurses-extern
EXCLUDE_FROM_ALL TRUE
INSTALL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ncurses
GIT_REPOSITORY https://github.com/mirror/ncurses.git
GIT_TAG master
UPDATE_COMMAND ""
CONFIGURE_COMMAND <SOURCE_DIR>/configure --prefix <INSTALL_DIR> --with-shared --without-debug --without-manpages --includedir <INSTALL_DIR>/include
BUILD_COMMAND make -C <BINARY_DIR>
INSTALL_COMMAND make -C <BINARY_DIR> install
)
then I would like to create a interface library to make linking targets to it simpler like so:
add_library(ncurses-lib INTERFACE)
add_dependencies(ncurses-lib ncurses-extern)
target_link_libraries(ncurses-lib INTERFACE ${CMAKE_SOURCE_DIR}/libs/ncurses/lib/libncurses.so)
target_include_directories(ncurses-lib INTERFACE ${CMAKE_SOURCE_DIR}/libs/ncurses/include)
I would think as ncurses-lib depends on ncurses-extern cmake would build ncurses-extern then try to make the library but it doesn't.
If ncurses-extern has been built and installed already then the cmake works, however if it cannot find libncurses.so (because the external project hasn't been run yet), it fails to build with the error:
ninja: error: '../libs/ncurses/lib/libncurses.so', needed by 'main', missing and no known rule to make it
How can I make it so the target_link_libraries only runs after the external project.

CMakeLists.txt not in root directory, can't do an automatic build

I have an issue with a repository not having its CMakeLists in the root directory, namely https://github.com/lz4/lz4
The CMakeLists.txt is in the subfolder contrib/cmake_unofficial.
I already checked similar questions on SO (Is it possible to have cmake build file (CMakeLists.txt) not in root in CLion, cmake - CMakeLists.txt is not in root folder (but is included in source)), but they only provide alternatives, and not a solution applicable to my situation.
Heres the cmake module I came up with:
if(ENABLE_LZ4)
message(STATUS "Using LZ4.")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DENABLE_LZ4")
# Enable ExternalProject CMake module
include(ExternalProject)
set(LZ4_SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/lz4)
set(LZ4_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/lib)
# Download and install lz4
ExternalProject_Add(
lz4
GIT_REPOSITORY https://github.com/lz4/lz4.git
GIT_TAG dev
SOURCE_DIR ${LZ4_SOURCE_DIR}
BINARY_DIR ${LZ4_BINARY_DIR}
INSTALL_COMMAND ""
CMAKE_ARGS
${LZ4_SOURCE_DIR}/contrib/cmake_unofficial
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
)
# Get lz4 source and binary directories from CMake project
ExternalProject_Get_Property(lz4 source_dir binary_dir)
# Create a liblz4 target to be used as a dependency by the program
add_library(liblz4 IMPORTED SHARED GLOBAL)
add_dependencies(liblz4 lz4)
include_directories(
${LZ4_SOURCE_DIR}/lib
)
set(LZ4_LIB ${LZ4_BINARY_DIR}/liblz4.so)
else()
message(STATUS "Not using LZ4.")
set(LZ4_LIB "")
endif()
Here the complete error output:
[ 0%] Performing update step for 'lz4'
Current branch dev is up to date.
[ 1%] Performing configure step for 'lz4'
CMake Error: The source directory "/****/build/lz4" does not appear to contain CMakeLists.txt.
Specify --help for usage, or press the help button on the CMake GUI.
CMakeFiles/lz4.dir/build.make:105: recipe for target 'lz4-prefix/src/lz4-stamp/lz4-configure' failed
make[2]: *** [lz4-prefix/src/lz4-stamp/lz4-configure] Error 1
CMakeFiles/Makefile2:72: recipe for target 'CMakeFiles/lz4.dir/all' failed
make[1]: *** [CMakeFiles/lz4.dir/all] Error 2
Makefile:94: recipe for target 'all' failed
make: *** [all] Error 2
I tried adding the path 'contrib/cmake_unofficial' to the CMAKE_ARGS variable (as seen in the module above), but it does not work (seems to be ignored?).
I also tried using PATCH_COMMAND to copy the CMakeLists.txt to the root before the build starts, but the relative paths of the file get messed up.
In other words, i need the cmake command to be called to build the library to be : cmake contrib/cmake_unofficial.
I also tried using CONFIGURE_COMMAND for this, but keep getting a file not found error for some reason (even though the path is correct).
The module has some other issues too, but I'm only interested in the non-root CMakeLists.
Thanks in advance!
ExternalProject separates download and source directories:
DOWNLOAD_DIR - a directory where downloading step is performed
SOURCE_DIR - a directory used as a source one when configuration step is performed
When use git for extract the project, note that git clone is called from the download directory, where it creates new directory with a project sources.
set(LZ4_DOWNLOAD_DIR ${CMAKE_CURRENT_BINARY_DIR}/lz4)
# Set a source dir based on the download one.
set(LZ4_SOURCE_DIR ${LZ4_DOWNLOAD_DIR}/lz4/contrib/cmake_unofficial)
set(LZ4_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/lib)
# Download and install lz4
ExternalProject_Add(
lz4
DOWNLOAD_DIR ${LZ4_DOWNLOAD_DIR} # Set download directory explicitely
GIT_REPOSITORY https://github.com/lz4/lz4.git
GIT_TAG dev
SOURCE_DIR ${LZ4_SOURCE_DIR}
BINARY_DIR ${LZ4_BINARY_DIR}
...
)
Probably not the correct way to do it, but this seems to work for me.
I used the CONFIGURE_COMMAND to call cmake on the correct directory.
Then use BUILD_COMMAND to call make
So essentially, it breaks down to this:
ExternalProject_Add(
lz4
GIT_REPOSITORY https://github.com/lz4/lz4.git
GIT_TAG dev
SOURCE_DIR ${LZ4_SOURCE_DIR}
BINARY_DIR ${LZ4_BINARY_DIR}
CONFIGURE_COMMAND cmake ${LZ4_SOURCE_DIR}/contrib/cmake_unofficial
BUILD_COMMAND make
INSTALL_COMMAND ""
CMAKE_ARGS
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
)

Using cmake as the configure tool (CONFIGURE_COMMAND) in CMakes "Add_External_Project" function

I am having an issue with CMakes Add_External_Project functionality (more of an annoyance than anything else). Specifically, I do not understand the keys CONFIGURE_COMMAND, BUILD_COMMAND and INSTALL_COMMAND.
In the following (working) example, which downloads Google's test library, the two files at the end of the question will ensure that the third party libraries are downloaded and built (not installed).
However, when I tried to add configure and build commands as "CONFIGURE_COMMAND" and "BUILD_COMMAND" (cmake . and cmake --build) instead of having to do execute_process CMake craps out with the error message:
[ 55%] Performing configure step for 'googletest'
/bin/sh: 1: cmake .: not found
Am I trying to do something that is obviously not within the scope of the Add_External_Project functionality?
Example Files:
CMakeLists.txt
cmake_minimum_required (VERSION 3.0)
project (Test VERSION 0.1.0.0 LANGUAGES CXX)
# Download and unpack googletest at configure time
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt.in" "${CMAKE_BINARY_DIR}/googletest-download/CMakeLists.txt" #ONLY)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/googletest-download" )
execute_process(COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/googletest-download")
add_subdirectory("${CMAKE_BINARY_DIR}/googletest-src" "${CMAKE_BINARY_DIR}/googletest-build")
CMakeLists.txt.in
cmake_minimum_required(VERSION 3.0)
project(third-party NONE)
include(ExternalProject)
ExternalProject_Add(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG master
SOURCE_DIR "#CMAKE_BINARY_DIR#/googletest-src"
BINARY_DIR "#CMAKE_BINARY_DIR#/googletest-build"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)
If you don't specify CONFIGURE_COMMAND at all, it will assume a CMake project and run the appropriate cmake command for you (by appropriate, I mean it will use the same CMake generator as your main build, etc.). Similarly, if you leave out BUILD_COMMAND, it will also assume a CMake project and do cmake --build for you. So in your case, just leave out those two lines and ExternalProject_Add() should do exactly what you want.
The main reason you might specify these two options as empty strings is to prevent those steps from doing anything at all. This can be useful, for example, to use ExternalProject_Add() simply for its download and unpacking functionality. This exact situation is used in a technique described here for downloading the source of GoogleTest so it can be added to your project via add_subdirectory(), making it part of your build (see also this answer and other answers to that question for some related material). I suspect this might be where your code is derived from, as the structure looks similar.
For completeness, if you find yourself in a situation where you do need to specify a CMake command, don't use a bare cmake to refer to the command to run. Instead, always use ${CMAKE_COMMAND}, which is provided by CMake as the location of the CMake executable currently being used to process the file. Using this variable means cmake doesn't have to be on the user's PATH and also ensures that if the developer chooses to run a different version of CMake other than the one on the PATH, that same cmake will still be used for the command you are adding.
You can use PATCH_COMMAND like this:
option(WITH_MBEDTLS "Build with mbedtls" OFF)
if(WITH_MBEDTLS)
ExternalProject_Add(external-mbedtls
URL https://github.com/ARMmbed/mbedtls/archive/mbedtls-2.16.1.tar.gz
UPDATE_COMMAND ""
PATCH_COMMAND ./scripts/config.pl set MBEDTLS_THREADING_C &&
./scripts/config.pl set MBEDTLS_THREADING_PTHREAD
CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX:PATH=${PROJECT_BINARY_DIR}/third_party/mbedtls
-DCMAKE_TOOLCHAIN_FILE:PATH=${TOOLCHAIN_FILE}
-DCMAKE_BUILD_TYPE:STRING=Debug
-DENABLE_TESTING:BOOL=OFF
-DENABLE_PROGRAMS:BOOL=ON
TEST_COMMAND ""
)
set(MBEDTLS_PREFIX ${PROJECT_BINARY_DIR}/third_party/mbedtls PARENT_SCOPE)
endif(WITH_MBEDTLS)

BUILD_BYPRODUCTS and dependency cycle for cmake + ninja + googletest + ExternalProject_Add

Attempting to use external project to build google test like so.
# Add googletest
ExternalProject_Add( googletest
GIT_REPOSITORY https://github.com/google/googletest.git
# We don't need to run update command. Takes time
# and the version we initially d/l will shoudl be fine
CMAKE_ARGS = "-Dgtest_disable_pthreads=1"
# Don't run update
UPDATE_COMMAND ""
# Disable install step
INSTALL_COMMAND ""
# BUILD_BYPRODUCTS googletest-prefix/src/googletest-stamp/googletest-gitinfo.txt
# BUILD_BYPRODUCTS googletest-prefix/tmp/googletest-cfgcmd.txt
BUILD_BYPRODUCTS "googletest-prefix/src/googletest-build/googlemock/libgmock_main.a"
)
# Get include dirs for googletest framework
ExternalProject_Get_Property(googletest source_dir)
set(GTEST_INCLUDE_DIRS
${source_dir}/googlemock/include
${source_dir}/googletest/include
)
# Create library target for gmock main, which is used to create
# test executables
ExternalProject_Get_Property(googletest binary_dir)
set(GTEST_LIBRARY_PATH ${binary_dir}/googlemock/libgmock_main.a)
set(GTEST_LIBRARY gmock_main)
add_library(${GTEST_LIBRARY} UNKNOWN IMPORTED)
set_property(TARGET ${GTEST_LIBRARY} PROPERTY IMPORTED_LOCATION ${GTEST_LIBRARY_PATH})
add_dependencies(${GTEST_LIBRARY} googletest)
With the ninja generator I get the below warning.
Policy CMP0058 is not set: Ninja requires custom command byproducts to be
explicit. Run "cmake --help-policy CMP0058" for policy details. Use the
cmake_policy command to set the policy and suppress this warning.
This project specifies custom command DEPENDS on files in the build tree
that are not specified as the OUTPUT or BYPRODUCTS of any
add_custom_command or add_custom_target:
googletest-prefix/src/googletest-stamp/googletest-gitinfo.txt
googletest-prefix/tmp/googletest-cfgcmd.txt
For compatibility with versions of CMake that did not have the BYPRODUCTS
option, CMake is generating phony rules for such files to convince 'ninja'
to build.
Project authors should add the missing BYPRODUCTS or OUTPUT options to the
custom commands that produce these files.
If I oblige the request of the cmake error by uncommenting the build byproducts lines in my external project command, I get a cyclical dependency error. However, if I leave the build byproducts out of it, the project seems to build just fine.
$ ninja
ninja: error: dependency cycle: googletest-prefix/src/googletest-stamp/googletest-configure -> googletest-prefix/tmp/googletest-cfgcmd.txt -> googletest-prefix/src/googletest-stamp/googletest-configure
I'm using cmake 3.4, ninja 1.6, and running on Windows using MSYS2 package.
I added cmake_policy(SET CMP0058 NEW) to my toplevel CMakeLists.txt file as the --help-policy text explains to. It no longer generates the warnings afterwards. I guess those files aren't needed. Not sure how they're getting picked up as dependencies.
Try to use something like in ExternalProject_Add function:
set(GMOCK_FILE_DIR "gmock-${GMOCK_VERSION}/src/googletest_github-build/googlemock/")
BUILD_BYPRODUCTS "${GMOCK_FILE_DIR}gtest/libgtest_main.a"
BUILD_BYPRODUCTS "${GMOCK_FILE_DIR}gtest/libgtest.a"
BUILD_BYPRODUCTS "${GMOCK_FILE_DIR}libgmock_main.a"
BUILD_BYPRODUCTS "${GMOCK_FILE_DIR}libgmock.a"

ExternalProject_Add doesn't install the Release configuration of the project

EDIT: I have found a probable cause but I do not understand why: the last line in the below script Project(Externals) when removed fixes my issue. So the question now why??
cmake_minimum_required(VERSION 2.8)
include(ExternalProject)
MACRO(EXTERNAL_DEF aNewTargetName aPathToSource)
ExternalProject_Add(
${aNewTargetName}
PREFIX ${CMAKE_INSTALL_PREFIX}
SOURCE_DIR ${aPathToSource}
TMP_DIR "${CMAKE_INSTALL_PREFIX}/tmp/${CMAKE_BUILD_TYPE}"
DOWNLOAD_DIR "${CMAKE_INSTALL_PREFIX}/src/${CMAKE_BUILD_TYPE}"
BINARY_DIR "${CMAKE_INSTALL_PREFIX}/src/${CMAKE_BUILD_TYPE}/${aNewTargetName}-build"
STAMP_DIR "${CMAKE_INSTALL_PREFIX}/src/${CMAKE_BUILD_TYPE}/${aNewTargetName}-stamp"
CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
--debug-output
BUILD_COMMAND "${CMAKE_COMMAND}" --build
"${CMAKE_INSTALL_PREFIX}/src/${CMAKE_BUILD_TYPE}/${aNewTargetName}-build" --config "${CMAKE_BUILD_TYPE}"
#INSTALL_DIR "${CMAKE_INSTALL_PREFIX}"
)
ENDMACRO()
get_filename_component(zlibAbsPath "./zlib" ABSOLUTE)
EXTERNAL_DEF(zlib_external ${zlibAbsPath})
Project(Externals)
I invoke cmake on the above CMakeLists.txt file with CMAKE_INSTALL_PREFIX set to let's say "d:\externals" and CMAKE_BUILD_TYPE set to "Release"
Expectation:
I would expect only the Release configuration to get built. And after it gets built I would expect it to get installed in D:\externals\bin\zlib.dll.
Problem:
In reality, ExternalProject_Add builds both a Debug and a Release, and installs the debug version of the dll in D:\externals\bin\zlibd.dll
Isn't my build script correct? What am I doing wrong?
EDIT:
some more info. I just noticed. In the generated D:\externals\src\Release\zlib_external-build\zlib.sln, the INSTALL target is not selected to build at all. If I check it to build for the Release configuration then hit "Build" from visual studio, the INSTALL target builds and installs the files where I expect them. I have no idea what's going on...
CMAKE_BUILD_TYPE only work with single-configuration projects
you can change to:
BUILD_COMMAND ""
INSTALL_COMMAND
${CMAKE_COMMAND}
--build .
--target install
--config Release
BUILD_ALWAYS 1
Use DCMAKE_CFG_INTDIR variable.
...
CMAKE_ARGS
...
-DCMAKE_CFG_INTDIR=Release