Add 'install' target to 'all' in CMake - makefile

I have a CMake project with the following directory tree:
build/
assets/
dest/
<other files>
dest is a directory where all installed files should go:
The executable, which goes to dest/ with a simple make, this is controlled with CMAKE_RUNTIME_OUTPUT_DIRECTORY
The assets, located on assets/, which go to dest/ after a make install.
But I don't want to issue make install to copy all files do the dest/ dir: I want a simple make to do this.
In this sense, how do I add the install target to the default one (all)? Or, is there a better way to solve this?

Using the following wont cause recursion. Requires CMake >= 3.15.
add_custom_command(
TARGET ${MY_TARGET} POST_BUILD
COMMAND ${CMAKE_COMMAND} --install ${CMAKE_BINARY_DIR} --config $<CONFIG>
)
Extra : You may want to provide a default (local) install path so this doesn't fail on Windows.
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "install" CACHE PATH "Default install path." FORCE)
endif()

This will execute the install target after building <target_name> (which could be all):
add_custom_command(
TARGET <target_name>
POST_BUILD
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target install
)

I've solved this problem by using the following macro to generate data dependency rules for the specified target. These rules require only a make and not a make install.
The macro maps an arbitrary file from your source tree into a "staging" tree, where each tree structure may be different, and you may optionally rename the file. The staged data is kept up-to-date, so if you change it in your source tree then it'll be updated the next time you make.
# Macro used to create dependencies for staging data files (e.g. config files, assets) and keeping them up-to-date.
# The given "source" file is copied (and possibly renamed) to "staged" for the given "target".
#
# It works by creating a rule that creates "staged" by copying "source", then creating a target that depends upon "staged",
# then making the given "target" depend upon the new target. Or in makefile speak:
#
# staged: source
# cp source staged
#
# targetData1: staged
#
# target: <existing dependencies...> targetData1
# <existing commands...>
#
# The intermediate rule is used for parallel build robustness. For details, see:
# http://www.cmake.org/cmake/help/v2.8.12/cmake.html#command:add_custom_command
#
# Example:
# target = myExeTarget
# source = "${CMAKE_CURRENT_SOURCE_DIR}/../../data/images/bush1.png"
# staged = "${STAGING_DATA_DIR}/images/bush1.png"
macro(add_data_dependency target source staged)
add_custom_command(
OUTPUT "${staged}"
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${source}" "${staged}" # Dir(s) will be created if needed.
DEPENDS "${source}"
)
if (NOT DEFINED add_data_dependency_counter)
#message(status "Setting tmp counter......")
set(add_data_dependency_counter "0")
endif()
math(EXPR add_data_dependency_counter "${add_data_dependency_counter} + 1")
#message(status "tmp counter is ${add_data_dependency_counter}......")
set(customTarget "${target}Data${add_data_dependency_counter}")
add_custom_target(${customTarget} DEPENDS "${staged}")
add_dependencies(${target} ${customTarget})
endmacro()
In your case, usage would be something like:
add_data_dependency(myTarget "${CMAKE_CURRENT_SOURCE_DIR}/assets/file1.ext" "${CMAKE_CURRENT_SOURCE_DIR}/dest/file1.ext")
add_data_dependency(myTarget "${CMAKE_CURRENT_SOURCE_DIR}/assets/file2.ext" "${CMAKE_CURRENT_SOURCE_DIR}/dest/file2.ext")
add_data_dependency(myTarget "${CMAKE_CURRENT_SOURCE_DIR}/assets/rename_me.ext" "${CMAKE_CURRENT_SOURCE_DIR}/dest/renamed.ext")
:
:
Then whenever you make, the files will be copied if missing or out-of-date (assuming that myTarget is part of all).

The normal way to use CMake is to create a build directory outside your project and all compiled binaries are put there. When you are finished developing and want to install some binaries into your system then you call make install. In this way you keep your project source folder free from all compiler generated stuff.
Example file directory structure:
my_project/
my_project_build/
from my_project_build you call cmake ../my_project to generate build files. Call make to build it and all binaries will be in my_project_build.

Related

How can i setup meson and ninja on Ubuntu-Linux to produce the expected .a file by use of MakeFile?

