Rename the output of CPack - cmake

I would like to rename the installer file that CPack (v2.8.7) produces to include a version number that is obtained at build time from the version control system. It appears this cannot be be done by setting the CPACK_* variables because that happens at "cmake" time.
What I want to be able to do is to run "(n)make package" and have the installer file be created with no further commands required. The two possible approaches that I am aware of are manipulating the CPack filename variables at build time and renaming the final output of CPack.
If using "include(CPack)" in a CMakeLists.txt file then it appears that CPack always runs last and you can't have a post-build command. This mailing list message suggests that a custom target can be written to run CPack, but I was unable to figure out how to do that without creating infinite recursion.
How can this be done?

Why not extract the build-info from the VCS at cmake-time? Then you can easily modify the CPACK_PACKAGE_FILE_NAME to include your version-number.
Added bonus: When doing this at CMake-time, you can e.g. fill a "Readme.txt" file with the git-info using CMake's configure_file and add it to your package. Or perhaps use it to fill a "config.h", which is used in your builds.
Example:
in one of my own projects, I have a little piece of CMake code which finds Git and extracts the current changeset hash from the source code repository. It may not be the best Git way of extracting the info, but it works for me...
# First try to find the git-executable
find_program( Git_EXECUTABLE NAMES git git.cmd PATHS
${Git_DIR}
ENV PATHS
$ENV{Git_DIR}
)
# Run "git log -n 1 --pretty="%h" for the current commit-hash
execute_process( COMMAND ${Git_EXECUTABLE} "log" "-n" "1" "--pretty=\"%h\""
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE Git_Commit_Hash
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# and use a regex to strip quotes.
string( REGEX REPLACE "^\"(.*)\"$" "\\1" Git_Commit_Hash ${Git_Commit_Hash} )
The result will be a Git_Commit_Hash variable with the 7-char hash value, which is used when setting up CPack:
set( CPACK_PACKAGE_NAME "MyProject" )
message( STATUS " CPack options: " ${CPACK_PACKAGE_NAME} )
message( STATUS " Preparing CPACK: " )
message( STATUS " and hash: ${Git_Commit_Hash}" )
set( CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}_${Git_Build_Version}_${CPACK_PACKAGE_VERSION}" )

With a bit of help from the CMake mailing list I figured out how to do it, using subversion.
CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(myapp)
add_executable(main main.cpp)
install(TARGETS main DESTINATION .)
add_custom_target(first ALL
# update the working copy
COMMAND ${Subversion_SVN_EXECUTABLE} update ${CMAKE_SOURCE_DIR}
# generate cpackoptions.cmake at build time so we get the
# most recent revision number
COMMAND ${CMAKE_COMMAND}
-DSOURCE_DIR=${CMAKE_SOURCE_DIR}
-DBINARY_DIR=${CMAKE_BINARY_DIR}
-Dproj_name=${CMAKE_PROJECT_NAME}
-P ${CMAKE_SOURCE_DIR}/create-cpackoptions.cmake
)
add_dependencies(main first)
set(CPACK_PROJECT_CONFIG_FILE ${CMAKE_BINARY_DIR}/CPackOptions.cmake)
include(CPack)
create-cpackoptions.cmake
include(FindSubversion)
Subversion_WC_INFO(${SOURCE_DIR} ${proj_name})
set(revision ${${proj_name}_WC_REVISION})
configure_file(${SOURCE_DIR}/CPackOptions.cmake.in
${BINARY_DIR}/CPackOptions.cmake
#ONLY)
cpackOptions.cmake.in
set(CPACK_PACKAGE_FILE_NAME "#proj_name#-${CPACK_PACKAGE_VERSION}r#revision#-${CPACK_SYSTEM_NAME}")

Related

Generate script during build with cmake

I have a cmake project with a single source file called main.c. I want to additionally provide a wrapper script which calls main with specific parameters.
My CMakeLists.txt looks as follows:
cmake_minimum_required(VERSION 3.1...3.16)
file(WRITE ${CMAKE_BINARY_DIR}/wrapper "#!/usr/bin/env bash\n")
file(APPEND ${CMAKE_BINARY_DIR}/wrapper "./main options\n")
add_executable(main main.c)
add_custom_target(wrapper_target
ALL DEPENDS wrapper)
add_custom_target(main_target
ALL DEPENDS main wrapper_target)
add_dependencies(main wrapper_target)
install(
TARGETS main
RUNTIME DESTINATION bin/)
install(
PROGRAMS wrapper
DESTINATION bin/)
If I run cmake --install ., the script wrapper is installed together with the binary main. Running cmake --build . produces the script wrapper, but it is not marked as executable (on Linux).
How can I tell cmake to also generate wrapper during build and mark it as executable?
Note: I need this for an automated build system which runs build and not install, and expects a specific file to be available on build.
Try:
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/wrapper.tmp
"#!/usr/bin/env bash
# Note that './main' is relative from whatever directory you are in
# Use just main assuming the install prefix is in your bath
# Or use $<TARGET_FILE:main>
# Or maybe ${CMAKE_INSTALL_PREFIX}/bin/main
./main options
")
# add execute permissions
file(
COPY ${CMAKE_CURRENT_BINARY_DIR}/wrapper.tmp
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
)
# Rename the file
file(RENAME
${CMAKE_CURRENT_BINARY_DIR}/wrapper.tmp
${CMAKE_CURRENT_BINARY_DIR}/wrapper
)
How can I tell cmake to also generate wrapper during build and mark it as executable?
file( is a in a cmake script - it is executed during configuration phase, when cmake is executed. To generate the file during build use add_custom_command, the most "portable" way in cmake sense would be to run a cmake script inside add_custom_command:
add_custom_command
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/wrapper
COMMAND $(CMAKE_COMMAND)
-D CMAKE_CURRENT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}
-P ${CMAKE_CURRENT_SOURCE_DIR}/the_cmake_script.cmake
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/the_cmake_script.cmake
COMMAND Generating wrapper script...
VERBATIM
)
then inside the_cmake_script.cmake you could do the script above - add_custom_command will execute the command cmake -P <the script> during build of you project. That way you can DEPEND properly on the wrapper script.
CMake 3.20 added support for FILE_PERMISSIONS attribute to the FILE command. So one could simply:
FILE(GENERATE OUTPUT ${CMAKE_BINARY_DIR}/wrapper
CONTENT
"#!/usr/bin/env bash
./main options
"
FILE_PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ)

CMake custom target doesn't build

I'm building two targets using:
add_library(tgt1 SHARED a.cpp)
add_library(tgt2 SHARED b.cpp)
After both are built, I need to run a post build step that depends on both targets. I tried many combinations of the following but with no success:
add_custom_target(final_tgt DEPENDS tgt1 tgt2)
add_custom_command(TARGET final_tgt POST_BUILD COMMAND <command> ARGS <args>)
The final target would simply not build, even though its build.make contains the custom command.
Tried to use ALL for the custom target, however make attempts to build it first while missing the first targets.
And I can't use an add_library or add_executable for the final target, since they require specifying source files.
What is the correct way to do it?
===================================
Edit: below is a minimal verifiable source code.
What it attempts to do is to compile code (for Mac) in two architectures and as a post-build to create a universal binary using lipo:
cmake_minimum_required(VERSION 2.8)
set(icpc_req_path "/usr/local/bin/icpc-16.0.146")
set(CMAKE_CXX_COMPILER "${icpc_req_path}")
project("CMakeTest")
set(SOURCE_FILES a.cpp)
set (TARGET_NAME "TGT")
set(TARGETS "")
set(ARCHITECTURES i386 x86_64)
foreach(ar ${ARCHITECTURES})
set(CMAKE_CXX_FLAGS_RELEASE "")
set(CMAKE_CXX_FLAGS_DEBUG "")
set(CMAKE_CXX_FLAGS "")
add_library(TGT_${ar} SHARED ${SOURCE_FILES})
set_target_properties(${TARGET_NAME}_${ar}
PROPERTIES COMPILE_FLAGS "-arch ${ar} -xSSE3")
set_target_properties(${TARGET_NAME}_${ar}
PROPERTIES LINK_FLAGS "-arch ${ar}")
set(TARGETS "${TARGETS};lib${TARGET_NAME}_${ar}.dylib")
endforeach(ar)
message("Targets: ${TARGETS}")
add_custom_target(${TARGET_NAME} DEPENDS ${TARGETS})
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND "lipo"
ARGS "-create" ${TARGETS} "-output" "${TARGET_NAME}.dylib")
And the contents of a.cpp is:
int main(){}
add_custom_command comes in two flavors: Producing new output, and acting on single targets. Docs
What is the command that you are calling doing? Is it in any way producing results (new files, etc)? If so, use add_custom_command like this:
add_custom_command(
OUTPUT <output-file>
DEPENDS tgt1 tgt2
COMMAND <command>
ARGS <args>
COMMENT "Running <command> on targets tgt1 and tgt2."
)
Using the second variant of add_custom_command which does not have an OUTPUT argument, because it changes a <target> as POST_BUILD (or pre-build, pre-link) step, needs a single target. So, which one of tgt1 and tgt2 gets modified by your <command>?
Let's assume that tgt1 gets modified in the POST_BUILD step and tgt2 is untouched. Then you can do it like this:
add_library(tgt2 SHARED b.cpp)
add_library(tgt1 SHARED a.cpp)
add_custom_command(
TARGET tgt1 POST_BUILD
COMMAND <command>
ARGS <args>
)
add_dependencies(tgt1 tgt2) # tgt1 depends on tgt2 because
# POST_BUILD-step is just the final step to build 'tgt1'
# NOTE: It is incorrect to modify 'tgt2' as POST_BUILD step for tgt1.
# So this example expects no changes to tgt2 in add_custom_command.
--
EDIT after more details given in question:
Working example
CMakeLists.txt
# I don't have 'icpc' and could not find it easily available for macOS.
# Instead, let's create a file "TGT" where contents are the two hashes of the two
# libraries, like done in 'Th.Thielemann's answer.
cmake_minimum_required(VERSION 3.10)
project(q50198141)
add_library(Big SHARED library1.cpp)
add_library(Foo SHARED library2.cpp)
add_custom_command(OUTPUT combined
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/combine.sh
ARGS $<TARGET_FILE:Big> $<TARGET_FILE:Foo> combined
DEPENDS Big Foo combine.sh
COMMENT Build output 'combined'
)
add_custom_target(run_combined ALL DEPENDS combined)
combined.sh (Make sure to be excecutable!)
#!/bin/bash
# Hardcoded for q50198141
# Args: In1 In2 Out
md5sum $1 $2 > $3
The following example works for me.
Two targets independent from each other. One custom target depending from both.
add_library(Big SHARED ${SOURCES_BIG})
add_library(Foo SHARED ${SOURCES_FOO})
add_custom_target(pack ALL
COMMAND ${CMAKE_COMMAND} -E md5sum ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}Big${CMAKE_SHARED_LIBRARY_SUFFIX}
COMMAND ${CMAKE_COMMAND} -E md5sum ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}Foo${CMAKE_SHARED_LIBRARY_SUFFIX}
DEPENDS Big Foo
COMMENT Build target pack
)
Note: Calculating the hash of the code is just to show whether targets are rebuild on changes or not.

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)

