I want to rename certain executables in CMakeLists.txt but also want symbolic links from the older names to new files for backward compatibility. How can this be accomplished on systems that support symbolic links?
Also what are the alternatives for system that does not support symbolic links?
Another way to do it:
INSTALL(CODE "execute_process( \
COMMAND ${CMAKE_COMMAND} -E create_symlink \
${target} \
${link} \
)"
)
This way the symlinking will be done during make install only.
You can create a custom target and use CMake to create symlinks
ADD_CUSTOM_TARGET(link_target ALL
COMMAND ${CMAKE_COMMAND} -E create_symlink ${target} ${link})
This will only work on systems that support symlinks, see the documentation.
Before CMake v3.14, this did not work on Windows. In v3.13, support for Windows was added.
Another method that is a bit more verbose and only runs on install:
macro(install_symlink filepath sympath)
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${filepath} ${sympath})")
install(CODE "message(\"-- Created symlink: ${sympath} -> ${filepath}\")")
endmacro(install_symlink)
Use it like this (similar to ln -s):
install_symlink(filepath sympath)
Lets say you need to create a link in binary dir to a target located in source directory.
You can try file CREATE_LINK since version 3.14
${CMAKE_COMMAND} -E create_symlink is accessible at Windows since 3.17
You can use execute_process since early cmake versions:
if(WIN32)
get_filename_component(real_path "${dirname}" REALPATH)
string(REPLACE "/" "\\" target_path "${real_path}")
execute_process(
COMMAND cmd /C mklink /J ${dirname} "${target_path}"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
)
else()
execute_process(COMMAND "ln -s ${CMAKE_CURRENT_SOURCE_DIR}/${dirname} ${CMAKE_CURRENT_BINARY_DIR}/${dirname}")
endif()
I've added the check to #ulidtko's approach, so symlink doesn't overridden on every rebuild unconditionally:
install(CODE "if (NOT EXISTS ${link})
execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink \
${target} \
${link})
endif()" )
When I build, I create a symlink to the compile_commands.json. This helps clangd and vscode's intellisense.
Here's a full working example of what worked for me.
add_custom_command is what creates the symlink.
cmake_minimum_required(VERSION "3.1.0")
# project name
project("a-s-i-o")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -std=c++17 -pthread -Wall -Wextra -Werror -pedantic -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -Wno-unused-private-field"
)
# Create `compile_commands.json` file.
#
# This is required by `clangd` to find the header files. For this to work, this
# file must be in the root of the project. Therefore, we create a symbolic link in the root that
# points to the `compile_commands.json` file created by CMake in the `build` directory.
#
# This specific commands creates the file in the build directory. The command `add_custom_command`
# creates the symbolic link in the root directory.
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_executable(x main.cpp)
target_include_directories(x PUBLIC "/usr/local/include/asio-1.20.0/include")
# When target `x` is built, a symlink will be created to
# `build/compile_commands.json`. This is required for clangd to be able to find
# the header files included in the project.
add_custom_command(TARGET x
COMMAND ${CMAKE_COMMAND} -E create_symlink "${CMAKE_CURRENT_BINARY_DIR}/compile_commands.json" "../compile_commands.json"
DEPENDS compile_commands.json
VERBATIM ON
)
# Print success message to the console
add_custom_command(TARGET x POST_BUILD
COMMAND echo "Created symlink pointing to `compile_commands.json`"
DEPENDS compile_commands.json
VERBATIM ON
)
I've struggled with this a few different ways with the above responses in order to install '.so.#' files that refer to other '.so.#.#' files. I've had success by not introducing a link to the file, but by installing the '.so.#.#' file as the '.so.#' file.
I.e. instead of
install(
FILES
.../libmpi.so.12.0
DESTINATION lib
)
install(CODE
"EXECUTE_PROCESS( ${CMAKE_COMMAND} -E create_symlink lib/libmpi.so.12.0 lib/libmpi.so.12)")
Which didn't quite work for me even. I have instead had success by doing this.
install(FILES
.../libmpi.so.12.0
RENAME libmpi.so.12
DESTINATION lib
)
Not 'exactly' the same, but sufficient. Don't defeat the problem, solve the problem.
If what you are looking for is links for your executables and library files based on version numbers you can let CMake take care of that for you by setting the appropriate properties SOVERSION and VERSION on your targets:
The following CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(version_links C)
add_library(mylib SHARED lib.c)
set_target_properties(mylib PROPERTIES
PUBLIC_HEADER lib.h
VERSION 3.2.1
SOVERSION 3.2
)
add_executable(exec exec.c)
target_link_libraries(exec PRIVATE mylib)
set_target_properties(exec PROPERTIES
VERSION 3.2.1
)
install(TARGETS mylib exec)
produces the following tree inside CMAKE_INSTALL_PREFIX:
${CMAKE_INSTALL_PREFIX}
├── bin
│ ├── exec -> exec-3.2.1
│ └── exec-3.2.1
├── include
│ └── lib.h
└── lib
├── libmylib.so -> libmylib.so.3.2
├── libmylib.so.3.2 -> libmylib.so.3.2.1
└── libmylib.so.3.2.1
References:
https://cmake.org/cmake/help/latest/command/set_target_properties.html
https://cmake.org/cmake/help/latest/manual/cmake-properties.7.html#target-properties
https://cmake.org/cmake/help/latest/prop_tgt/SOVERSION.html
Related
I specifically get an undefined reference when I call Mix_OpenAudio(...). I am able to include 'SDL_mixer.h' just fine.
This is my CMakeList.txt. Sorry about all the other packages included. I'll include the FindSDL_MIXER.cmake as well.
I am able to create SDL_Mixer data types just fine as well. I installed SDL_Mixer using apt-get install.
PROJECT(Window)
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/CMakeModules")
add_definitions( -DMAGICKCORE_QUANTUM_DEPTH=16 )
add_definitions( -DMAGICKCORE_HDRI_ENABLE=0 )
FIND_PACKAGE(OpenGL REQUIRED)
FIND_PACKAGE(SDL2 REQUIRED)
FIND_PACKAGE(GLEW REQUIRED)
FIND_PACKAGE(GLM REQUIRED)
FIND_PACKAGE(Bullet REQUIRED)
FIND_PACKAGE(ASSIMP REQUIRED)
FIND_PACKAGE(ImageMagick COMPONENTS Magick++ REQUIRED )
FIND_PACKAGE(SDL_MIXER)
SET(CXX11_FLAGS "-std=gnu++11 -lassimp")
SET(CDEBUG_FLAGS -g)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX11_FLAGS} ${CDEBUG_FLAGS}")
SET(TARGET_LIBRARIES "${OPENGL_LIBRARY} ${SDL2_LIBRARY} ${ASSIMP_LIBRARIES}")
# Find where Magick++-config lives
IF(UNIX)
ADD_DEFINITIONS(-DUNIX)
ENDIF(UNIX)
IF(NOT APPLE)
IF(GLEW_FOUND)
INCLUDE_DIRECTORIES(${GLEW_INCLUDE_DIRS})
LINK_LIBRARIES(${GLEW_LIBRARIES})
ENDIF(GLEW_FOUND)
IF(ASSIMP_FOUND)
INCLUDE_DIRECTORIES(${ASSIMP_INCLUDE_DIRS})
LINK_LIBRARIES(${ASSIMP_LIBRARIES})
ENDIF(ASSIMP_FOUND)
ENDIF(NOT APPLE)
INCLUDE_DIRECTORIES(
"${PROJECT_SOURCE_DIR}/include"
${SDL2_INCLUDE_DIR}
${GLM_INCLUDE_DIRS}
${ASSIMP_INCLUDE_DIRS}
${ImageMagick_INCLUDE_DIRS}
${BULLET_INCLUDE_DIRS}
)
# Copy shaders, models, and default config
# FILE(COPY src/shaders DESTINATION .)
# FILE(COPY models DESTINATION .)
# FILE(COPY textures DESTINATION .)
# FILE(COPY config.json DESTINATION .)
# Set Includes
SET(INCLUDES ${PROJECT_SOURCE_DIR}/include)
INCLUDE_DIRECTORIES(${INCLUDES} ${ASSIMP_INCLUDE_DIRS} ${ImageMagick_INCLUDE_DIRS} ${BULLET_INCLUDE_DIRS})
# Set sources
FILE(GLOB_RECURSE SOURCES "src/*.cpp")
ADD_EXECUTABLE(${PROJECT_NAME} ${SOURCES})
add_custom_target("${PROJECT_NAME}_SUCCESSFUL" ALL
DEPENDS ${PROJECT_NAME}
COMMAND ${CMAKE_COMMAND} -E echo ""
COMMAND ${CMAKE_COMMAND} -E echo "====================="
COMMAND ${CMAKE_COMMAND} -E echo " Compile complete!"
COMMAND ${CMAKE_COMMAND} -E echo "====================="
COMMAND ${CMAKE_COMMAND} -E echo "${CMAKE_CURRENT_BINARY_DIR}"
)
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${OPENGL_LIBRARY} ${SDL2_LIBRARY} ${ASSIMP_LIBRARY} ${ImageMagick_LIBRARIES} ${BULLET_LIBRARIES} ${SDL_MIXER_LIBRARY})
Now here is FindSDL_MIXER.cmake.
#
# Find SDL_MIXER
#
# Additional modules
include(FindPackageHandleStandardArgs)
# Find include files
find_path(
SDL_MIXER_INCLUDE_DIR
PATHS
/usr/include
/usr/local/include
/sw/include
/opt/local/include
${SDL_MIXER_ROOT_DIR}/include
DOC "The directory where SDL_mixer.h resides")
# Handle REQUIRD argument, define *_FOUND variable
#find_package_handle_standard_args(SDL_MIXER_INCLUDE_DIR)
# Hide some variables
mark_as_advanced(SDL_MIXER_INCLUDE_DIR)
It's because the FindSDL_MIXER.cmake isn't setting the library variable. The cmake 3.13 has an updated FindSDL_mixer.cmake that sets this correctly you can use it as an example. Specifically you need a line like this in FindSDL_MIXER.cmake to find the library and set the variable.
find_library(SDL_MIXER_LIBRARY
NAMES SDL2_mixer
HINTS
ENV SDLMIXERDIR
ENV SDLDIR
PATH_SUFFIXES lib
)
I've also noticed that SDL_MIXER_INCLUDE_DIR does not appear to be used in the CMakeLists.txt for the target. It just happens that it is probably located in the same place as some of the other header files being included.
Also you probably want to rename the file to FindSDL2_MIXER.cmake and change the corresponding FIND_PACKAGE(SDL2_MIXER). I'm not entirely sure if the posted FindSDL_MIXER.cmake is being used because it looks like the find_path is not syntactically correct, it's missing the name of the file to be found.
I downloaded nitroshare.tar.gz file as i want to install it on my Kali Linux.
I followed up instructions as said on there github page to install it & downloaded all the packages that are required for the installation.
When i use cmake command it is showing me this error:
Unknown cmake command qt5_wrap_ui
Here is CMakeList.txt file:
cmake_minimum_required(VERSION 3.7)
configure_file(config.h.in "${CMAKE_CURRENT_BINARY_DIR}/config.h")
set(SRC
application/aboutdialog.cpp
application/application.cpp
application/splashdialog.cpp
bundle/bundle.cpp
device/device.cpp
device/devicedialog.cpp
device/devicelistener.cpp
device/devicemodel.cpp
icon/icon.cpp
icon/trayicon.cpp
settings/settings.cpp
settings/settingsdialog.cpp
transfer/transfer.cpp
transfer/transfermodel.cpp
transfer/transferreceiver.cpp
transfer/transfersender.cpp
transfer/transferserver.cpp
transfer/transferwindow.cpp
util/json.cpp
util/platform.cpp
main.cpp
)
if(WIN32)
set(SRC ${SRC} data/resource.rc)
endif()
if(APPLE)
set(SRC ${SRC}
data/icon/nitroshare.icns
transfer/transferwindow.mm
)
set_source_files_properties("data/icon/nitroshare.icns" PROPERTIES
MACOSX_PACKAGE_LOCATION Resources
)
endif()
if(QHttpEngine_FOUND)
set(SRC ${SRC}
api/apihandler.cpp
api/apiserver.cpp
)
endif()
if(APPINDICATOR_FOUND)
set(SRC ${SRC}
icon/indicatoricon.cpp
)
endif()
qt5_wrap_ui(UI
application/aboutdialog.ui
application/splashdialog.ui
device/devicedialog.ui
settings/settingsdialog.ui
transfer/transferwindow.ui
)
# Update the TS files from the source directory
file(GLOB TS data/ts/*.ts)
add_custom_target(ts
COMMAND "${Qt5_LUPDATE_EXECUTABLE}"
-silent
-locations relative
"${CMAKE_CURRENT_SOURCE_DIR}"
-ts ${TS}
)
# Create a directory for the compiled QM files
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/data/qm")
# Create commands for each of the translation files
set(QM_XML)
set(QM)
foreach(_ts_path ${TS})
get_filename_component(_ts_name "${_ts_path}" NAME)
get_filename_component(_ts_name_we "${_ts_path}" NAME_WE)
set(_qm_name "${_ts_name_we}.qm")
set(_qm_path "${CMAKE_CURRENT_BINARY_DIR}/data/qm/${_qm_name}")
set(QM_XML "${QM_XML}<file>qm/${_qm_name}</file>")
list(APPEND QM "${_qm_path}")
add_custom_command(OUTPUT "${_qm_path}"
COMMAND "${Qt5_LRELEASE_EXECUTABLE}"
-compress
-removeidentical
-silent
"${_ts_path}"
-qm "${_qm_path}"
COMMENT "Generating ${_ts_name}"
)
endforeach()
# Create a target for compiling all of the translation files
add_custom_target(qm DEPENDS ${QM})
# Configure the i18n resource file
set(QRC_QM "${CMAKE_CURRENT_BINARY_DIR}/data/resource_qm.qrc")
configure_file(data/resource_qm.qrc.in "${QRC_QM}")
qt5_add_resources(QRC
data/resource.qrc
"${QRC_QM}"
)
add_executable(nitroshare WIN32 MACOSX_BUNDLE ${SRC} ${UI} ${QRC})
target_compile_features(nitroshare PRIVATE
cxx_lambdas
cxx_nullptr
cxx_strong_enums
cxx_uniform_initialization
)
# In order to support Retina, a special tag is required in Info.plist.
if(APPLE)
set_target_properties(nitroshare PROPERTIES
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in"
MACOSX_BUNDLE_ICON_FILE "nitroshare.icns"
MACOSX_BUNDLE_GUI_IDENTIFIER "com.NathanOsman.NitroShare"
MACOSX_BUNDLE_BUNDLE_NAME "NitroShare"
)
target_link_libraries(nitroshare "-framework ApplicationServices")
endif()
target_link_libraries(nitroshare Qt5::Widgets Qt5::Network Qt5::Svg)
if(Qt5WinExtras_FOUND)
target_link_libraries(nitroshare Qt5::WinExtras)
endif()
if(Qt5MacExtras_FOUND)
target_link_libraries(nitroshare Qt5::MacExtras)
endif()
if(QHttpEngine_FOUND)
target_link_libraries(nitroshare QHttpEngine)
endif()
if(APPINDICATOR_FOUND)
target_include_directories(nitroshare PRIVATE ${APPINDICATOR_INCLUDE_DIRS})
target_link_libraries(nitroshare ${APPINDICATOR_LIBRARIES})
endif()
include(GNUInstallDirs)
install(TARGETS nitroshare
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
BUNDLE DESTINATION .
)
if(WIN32)
# If windeployqt is available, use it immediately after the build completes to
# ensure that the dependencies are available.
include(DeployQt)
if(WINDEPLOYQT_EXECUTABLE)
windeployqt(nitroshare)
endif()
# If QHttpEngine was used, include it in the installer
if(QHttpEngine_FOUND)
add_custom_command(TARGET nitroshare POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E
copy_if_different "$<TARGET_FILE:QHttpEngine>" "$<TARGET_FILE_DIR:nitroshare>"
COMMENT "Copying QHttpEngine..."
)
endif()
# If Inno Setup is available, provide a target for building an EXE installer.
find_package(InnoSetup)
if(INNOSETUP_EXECUTABLE)
configure_file(dist/setup.iss.in "${CMAKE_CURRENT_BINARY_DIR}/setup.iss")
add_custom_target(exe
COMMAND "${INNOSETUP_EXECUTABLE}"
/Q
/DTARGET_FILE_NAME="$<TARGET_FILE_NAME:nitroshare>"
"${CMAKE_CURRENT_BINARY_DIR}/setup.iss"
DEPENDS nitroshare
COMMENT "Building installer..."
)
endif()
endif()
if(APPLE)
# If QHttpEngine was used, copy the dynlib over and correct its RPATH
if(QHttpEngine_FOUND)
add_custom_command(TARGET nitroshare POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E
make_directory "$<TARGET_FILE_DIR:nitroshare>/../Frameworks"
COMMAND "${CMAKE_COMMAND}" -E
copy_if_different
"$<TARGET_FILE:QHttpEngine>"
"$<TARGET_FILE_DIR:nitroshare>/../Frameworks/$<TARGET_SONAME_FILE_NAME:QHttpEngine>"
COMMAND install_name_tool
-change
"$<TARGET_SONAME_FILE_NAME:QHttpEngine>"
"#rpath/$<TARGET_SONAME_FILE_NAME:QHttpEngine>"
"$<TARGET_FILE:nitroshare>"
COMMENT "Copying QHttpEngine and correcting RPATH..."
)
endif()
# If macdeployqt is available, use it immediately after the build completes to
# copy the Qt frameworks into the application bundle.
include(DeployQt)
if(MACDEPLOYQT_EXECUTABLE)
macdeployqt(nitroshare)
endif()
# Create a target for building a DMG that contains the application bundle and a
# symlink to the /Applications folder.
set(sym "${CMAKE_BINARY_DIR}/out/Applications")
set(dmg "${CMAKE_BINARY_DIR}/nitroshare-${PROJECT_VERSION}-osx.dmg")
add_custom_target(dmg
COMMAND rm -f "${sym}" "${dmg}"
COMMAND ln -s /Applications "${sym}"
COMMAND hdiutil create
-srcfolder "${CMAKE_BINARY_DIR}/out"
-volname "${PROJECT_NAME}"
-fs HFS+
-size 30m
"${dmg}"
DEPENDS nitroshare
COMMENT "Building disk image..."
)
endif()
# On Linux, include the icons, manpage, .desktop file, and file manager extensions when installing.
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/dist/icons"
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}"
)
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/dist/nitroshare.1"
DESTINATION "${CMAKE_INSTALL_MANDIR}/man1"
)
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/dist/nitroshare.desktop"
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications"
)
# Configure and install Python extensions for Nautilus-derived file managers
foreach(_file_manager Nautilus Nemo Caja)
string(TOLOWER ${_file_manager} _file_manager_lower)
set(_py_filename "${CMAKE_CURRENT_BINARY_DIR}/dist/nitroshare_${_file_manager_lower}.py")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/dist/nitroshare.py.in" "${_py_filename}")
install(FILES "${_py_filename}"
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/${_file_manager_lower}-python/extensions"
RENAME nitroshare.py
)
endforeach()
endif()
CMake doesn't know the function qt5_wrap_ui because you did not import Qt5 or any of the functions it defines.
Before calling qt5_wrap_ui, add this:
find_package(Qt5 COMPONENTS Widgets REQUIRED)
I used this line
find_package(Qt5Widgets)
In my Cmakelist.txt file
I found this on their Document Page of Qt5
I am trying to create a custom target that just copies a library to the LIBRARY_OUTPUT_PATH. Below is my CMakeLists.txt.
The contents of the MYCOMPONENT folder is mylib.so and CMakeLists.txt.
cmake_minimum_required(VERSION 2.8)
project( MYCOMPONENT )
add_custom_target( MYTARGET
COMMAND ${CMAKE_COMMAND} -E copy ./mylib.so ${LIBRARY_OUTPUT_PATH} )
I do the following:
cd MYCOMPFOLDER
mkdir build_debug
cd build_debug
cmake -DLIBRARY_OUTPUT_PATH=<mycompfolderfullpath>/build_debug/bin ..
Now I do an ls of build_debug and I get:
CMakeCache.txt CMakeFiles cmake_install.cmake Makefile
ls CMakeFiles/
3.5.1 CMakeTmp Makefile2
cmake.check_cache feature_tests.bin Makefile.cmake
CMakeDirectoryInformation.cmake feature_tests.c progress.marks
CMakeOutput.log feature_tests.cxx TargetDirectories.txt
CMakeRuleHashes.txt MYCOMPONENT.dir
ls CMakeFiles/MYCOMPONENT.dir/
build.make cmake_clean.cmake DependInfo.cmake progress.make
From build_debug, "find . -name mylib.so" is nowhere, so of course the "make MYTARGET" fails. How do I get cmake to properly handle mylib.so? This is a third party library, we don't build it, the target is to make sure it gets copied to the LIBRARY_OUTPUT_PATH from this folder so other components can link against it from there.
Don't use relative paths in -E copy part. Variables CMAKE_SOURCE_DIR, CMAKE_CURRENT_SOURCE_DIR, CMAKE_BINARY_DIR and CMAKE_CURRENT_BINARY_DIR are at your disposal.
When using CMake for cross compiling, one generally specifies a toolchain file via the CMAKE_TOOLCHAIN_FILE option. In GNU terminology, one can specify the host architecture toolset using this file. However, one can generally not expect to be able to execute anything built with this toolchain. So often enough, some build tools need to be compiled for the build architecture.
Consider the following setup. I have two source files genfoo.c and bar.c. During build, genfoo.c needs to be compiled and run. Its output needs to be written to foo.h. Then I can compile bar.c, which #include "foo.h". Since CMake defaults to using the host architecture toolchain, the instructions for bar.c are easy. But how do I tell it to use the build architecture toolchain for compiling genfoo.c? Simply saying add_executable(genfoo genfoo.c) will result in using the wrong compiler.
CMake can only handle one compiler at a time. So - if you don't go the long way to set up the other compiler as a new language - you will end up with two configuration cycles.
I see the following approaches to automate this process:
Taking the example "CMake Cross Compiling - Using executables in the build created during the build?" from the CMake pages as a starting point I'll get:
CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(FooBarTest)
# When crosscompiling import the executable targets
if (CMAKE_CROSSCOMPILING)
set(IMPORT_PATH "IMPORTFILE-NOTFOUND" CACHE FILEPATH "Point it to the export file path from a native build")
file(TO_CMAKE_PATH "${IMPORT_PATH}" IMPORT_PATH_CMAKE)
include(${IMPORT_PATH_CMAKE}/genfooTargets.cmake)
# Then use the target name as COMMAND, CMake >= 2.6 knows how to handle this
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/foo.h
COMMAND genfoo
)
add_executable(bar bar.cpp ${CMAKE_CURRENT_BINARY_DIR}/foo.h)
target_include_directories(bar PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
endif()
# Only build the generator if not crosscompiling
if (NOT CMAKE_CROSSCOMPILING)
add_executable(genfoo genfoo.cpp)
export(TARGETS genfoo FILE "${CMAKE_CURRENT_BINARY_DIR}/genfooTargets.cmake")
endif()
Then using a script like:
build.sh
#!/bin/bash
if [ ! -d hostBuild ]; then
cmake -E make_directory hostBuild
cmake -E chdir hostBuild cmake ..
fi
cmake --build hostBuild
if [ ! -d crossBuild ]; then
cmake -E make_directory crossBuild
cmake -E chdir crossBuild cmake .. -DIMPORT_PATH=${PWD}/hostBuild -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake
fi
cmake --build crossBuild
I'll get the desired results by calling ./build.sh.
Splitting the CMakeLists.txt and maybe even replace the export()/include() with something where I know the output path of my build tools e.g. by using CMAKE_RUNTIME_OUTPUT_DIRECTORY would simplify things:
CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(FooBarTest)
# Then use the target name as COMMAND. CMake >= 2.6 knows how to handle this
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/foo.h
COMMAND genfoo
)
add_executable(bar bar.cpp ${CMAKE_CURRENT_BINARY_DIR}/foo.h)
target_include_directories(bar PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
buildTools/CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(BuildTools)
add_executable(genfoo genfoo.cpp)
build.sh
#!/bin/bash
if [ ! -d crossBuild ]; then
cmake -E make_directory crossBuild
cmake -E chdir crossBuild cmake .. -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake
fi
if [ ! -d hostBuild ]; then
cmake -E make_directory hostBuild
cmake -E chdir hostBuild cmake ../buildTools -DCMAKE_RUNTIME_OUTPUT_DIRECTORY:PATH=${PWD}/crossBuild
fi
cmake --build hostBuild
cmake --build crossBuild
References
Making a CMake library accessible by other CMake packages automatically
CMake build multiple targets in different build directories
How do I make CMake output into a 'bin' dir?
It is possible to do that completely within CMake.
The trick is to run a separate CMake configuring stage within its own space, silently dismissing every crosscompiling setting and using the host's default toolchain, then import the generated outputs into it's parent, crosscompiling build.
First part:
set(host_tools_list wxrc generate_foo)
if(CMAKE_CROSSCOMPILING)
# Pawn off the creation of the host utilities into its own dedicated space
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/host_tools)
file(TO_NATIVE_PATH ${CMAKE_COMMAND} native_cmake_command)
file(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR} native_cmake_current_source_dir)
execute_process(
COMMAND "${native_cmake_command}" "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" "${native_cmake_current_source_dir}"
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/host_tools
)
add_custom_target(host_tools
COMMAND ${CMAKE_COMMAND} --build . --target host_tools --config $<CONFIG>
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/host_tools
)
include(${CMAKE_CURRENT_BINARY_DIR}/host_tools/host_tools.cmake)
foreach(tgt IN ITEMS ${host_tools_list})
add_dependencies(host${tgt} host_tools)
endforeach()
else()
# Add an empty target, host tools are built inplace
add_custom_target(host_tools
DEPENDS ${host_tools_list}
)
endif()
... then you add the usual add_executable and whatever ...
At the end:
if(NOT CMAKE_CROSSCOMPILING)
foreach(tgt IN ITEMS ${host_tools_list})
add_executable(host${tgt} ALIAS ${tgt})
endforeach()
export(TARGETS ${host_tools_list} NAMESPACE host FILE host_tools.cmake)
endif()
When it crosscompiles, it pawns off the creation of the host-run tools into its own dedicated space, and imports the targets as "hostwxrc" and "hostgenerate_foo", with a dependency on generating the host_tools themselves .
When it doesn't crosscompile, it builds wxrc and generate_foo as-is, and aliases them to hostwxrc and hostgenerate_foo.
After this, when you use $<TARGET_FILE:wxrc>, you refer to the wxrc built for the target platform, and $<TARGET_FILE:hostwxrc> refers to the wxrc built for the host platform, regardless whether they are the same or not.
I have an application with a mildly complicated build process, and as a bit of a newb to CMake, I was wondering if anyone could provide me with any pointers.
At preset, the application consists of a single executable, built from a source tree provided in the src and include folders.
It requires a few libraries to work, the big ones being Boost and Python. Python is embedded in the application, and Boost requires knowledge of the custom python install at compile time. I also use Qt, but I'm just linking against the system Qt for this.
What I'd like to have at the end is a stage folder, containing the compiled executable, and a lib folder with the required boost and python libraries.
At present, I have a single CMakeLists.txt file, and I am using ExternalProject to build Boost and Python from bzipped tarballs of their source. It gets a little messy where I copy out the compiled libs from the prefixed install directories.
Things are working, but I have a feeling I'm doing things very backwards. I sometimes see multiple CMakeLists in nested subdirectories but don't know how they would relate to my project. Would anyone who has worked on similarly scoped projects be able to weigh in and give me some pointers?
I should add that I hope to include Windows as a platform in the near future, and that things are currently running on Linux.
Note: This is my current CMakeLists.txt, I realise that boost isn't configured and that things aren't fully moved to the stage folder. I have been doing this manually, but I wanted to ask before I dig myself much deeper :)
Thanks!
CmakeList.txt
cmake_minimum_required(VERSION 2.6)
set(CMAKE_BUILD_TYPE Debug)
set(PROJ_NAME "mwave")
project(${PROJ_NAME})
include_directories("include")
include(ExternalProject)
# Add cmake dir to cmake module path so custom find modules will work
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH})
#Build Python via External Project
ExternalProject_Add(
Python
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/external/python
URL ${CMAKE_CURRENT_SOURCE_DIR}/extern/Python-3.3.0.tar.bz2
URL_MD5 2dbff60afed2b5f66adf6f77dac9e139
UPDATE_COMMAND ""
CONFIGURE_COMMAND ./configure -q --prefix=${CMAKE_CURRENT_BINARY_DIR}/external/python --enable-shared
BUILD_COMMAND make
BUILD_IN_SOURCE 1
INSTALL_COMMAND make install
)
# Manually copy the compiled python files and dirs to our stage folder
add_custom_command(TARGET Python PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_BINARY_DIR}/external/python/lib/pkgconfig
${CMAKE_CURRENT_BINARY_DIR}/stage/lib/pkgconfig)
add_custom_command(TARGET Python PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_BINARY_DIR}/external/python/lib/python3.3
${CMAKE_CURRENT_BINARY_DIR}/stage/lib/python3.3)
add_custom_command(TARGET Python PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_BINARY_DIR}/external/python/lib/libpython3.so
${CMAKE_CURRENT_BINARY_DIR}/stage/lib/libpython3.so)
add_custom_command(TARGET Python PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_BINARY_DIR}/external/python/lib/libpython3.3m.so.1.0
${CMAKE_CURRENT_BINARY_DIR}/stage/lib/libpython3.3m.so.1.0)
add_custom_command(TARGET Python PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E create_symlink
libpython3.3m.so.1.0
${CMAKE_CURRENT_BINARY_DIR}/stage/lib/libpython3.3m.so)
#Python
set(PYTHON_INCLUDE_DIRS "${CMAKE_CURRENT_BINARY_DIR}/external/python/include/python3.3m")
set(PYTHON_LIBRARIES "${CMAKE_CURRENT_BINARY_DIR}/external/python/lib/libpython3.3m.so" "pthread" "m" "util" "readline")
#Build boost via External Project
ExternalProject_Add(
Boost
DEPENDS Python
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/external/boost
URL ${CMAKE_CURRENT_SOURCE_DIR}/extern/boost_1_51_0_mwave.tar.bz2
URL_MD5 fe203a243e451b4dd4754c7b283b1db9
UPDATE_COMMAND ./bootstrap.sh --with-libraries=python,system,thread,program_options
CONFIGURE_COMMAND ""
BUILD_COMMAND ./b2
BUILD_IN_SOURCE 1
INSTALL_COMMAND ""
)
#Boost (workaround until external project is working)
set(Boost_INCLUDE_DIRS "/opt/mwave/include")
set(Boost_LIBRARIES "/opt/mwave/lib/libboost_python3.so" "/opt/mwave/lib/libboost_program_options.so")
#OpenImageIO
set(OIIO_PATH "/opt/mwave/oiio/dist/linux64.debug")
find_package(OIIO REQUIRED)
#Qt4
find_package(Qt4 REQUIRED)
set(QT_USE_QTOPENGL TRUE)
include(${QT_USE_FILE})
add_definitions(${QT_DEFINITIONS})
#OpenGL
find_package(OpenGL REQUIRED)
find_package(GLEW REQUIRED)
# Mwave app
set(HEADERS
"include/Application.h"
"include/ImageChannel.h"
"include/CompDag.h"
"include/Dag.h"
"include/Gui/DagView.h"
"include/Gui/DagScene.h"
"include/Gui/MainWindow.h"
"include/Gui/GLViewer.h"
"include/Gui/Nodes/GNodeEdge.h"
"include/Gui/Nodes/GNodeLabel.h"
"include/Gui/Nodes/GNodeCacheStatus.h"
"include/Gui/Nodes/GNode.h"
"include/Gui/Nodes/GRead.h"
"include/Gui/Nodes/GViewer.h"
"include/MwaveException.h"
"include/Nodes/Node.h"
"include/Nodes/Read.h"
"include/Nodes/Viewer.h"
"include/mwave.h"
"include/main.h"
"include/shaders.h"
)
set(QOBJECT_HEADERS
"include/Gui/QCompDag.h"
"include/Gui/QPythonEditor.h"
"include/Gui/ViewerWidget.h"
)
set(SOURCES
"src/Application.cpp"
"src/CompDag.cpp"
"src/main.cpp"
"src/mwave.cpp"
"src/Dag.cpp"
"src/Gui/DagView.cpp"
"src/Gui/DagScene.cpp"
"src/Gui/MainWindow.cpp"
"src/Gui/QPythonEditor.cpp"
"src/Gui/GLViewer.cpp"
"src/Gui/ViewerWidget.cpp"
"src/Gui/Nodes/GNode.cpp"
"src/Gui/Nodes/GNodeEdge.cpp"
"src/Gui/QCompDag.cpp"
"src/Nodes/Node.cpp"
"src/Nodes/Read.cpp"
"src/Nodes/Viewer.cpp"
)
QT4_WRAP_CPP(HEADERS_MOC ${QOBJECT_HEADERS})
## Compiler flags
if(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "-O2") ## Optimize
set(CMAKE_CXX_FLAGS "-O3") ## Optimize More
endif()
include_directories(${PYTHON_INCLUDE_DIRS}
${Boost_INCLUDE_DIRS}
${GLEW_INCLUDE_PATH}
${OPENGL_INCLUDE_DIR}
${OIIO_INCLUDE_DIR}
)
add_executable(mwave WIN32 ${HEADERS} ${HEADERS_MOC} ${SOURCES})
set_target_properties(mwave PROPERTIES OUTPUT_NAME mwave.bin)
target_link_libraries( mwave
${PYTHON_LIBRARIES}
${Boost_LIBRARIES}
${OIIO_LIBRARIES}
${GLEW_LIBRARY}
${OPENGL_LIBRARIES}
${QT_LIBRARIES})
cmake 2.6, which you are using, does not support ExternalProjects.
ExternalProjects are supported in cmake 2.8 series. Please move to cmake 2.8.
The below link clarifies
http://www.cmake.org/pipermail/cmake/2011-June/044993.html