Some years ago on Ubuntu 16.0.4 I've used this library: git clone https://github.com/Beckhoff/ADS and using only the make command I got build, compile and finally on the main directory I found a file called AdsLib-Linux.a and maybe nothing more than this.
Now I'm on Ubuntu 20.04 I need this library once again but this times make dosn't produce the same output and looking forth to the ReadMe instructions I finally used that instead of make:
meson build
ninja -C build
That now create a new directory build but no .a file as before on the root directory. Instead a new file in the build directory libADSLib.a is there. The same thing happens using right the make command.
Maybe the author changed over the years something on the config files or the behavior of the tools have changed, but I cannot get the former file anymore and I need it for other referencing code that now is not executing anymore.
Looking to the MakeFile I found that in the example folder, differently from the one on the parent directory, the MakeFile has something like that:
$(warning ATTENTION make is deprecated and superseeded by meson)
...
${PROGRAM}: LIB_NAME = ../AdsLib-${OS_NAME}.a
...
But all i've tried reading the guides on meson and ninja about setup, configure, build, and so on, did not produce anymore that file.
I've tried also to first build and then copy all files form the example folder to the parent directory and then build again, but again no .a file there.
How's the right way to configure the build process corectly so that this -Linux.a file is created. Or if not possibile anymore, what does it now produce I can use instead of what produced before?
Meson is a build system generator, similar to CMake or somewhat like ./configure, you need to run meson, then run ninja to actually build something.
You need to run both meson and ninja:
meson setup builddir
ninja -C builddir
Once you do that successfully, there will be a libAdsLib.a inside the builddir directory.
Let me correct a bit #dcbaker, according to their README you should setup build as build directory:
# configure meson to build the library into "build" dir
meson build
# let ninja build the library
ninja -C build
Of course, in general, it shouldn't be specific, but their example code is written in a weird way so this path is hard-coded. So, to use the example:
# configure meson to build example into "build" dir
meson example/build example
# let ninja build the example
ninja -C example/build
# and run the example
./example/build/example
About the library: it's now libAdsLib.a and produced in build directory. The name is set here and it's now in linux naming style, the old one - not. So, you have options:
Update your configuration/build files (Makefile?) where you use it
Copy or make symbolic link, e.g.
$ ln -s <>/build/libAdsLib.a <target_path>/AdsLib-Linux.a
Above it's very dependent on your development environment, do you have installation or setup scripts for it? do you permissions to modify/configure parameters for target application? do you need to support both old and new names? - many questions not related to original question about meson.

How to install subdirectories to different locations with CMake?

I have a couple of CMake projects which have their own install logic defined. Now, I want to add a global CMakeLists.txt that adds those projects using add_subdirectory() in order to build and install all of them at once. How can I provide a different ${CMAKE_INSTALL_PREFIX} to each of them from the outside?
What I tried is changing the install prefix between the calls to add the subdirectories but they are installed when actually calling make install and at that time, the install prefix is already set to the last assigned value.
If your subprojects require same variable to have different values(at global scope) you cannot build them using add_subdirectory within single global project.
Instead, global project may install them using cmake call either via execute_process or ExternalProject_Add. Sometimes, install(SCRIPT) command may be used for that.
Installing using execute_process makes subproject available immediately at configuration stage.
It can be used when subproject has some packaging mechanism (see CMake tutorials about packaging), so global object may execute find_package(<subproject-name>) and link with libraries, defined in subproject, in the most simple way(using variables, filled outside).
# Configure subproject into <subproject-build-dir>
execute_process(COMMAND ${CMAKE_COMMAND} -D<var>=<value> <subproject-source-dir>
WORKING_DIRECTORY <subproject-build-dir>)
# Build and install subproject
execute_process(COMMAND ${CMAKE_COMMAND} --build <subproject-build-dir> --target install)
# Extract variables, related to subproject for link with it.
find_package(<subproject-name>)
# Create target in global project and link with subproject
include_directories(${<subproject-name>_INCLUDE_DIRS})
link_directories(${<subproject-name>_LIBRARY_DIRS})
add_executable(global_program main.cpp)
target_link_libraries(global_program ${<subproject-name>_LIBRARIES})
Installing using ExternalProject_add assigns target to the subproject, which will be installed at build stage. Linking with subproject in that case is also possible, but requires to fill variables manually.
# Configure, build and install subproject
ExternalProject_Add(<subproject_target>
SOURCE_DIR <subproject-source-dir>
CMAKE_CACHE_ARGS -D<var>=<value>
BINARY_DIR <subproject-build-dir>
INSTALL_DIR <CMAKE_INSTALL_PREFIX-for-subproject>
)
# Create target in global project and link with subproject
include_directories(<subproject-include-files-location>)
link_directories(<subproject-libraries-location>)
add_executable(global_program main.cpp)
target_link_libraries(global_program <subproject-libraries-name>)
# Make sure that subproject will be built before executable.
add_dependencies(global_program <subproject_target>)
Installing using install(SCRIPT) executes script at install stage. This approach can be used when there is no build-dependencies between global project and subproject.
subproject_install.cmake:
# Configure subproject into <subproject-build-dir>
execute_process(COMMAND ${CMAKE_COMMAND} -D<var>=<value> <subproject-source-dir>
WORKING_DIRECTORY <subproject-build-dir>)
# Build and install subproject
execute_process(COMMAND ${CMAKE_COMMAND} --build <subproject-build-dir> --target install)
CMakeLists.txt:
install(SCRIPT subproject_install.cmake)