cmake add_custom_command not working

I am trying to run gperf from a cmake file.
I created a very minimal CMakeLists.txt below.
When I run it by
$ cmake .
$ make
It does not create the example.hpp file
What could be problem with the below CMakeLists.txt?
cmake_minimum_required( VERSION 2.6 )
function(gperf_generate_new source target)
add_custom_target(${target} echo "Creating ${target}")
add_custom_command(
SOURCE ${source}
TARGET ${target}
COMMAND gperf -L c++ ${source} > ${target}
OUTPUTS ${target}
DEPENDS ${source}
)
endfunction()
gperf_generate_new(command_options.new.gperf example.hpp)
Files, produced by source-files generators(like gpref) are rarely needed as standalone. Instead, these source files are usually used for creating executables or libraries inside a project.
So, standard pattern of using source-file generators in the CMake looks like:
# Call add_custom_command() with appropriate arguments for generate output file
# Note, that *gperf* will work in the build tree,
# so for file in the source tree full path should be used.
function(gperf_generate_new input output)
add_custom_command(
OUTPUT ${output}
COMMAND gperf -L c++ ${input} > ${output}
DEPENDS ${input}
COMMENT "Generate ${output}" # Just for nice message during build
)
endfunction()
# Generate *example.hpp* file ...
gperf_generate_new(${CMAKE_CURRENT_SOURCE_DIR}/command_options.new.gperf example.hpp)
# ... for use it in executable
add_executable(my_program ${CMAKE_CURRENT_BINARY_DIR}/example.hpp <other sources>)
If you want only to test whether example.hpp is generating, instead of add_executable() use
add_custom_target(my_target
ALL # Force target to be built with default build target.
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/example.hpp
)
Note, that linkage between add_custom_command and add_custom_target is expressed using same filename in their OUTPUT and DEPENDS options correspondingly. With such link order of these commands is insignificant (but both commands should be called from the same CMakeLists.txt script).

