I am trying to build QT based application with CMake and everything goes well enough. I followed this tutorial and I can build my application. Now I want to run npm run build before build using add_custom_command it does not seems to goes as expected.
The build process fails with
RCC: Error in 'tray-icon/systray.qrc': Cannot find file 'html/js/app.full.js'
AUTORCC: error: process for.build/Debug/x64/tray-icon/CMakeFiles/tray-icon.dir/qrc_systray.cpp failed:
RCC: Error in 'tray-icon/systray.qrc': Cannot find file 'html/js/app.full.js'
Can you tell me how to execute npm command before the build verification step on tray-icon/systray.qrc?
This is my cmake file
cmake_minimum_required(VERSION 2.8.11)
project(tray-icon)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
find_package(Qt5Widgets 5.5)
find_package(Qt5Qml 5.5)
find_package(Qt5WebEngine 5.5)
find_package(Qt5WebEngineCore 5.5)
find_package(Qt5WebEngineWidgets 5.5)
# generate rules for building source files from the resources
set(SOURCES tray-icon.cpp window.cpp systray.qrc)
set(CMAKE_VERBOSE_MAKEFILE 1)
#adds target
add_executable(tray-icon ${SOURCES})
# custom build command for javascript part of the application
add_custom_command (
TARGET "tray-icon"
PRE_BUILD COMMAND npm run build
)
# Find the QtWidgets library
target_link_libraries(tray-icon
Qt5::Widgets
Qt5::WebEngine
Qt5::WebEngineWidgets)
install(TARGETS tray-icon DESTINATION .)
PS: My final solution looks like this
# custom build command for javascript part of the application
add_custom_target(
tray-icon_automoc
)
add_custom_target (
npm-target
COMMAND cd ${PROJECT_SOURCE_DIR} && cd html && npm install && npm run build
)
You could try add_custom_target and add_dependencies.
add_custom_target (
npm-target
COMMAND npm run build
)
add_dependencies(tray-icon npm-target)
To overcome the issue mentioned in the comments you should be able to add dependency using AUTOGEN_TARGET_DEPENDS target property. It can be set instead to a list of dependencies for the _automoc target.
Related
Coming from other package managers I expect to be able to do the following on a project:
configure the required libraries (in a config file, e.g. CMakelists.txt or nuget's packages.config)
install/upgrade all the required libraries
This seems to not be possible with vcpkg and CMake:
I managed to set up vcpkg and CMakefile (linux):
# in ternimal
vcpkg install <package>
# CMakeLists.txt
find_package(nlohmann_json CONFIG REQUIRED)
target_link_libraries(<app> <package>)
This however requires to manually install all the required libraries (vcpkg install <package>).
How can I list the required packages and have vcpkg or CMake managed them as required (e.g. automatically install them if they are missing)?
I've recently spent some time working with CMake and vcpkg. I dislike the typical approach of managing vcpkg and its installed packages separately from my CMake projects, so I wrote some CMake scripts to handle vcpkg during the CMake build process.
The CMake script to install vcpkg is primarily a call to ExternalProject_Add.
# ./vcpkg_utilities.cmake (part 1)
include(ExternalProject)
function(get_vcpkg)
ExternalProject_Add(vcpkg
GIT_REPOSITORY https://github.com/microsoft/vcpkg.git
CONFIGURE_COMMAND ""
INSTALL_COMMAND ""
UPDATE_COMMAND ""
BUILD_COMMAND "<SOURCE_DIR>/bootstrap-vcpkg.sh"
)
ExternalProject_Get_Property(vcpkg SOURCE_DIR)
set(VCPKG_DIR ${SOURCE_DIR} PARENT_SCOPE)
set(VCPKG_DEPENDENCIES "vcpkg" PARENT_SCOPE)
endfunction()
The script to have vcpkg install a package requires a custom command and target.
# ./vcpkg_utilities.cmake (part 2)
function(vcpkg_install PACKAGE_NAME)
add_custom_command(
OUTPUT ${VCPKG_DIR}/packages/${PACKAGE_NAME}_x64-linux/BUILD_INFO
COMMAND ${VCPKG_DIR}/vcpkg install ${PACKAGE_NAME}:x64-linux
WORKING_DIRECTORY ${VCPKG_DIR}
DEPENDS vcpkg
)
add_custom_target(get${PACKAGE_NAME}
ALL
DEPENDS ${VCPKG_DIR}/packages/${PACKAGE_NAME}_x64-linux/BUILD_INFO
)
list(APPEND VCPKG_DEPENDENCIES "get${PACKAGE_NAME}")
set(VCPKG_DEPENDENCIES ${VCPKG_DEPENDENCIES} PARENT_SCOPE)
endfunction()
Getting the main project linked to vcpkg appropriately (via toolchain file) requires vcpkg to be available when the main project is configured, so the main project is configured as an ExternalProject.
# /CMakeLists.txt
cmake_minimum_required(VERSION 3.17)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
project(top LANGUAGES CXX)
include(vcpkg_utilities)
get_vcpkg()
vcpkg_install(nlohmann_json)
include(ExternalProject)
set_property(DIRECTORY PROPERTY EP_BASE main_projectname)
ExternalProject_Add(main_projectname
DEPENDS ${VCPKG_DEPENDENCIES}
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/src
INSTALL_COMMAND ""
CMAKE_ARGS --no-warn-unused-cli;
-DCMAKE_TOOLCHAIN_FILE=${VCPKG_DIR}/scripts/buildsystems/vcpkg.cmake
)
Using this structure, CMake will manage the installation of both vcpkg and packages maintained by it.
Note that this is a point solution. I have a more generalized template for this in my Github that covers Windows and Linux, and allows the use of a separately-maintained vcpkg instance.
I've done something similar in my projects using ExternalProject_Add.
Code will be similar to the below pattern (I've replaced my dependency with your)
find_package(nlohmann_json CONFIG) # make package not REQUIRED to avoid error...
if(NOT NLOHMANN_JSON_FOUND) # or whatever variable indicating _FOUND
...
# here ExternalProject_Add from https://github.com/nlohmann/json.git
# using specific tag...
...
ExternalProject_Get_Property(... INSTALL_DIR)
# create imported target here similar to nlohmann_json, as if we found it
...
else()
# We've found lib already installed... will use it...
...
endif()
Another option can be using FetchContent, but I haven't tried it yet.
I'd like to link my application with a prebuilt gRPC 1.30.
The directory structure that I want:
- project root
- external
+ gRPC
- src
... my source code here ...
I've built gRPC:
git clone --recurse-submodules -b v1.30.0 https://github.com/grpc/grpc
cmake -DgRPC_INSTALL=ON -DgRPC_BUILD_TESTS=OFF -DCMAKE_INSTALL_PREFIX="D:/grpc" -A x64 ..
cmake --build . --target install --config Release
The final structure of files produced in D:/grpc is weird... the topmost cmake folder contains protobuf-config.cmake and the grpc's one is in D:/grpc/lib/cmake/grpc. But I don't really care as long as it works.
This is in my project's CMakeLists.txt.
set(CMAKE_PREFIX_PATH ${EXTERNAL_DIRECTORY}
${EXTERNAL_DIRECTORY}/grpc/
${EXTERNAL_DIRECTORY}/grpc/cmake/
${CMAKE_PREFIX_PATH})
find_package(Protobuf CONFIG REQUIRED)
find_package(gRPC CONFIG REQUIRED)
add_executable(test main.cpp)
target_link_libraries(test PRIVATE grpc::grpc++)
And finally, this is the error that I'm getting when I try to build it:
Target "test" links to target "Threads::Threads" but the target was
not found. Perhaps a find_package() call is missing for an IMPORTED
target, or an ALIAS target is missing?
I'm stuck here.
I simply added:
find_package(Threads REQUIRED)
I want to use cmake execute_process, or similar, to get the git hash at build time not at configure time. I have it working and populating the git hash at configure time, but I would like to have it update at build time.
Here is an example of what I have to get the git hash at configure time.
(this is an example from Profession Cmake https://crascit.com/professional-cmake/)
foobar_version.cpp.in
std::string getFooBarGitHash()
{
return "#FooBar_GIT_HASH#";
}
CMakeLists.txt
find_package(Git REQUIRED)
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
RESULT_VARIABLE result
OUTPUT_VARIABLE FooBar_GIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(result)
message(FATAL_ERROR "Failed to get git hash: ${result}")
endif()
configure_file(foobar_version.cpp.in foobar_version.cpp #ONLY)
This works and populates a foobar_version.cpp file with the git hash as expected, but I want to have this run at build time. Is there a convenient way to do this?
I solved this problem by generating a custom version.h header file with a python script GitHashExtractor and the following snippet in my cmake file.
set(PROJECT_SOURCES
[... your files ...]
GitHashExtractor/firmwareVersion.py
GitHashExtractor/version.h
)
# START github.com/ni-m/gitHashExtractor
set(VERSION_FILE "./GitHashExtractor/version.h")
set(VERSION_PYTHON "./GitHashExtractor/firmwareVersion.py")
set_property(SOURCE ${VERSION_FILE} PROPERTY SKIP_AUTOGEN ON) #Qt Framework
add_custom_command(OUTPUT ${VERSION_FILE}
COMMAND python ${VERSION_PYTHON} [ARGS] [Qt.h]
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Creating ${VERSION_FILE}"
)
# target GitHashExtractor is always built
add_custom_target(GitHashExtractor ALL DEPENDS ${VERSION_FILE})
# END github.com/ni-m/gitHashExtractor
The last line makes sure to always build this target.
This snipped is tested unter the Qt framework with cmake. You may want to remove the set_property(SOURCE ${VERSION_FILE} PROPERTY SKIP_AUTOGEN ON) line if you're not using Qt.
I am looking to automate the compilation of a given c++ library (in this case, cpprestsdk). I am looking to build the library using cmake.
Like many other projects, this has dependencies. Namely, it requires OpenSSL, Boost, ZLIB and websocketpp.
I'm looking for a way in which I can provide a CMakeLists file that will fetch and build all of the prerequisites and allow cpprestsdk to build without necessarily having the libraries pre-installed on a computer.
The way I am fetching cpprestsdk is as follows:
FetchContent_Declare(cpprestsdk
GIT_REPOSITORY https://github.com/microsoft/cpprestsdk.git
GIT_TAG master)
FetchContent_GetProperties(cpprestsdk)
if(NOT cpprestsdk_POPULATED)
FetchContent_Populate(cpprestsdk)
add_subdirectory(${cpprestsdk_SOURCE_DIR} ${cpprestsdk_BINARY_DIR})
endif()
Of course, since this does not populate it's own dependencies, it will not be able to build. Although websocketpp has an embedded release, it is only used if not found. For this reason, I will use it as an example.
FetchContent_Declare(websocketpp
GIT_REPOSITORY https://github.com/zaphoyd/websocketpp.git
GIT_TAG master
)
FetchContent_GetProperties(websocketpp)
if(NOT websocketpp_POPULATED)
FetchContent_Populate(websocketpp)
add_subdirectory(${cpprestsdk_SOURCE_DIR} ${cpprestsdk_BINARY_DIR})
endif()
This will fetch websocketpp, but as it is only the configure stage, nothing will be built. For this reason, cpprestsdk will not be able to find the dependency and instead fallback to the embedded release.
My question is therefore: Is there any way I can force a build after fetching a package? Or remove the library requirement during the configure stage? I think this may be possible using ExternalProject instead, however I'm not too sure how I would set that up either.
I use a subroutine to fetch the source code, then use execute_process to build and install the target.
After this, even find_package can locate the library.
Fetch subroutine:
cmake_minimum_required(VERSION 3.16)
# suppress CMP0042
cmake_policy(SET CMP0042 NEW)
set(CMAKE_MACOSX_RPATH 1)
# this must exist or cmake will complain about not presenting a project()
project(${PK_NAME}_download NONE)
include(FetchContent)
FetchContent_Declare(
${PK_NAME}
GIT_REPOSITORY "${PK_GIT}"
GIT_TAG "${PK_GIT_TAG}"
PREFIX "${#PK_NAME#_ROOT}"
)
if(NOT ${PK_NAME}_POPULATED)
FetchContent_MakeAvailable(${PK_NAME})
endif()
Download, build and install:
# copy the template to place
configure_file(${CMAKE_CURRENT_LIST_DIR}/FetchLib.cmake ${${PK_NAME}_ROOT}/CMakeLists.txt)
# download source code
execute_process(
COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" -DCMAKE_INSTALL_PREFIX:PATH=${${PK_NAME}_ROOT} ${SUB_PARAM}
.
RESULT_VARIABLE result
WORKING_DIRECTORY ${${PK_NAME}_ROOT}
)
if(result)
message(FATAL_ERROR "CMake step for ${PK_NAME} failed: ${result}")
endif()
set(CMAKE_INSTALL_PREFIX "${${PK_NAME}_ROOT}")
# build
execute_process(
COMMAND ${CMAKE_COMMAND} --build .
RESULT_VARIABLE result
WORKING_DIRECTORY ${${PK_NAME}_ROOT}
)
if(result)
message(FATAL_ERROR "Build step for ${PK_NAME} failed: ${result}")
endif()
# install
execute_process(
COMMAND ${CMAKE_COMMAND} --install .
RESULT_VARIABLE result
WORKING_DIRECTORY ${${PK_NAME}_ROOT}
)
if(result)
message(FATAL_ERROR "Install step for ${PK_NAME} failed: ${result}")
endif()
You are looking for ExternalProject_add
I often use this approach to build externals projects, it provides me automatic control over external projects that are inserted in my projects.
You can include like this:
ExternalProject_add(cpprestsdkDownload
GIT_REPOSITORY https://github.com/microsoft/cpprestsdk
GIT_TAG master
CMAKE_ARGS
-DWERROR:BOOL="0"
-DBUILD_SAMPLES:BOOL="0"
-DBUILD_TESTS:BOOL="0" )
You can use your custom configure,build and install commands to ! Have Fun !
Is there are proper way to find a library (via FindPackage()) which was built with ExternalProject_Add()?
The problem is that CMake cannot find the library at CMake-time because the external library gets build at compile time. I know that it is possible to combine these two CMake function when building the library and the project in a superbuild but I want to use it in a normal CMake project.
In fact I would like to build VTK 6 with ExternalProject_Add and find it with FindPackage all inside my CMake project.
there is a way to do this. but it´s kind of hackish.
you basically add a custom target, that reruns cmake during build.
you will have to try this in a small test project, to decide if it works for you
find_package(Beaengine)
############################################
#
# BeaEngine
#
include(ExternalProject)
externalproject_add(BeaEngine
SOURCE_DIR ${PROJECT_SOURCE_DIR}/beaengine
SVN_REPOSITORY http://beaengine.googlecode.com/svn/trunk/
CMAKE_ARGS -DoptHAS_OPTIMIZED=TRUE -DoptHAS_SYMBOLS=FALSE -DoptBUILD_64BIT=FALSE -DoptBUILD_DLL=FALSE -DoptBUILD_LITE=FALSE
INSTALL_COMMAND ""
)
if(NOT ${Beaengine_FOUND})
#rerun cmake in initial build
#will update cmakecache/project files on first build
#so you may have to reload project after first build
add_custom_target(Rescan ${CMAKE_COMMAND} ${CMAKE_SOURCE_DIR} DEPENDS BeaEngine)
else()
#Rescan becomes a dummy target after first build
#this prevents cmake from rebuilding cache/projects on subsequent builds
add_custom_target(Rescan)
endif()
add_executable(testapp testapp.cpp )
add_dependencies(testapp Rescan)
if(${Beaengine_FOUND})
target_link_libraries(testapp ${Beaengine_LIBRARY})
endif()
this seems to work well for mingw makefiles / eclipse makefile projects.
vs will request to reload all projects after first build.
You can force a build using the build_external_project function below.
It works by generating a simple helper project inside the build tree and then calling the cmake configuration and the cmake build on the helper.
Customize at will for the actual ExternalProject_add command.
Note that the trailing arguments are used to pass CMAKE_ARGS. Furthur enhancements are left as an exercise to the reader :-)
# This function is used to force a build on a dependant project at cmake configuration phase.
#
function (build_external_project target prefix url) #FOLLOWING ARGUMENTS are the CMAKE_ARGS of ExternalProject_Add
set(trigger_build_dir ${CMAKE_BINARY_DIR}/force_${target})
#mktemp dir in build tree
file(MAKE_DIRECTORY ${trigger_build_dir} ${trigger_build_dir}/build)
#generate false dependency project
set(CMAKE_LIST_CONTENT "
cmake_minimum_required(VERSION 2.8)
include(ExternalProject)
ExternalProject_add(${target}
PREFIX ${prefix}/${target}
URL ${url}
CMAKE_ARGS ${ARGN}
INSTALL_COMMAND \"\"
)
add_custom_target(trigger_${target})
add_dependencies(trigger_${target} ${target})
")
file(WRITE ${trigger_build_dir}/CMakeLists.txt "${CMAKE_LIST_CONTENT}")
execute_process(COMMAND ${CMAKE_COMMAND} ..
WORKING_DIRECTORY ${trigger_build_dir}/build
)
execute_process(COMMAND ${CMAKE_COMMAND} --build .
WORKING_DIRECTORY ${trigger_build_dir}/build
)
endfunction()
Time has passed and CMake implemented a native version allowing to reference targets from an ExternalProject_Add.
This feature is implemented in the FetchContent module. It allows downloading and immediately consuming targets defined at configure time.
It uses a scratch build dir as hinted by my previous answer, but in a more integrated API.