Detect if files are overridden in the installation directory

I'm configuring a CMake project from source and build and install it with make, i.e.,
$ ls
./build/ ./source/
$ cd build/
$ cmake ../source/
[...]
$ make
[...]
$ make install
[...]
I noticed now that the project is badly configured in that it contains numerous files with the same name that get overridden in the installation directory, e.g, /usr/local/include/mystring.h gets written to more than once by make install.
Is there a systematic way to detect if files are overridden within the same project?
Well, you can wrap install() calls into your own function, which would store all installed files in cache list variable and check for existing item before actually call install().
You may also check ${PROJECT_BINARY_DIR}/install_manifest.txt file - maybe it would contain duplicates in your case.

CMake: Exclude custom install target(s) from 'make install [all]'

I have a library that is built and linked to as part of my project. I want to provide the facility to OPTIONALLY have the library installed system-wide (or wherever ${CMAKE_INSTALL_PREFIX} is set). Otherwise, by default, the project's final build products will be linked statically to the library, and the former get installed, but the library binaries stay in the build directory.
In other words:
$ make
$ make install
will build and install, the programs, but only something like
$ make install.foo
will install the library to ${CMAKE_INSTALL_PREFIX}, building it first if needed.
I have something like this so far (simplified from the actual script, so there might be errors):
INCLUDE_DIRECTORIES( "${CMAKE_CURRENT_LIST_DIR}")
SET (FOO_LIBRARY "foo")
# Following builds library and makes it available to
# be linked other targets within project by:
# TARGET_LINK_LIBRARIES(${progname} ${FOO_LIBRARY})
ADD_LIBRARY(${FOO_LIBRARY}
foo/foo.cpp # and other sources ...
)
###########################################################
# Approach #1
# -----------
# Optionally allow users to install it by invoking:
#
# cmake .. -DINSTALL_FOO="yes"
#
# This works, but it means that users will have to run
# cmake again to switch back and forth between the libary
# installation and non-library installation.
#
OPTION(INSTALL_FOO "Install foo" OFF)
IF (INSTALL_FOO)
INSTALL(TARGETS ${FOO_LIBRARY} DESTINATION lib/foo)
SET(FOO_HEADERS foo/foo.h)
INSTALL(FILES ${FOO_HEADERS} DESTINATION include/foo)
UNSET(INSTALL_FOO CACHE)
ENDIF()
###########################################################
###########################################################
# Approach #2
# -----------
# Optionally allow users to install it by invoking:
#
# make install.foo
#
# Unfortunately, this gets installed by "make install",
# which I want to avoid
SET(FOO_INSTALL "install.foo")
ADD_CUSTOM_TARGET(${FOO_INSTALL}
COMMAND ${CMAKE_COMMAND}
-D COMPONENT=foo
-P cmake_install.cmake)
ADD_DEPENDENCIES(${FOO_INSTALL} ${FOO_LIBRARY})
INSTALL(TARGETS ${FOO_LIBRRARY}
DESTINATION lib/foo COMPONENT foo)
SET(FOO_HEADERS foo/foo.h)
INSTALL(FILES ${FOO_HEADERS}
DESTINATION include/foo COMPONENT foo)
###########################################################
As can be seen, approach #1 sort of works, but the required steps to install the library are:
$ cmake .. -DINSTALL_FOO="yes"
$ make && make install
And then, to go back to "normal" builds, the user has to remember to run cmake again without the "-DINSTALL_FOO" option, otherwise the library will be installed on the next "make install".
The second approach works when I run "make install.foo", but it also install the library if I run "make install". I would like to avoid the latter.
Does anyone have any suggestions on how to go about achieving this?
You are on the right track with approach #2. You can trick CMake into avoiding the installation of the FOO related files by using the OPTIONAL switch of the install command.
For the FOO library target, the commands have to modified in the following way:
SET (FOO_LIBRARY "foo")
ADD_LIBRARY(${FOO_LIBRARY} EXCLUDE_FROM_ALL
foo/foo.cpp
)
INSTALL(TARGETS ${FOO_LIBRARY}
DESTINATION lib/foo COMPONENT foo OPTIONAL)
The EXCLUDE_FROM_ALL is added to ADD_LIBRARY to prevent the library from building when you run a plain make. The installation of FOO_LIBRARY is made optional by adding the OPTIONAL switch.
Making the installation of the FOO_HEADERS optional requires the following changes:
SET(FOO_HEADERS foo/foo.h)
SET(BINARY_FOO_HEADERS "")
FOREACH (FOO_HEADER ${FOO_HEADERS})
ADD_CUSTOM_COMMAND(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${FOO_HEADER}
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${FOO_HEADER} ${CMAKE_CURRENT_BINARY_DIR}/${FOO_HEADER}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${FOO_HEADER})
LIST (APPEND BINARY_FOO_HEADERS ${CMAKE_CURRENT_BINARY_DIR}/${FOO_HEADER})
ENDFOREACH()
INSTALL(FILES ${BINARY_FOO_HEADERS}
DESTINATION include/foo COMPONENT foo OPTIONAL)
The FOREACH loop sets up custom commands which copy the FOO_HEADERS verbatim to the corresponding binary dir. Instead of directly using the headers in the current source dir the INSTALL(FILES ... command picks up the copied headers from the binary dir. The paths of the headers in the binary dir are collected in the variable BINARY_FOO_HEADERS.
Finally the FOO_INSTALL target has to be set up in the following way:
SET(FOO_INSTALL "install.foo")
ADD_CUSTOM_TARGET(${FOO_INSTALL}
COMMAND ${CMAKE_COMMAND}
-D COMPONENT=foo
-P cmake_install.cmake
DEPENDS ${BINARY_FOO_HEADERS})
ADD_DEPENDENCIES(${FOO_INSTALL} ${FOO_LIBRARY})
The custom FOO_INSTALL is added with a dependency on BINARY_FOO_HEADERS to trigger the copying of the header files. A target level dependency on FOO_LIBRARY triggers the building of the library when you run make install.foo.
The solution however has the following drawbacks:
Upon configuring CMake will issues a warning Target "foo" has EXCLUDE_FROM_ALL set and will not be built by default but an install rule has been provided for it. CMake will however do the right thing anyway.
Once you have run make install.foo a subsequent make install command will also install all FOO related files, because the built FOO library and the headers exist in the binary directory.
Check if this solution works for you.
You would have to use different commands to those you said you would like to use, but I think it addresses the problem fairly well.

Create tar archive with Cmake

I have used the *_OUTPUT_PATH variables in my CMakeLists.txt file to specify specific locations for my binaries and library files, and that seems to be working "automatically"
I would like as part of a "build" for one final step to happen, which is to create a tarball of the binaries that output directory.
What do I need to add to create a tar?
You can use a CMake custom target to invoke CMake in command mode and have it produce a tarball from the binaries in the output directory. Here is a CMakeLists.txt that sketches the necessary steps:
project(TarExample)
set (EXECUTABLE_OUTPUT_PATH "${CMAKE_CURRENT_BINARY_DIR}/executables")
add_executable(foo foo.cpp)
add_custom_target(create_tar ALL COMMAND
${CMAKE_COMMAND} -E tar "cfvz" "executables.tgz" "${EXECUTABLE_OUTPUT_PATH}")
add_dependencies(create_tar foo)
The custom target generates a gzipped tarball from the files in the directory EXECUTABLE_OUTPUT_PATH. The add_dependencies call ensures that the tarball is created as a final step.
To produce an uncompressed tarball, use the option cfv instead of cfvz.

Resources