What is INSTALL_DIR useful for in ExternalProject_Add command?

I don't understand the usage of INSTALL_DIR in ExternalProject_Add command. I try to use it but it does not seem to work. Here is an example of a CMakeLists.txt, using Eigen library which compiles quickly:
cmake_minimum_required (VERSION 2.6)
project (example CXX)
include(ExternalProject)
include(ProcessorCount)
set(CMAKE_VERBOSE_MAKEFILE ON)
ProcessorCount(N)
if(NOT N EQUAL 0)
set(CMAKE_BUILD_FLAGS -j${N})
endif()
ExternalProject_Add
(
mylib
PREFIX myprefix
DOWNLOAD_COMMAND wget http://bitbucket.org/eigen/eigen/get/3.2.4.tar.gz && tar xvzf 3.2.4.tar.gz -C mylib --strip-components=1
)
I chose the following project hierarchy:
project
CMakeLists.txt
build/
From build repository, I type:
cmake ..
make
The installation process fails with the following message:
file cannot create directory: /usr/local/include/eigen3.
Maybe need administrative privileges.
As far as I understand, it means that I need to define a "prefix" during the configuration step:
cmake -D CMAKE_INSTALL_PREFIX=$INSTALL_DIR ..
But, the INSTALL_DIR variable is already defined in the ExternalProject_Add command. However, I get the same error when I modify the value of INSTALL_DIR by adding
INSTALL_DIR myprefix/src/install
in the ExternalProject_Add command.
So, what is INSTALL_DIR useful for?
What am I doing wrong?
Of course, I know how to provide my own configuration command to add a prefix and solve the problem. But it is not my question. My question is: if I have to do that, what is the purpose of INSTALL_DIR?
From what I found in this discussion https://www.mail-archive.com/cmake#cmake.org/msg51663.html (scroll to the end of the page to navigate through the thread messages) it is indeed pretty common thing to use CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/contrib
Furthermore, lurking through the ExternalProject.cmake module I found out that the only effect setting this directory has is that CMake will create directory specified in INSTALL_DIR before doing anything else.
Also, it will set the property that you can gather through ExternalProject_Get_Property(${project_name} install_dir) command.
And that's pretty much it.
// As of CMake version 3.2.2