I successfully created a native node addon with napi and cmakejs. But when adding a simple library the unix make file, generated by cmake-js explodes with
[ 50%] Linking CXX static library liblib_name.a
[ 50%] Built target lib_name
CMakeFiles/spielwiese.dir/flags.make:10: * missing separator. Stop.
make[1]: * [CMakeFiles/Makefile2:72: CMakeFiles/spielwiese.dir/all] Error 2
make: *** [Makefile:130: all] Error 2
ERR! OMG Process terminated: 2
Minimized example project: https://github.com/Superlokkus/spielwiese/tree/napi
The root CMakeLists should be close or intented to be close to cmakejs example boilerplate version, just with an additional function PARSE_CMAKEJS_PROPERTIES to also build it via a cmake CLI command, for nice developing with IDEs like CLion. However the problem persisits when removing the PARSE_CMAKEJS_PROPERTIES function.
I added the library with a add_subdirectory, if you remove https://github.com/Superlokkus/spielwiese/blob/napi/CMakeLists.txt#L47 aka add_subdirectory(src/lib_name) and change https://github.com/Superlokkus/spielwiese/blob/napi/CMakeLists.txt#L63 aka target_link_libraries(${PROJECT_NAME} PUBLIC ${CMAKE_JS_LIB} lib_name) to target_link_libraries(${PROJECT_NAME} PUBLIC ${CMAKE_JS_LIB}) and also remove
https://github.com/Superlokkus/spielwiese/blob/napi/src/spielwiese.cpp#L3 aka #include <lib_name/lib_name.hpp>,
the project builds again incl. the mocha test. However add the example library and you get the error again.
Root CMakeLists:
cmake_minimum_required(VERSION 3.2)
# The following function is just for nice CLion IDE support with cmake-js
function(PARSE_CMAKEJS_PROPERTIES)
function(GET_VARIABLE INPUT_STRING VARIABLE_TO_SELECT OUTPUT_VARIABLE)
set(SEARCH_STRING "${VARIABLE_TO_SELECT}=\"")
string(LENGTH "${SEARCH_STRING}" SEARCH_STRING_LENGTH)
string(LENGTH "${INPUT_STRING}" INPUT_STRING_LENGTH)
string(FIND "${INPUT_STRING}" "${VARIABLE_TO_SELECT}=\"" SEARCH_STRING_INDEX)
math(EXPR SEARCH_STRING_INDEX "${SEARCH_STRING_INDEX}+${SEARCH_STRING_LENGTH}")
string(SUBSTRING "${INPUT_STRING}" ${SEARCH_STRING_INDEX} ${INPUT_STRING_LENGTH} AFTER_SEARCH_STRING)
string(FIND "${AFTER_SEARCH_STRING}" "\"" QUOTE_INDEX)
string(SUBSTRING "${AFTER_SEARCH_STRING}" "0" "${QUOTE_INDEX}" RESULT_STRING)
set("${OUTPUT_VARIABLE}" "${RESULT_STRING}" PARENT_SCOPE)
endfunction(GET_VARIABLE)
string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER)
if (CMAKE_BUILD_TYPE_LOWER STREQUAL "debug")
exec_program(./node_modules/.bin/cmake-js ${CMAKE_CURRENT_SOURCE_DIR}
ARGS print-configure --debug
OUTPUT_VARIABLE CMAKE_JS_OUTPUT
)
else ()
exec_program(./node_modules/.bin/cmake-js ${CMAKE_CURRENT_SOURCE_DIR}
ARGS print-configure
OUTPUT_VARIABLE CMAKE_JS_OUTPUT
)
endif ()
get_variable("${CMAKE_JS_OUTPUT}" "CMAKE_JS_INC" CMAKE_JS_INC)
set(CMAKE_JS_INC "${CMAKE_JS_INC}" PARENT_SCOPE)
get_variable("${CMAKE_JS_OUTPUT}" "CMAKE_LIBRARY_OUTPUT_DIRECTORY" CMAKE_LIBRARY_OUTPUT_DIRECTORY)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}" PARENT_SCOPE)
endfunction(PARSE_CMAKEJS_PROPERTIES)
# Name of the project (will be the name of the plugin)
project(spielwiese VERSION 1.0)
if (NOT CMAKE_JS_INC)
parse_cmakejs_properties()
endif ()
add_subdirectory(src/lib_name)
# Essential include files to build a node addon,
# you should add this line in every CMake.js based project.
include_directories(${CMAKE_JS_INC})
# Declare the location of the source files
file(GLOB SOURCE_FILES "src/*.cpp")
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES})
# This line will give our library file a .node extension without any "lib" prefix
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")
# Essential library files to link to a node addon,
# you should add this line in every CMake.js based project.
target_link_libraries(${PROJECT_NAME} PUBLIC ${CMAKE_JS_LIB} lib_name)
# Include N-API wrappers
execute_process(COMMAND node -p "require('node-addon-api').include"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE NODE_ADDON_API_DIR
)
string(REPLACE "\"" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR})
Libnames CMakeLists
cmake_minimum_required(VERSION 3.0)
project(lib_name VERSION 1.0)
string(TOUPPER ${PROJECT_NAME} PROJECT_NAME_UPPER_CASE)
configure_file(
${PROJECT_NAME}_version.hpp.in
${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}/${PROJECT_NAME}_version.hpp
)
set(${PROJECT_NAME}_implementation_files
src/lib.cpp
)
add_library(${PROJECT_NAME} ${${PROJECT_NAME}_implementation_files})
set_property(TARGET ${PROJECT_NAME} PROPERTY LINKER_LANGUAGE CXX)
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 17)
target_include_directories(${PROJECT_NAME}
PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
)
install(TARGETS ${PROJECT_NAME}
ARCHIVE
DESTINATION lib)
install(TARGETS ${PROJECT_NAME}
PUBLIC_HEADER
DESTINATION include)
Update
I did some experimentation and it seems
target_include_directories(${PROJECT_NAME}
PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
)
is doing the trouble, so it looks like cmakejs has some problem with that command?!
Turns out the snippet taken from the official cmake-js documentation
execute_process(COMMAND node -p "require('node-addon-api').include"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE NODE_ADDON_API_DIR
)
is invoking node to get the path to the node headers. But the output of node might contain a newline, which will ruin the day of all includes after that. So the correct part of the root CMakeLists.txt would be
# Include N-API wrappers
execute_process(COMMAND node -p "require('node-addon-api').include"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE NODE_ADDON_API_DIR
)
string(REPLACE "\n" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
string(REPLACE "\"" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR})
So a addition of string(REPLACE "\n" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR}) to get rid of the newline fixed the problem.
Thanks #fred for the help to find the problem
Already created a pull request to fix the cmake-js documentation: https://github.com/cmake-js/cmake-js/issues/175
Related
when building a project stucture like following:
LinearAlgebra
|---HEADER
|-----|---Linear.h
|---SRC
|-----|---Linear.cpp
|---TEST
|-----|---hello_test.cc
here is my CMakeList.txt:
cmake_minimum_required(VERSION 3.10)
project(LinearAlgebra )
set(CMAKE_CXX_STANDARD 11)
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
add_library(LA_HEADER INTERFACE header/Linear.h)
add_library(LA_SRC INTERFACE src/Linear.cpp)
target_include_directories(LA_HEADER INTERFACE "${PROJECT_SOURCE_DIR}/header" "${PROJECT_SOURCE_DIR}/src")
enable_testing()
add_executable(
hello_test
test/hello_test.cc
)
target_link_libraries(
hello_test
gtest_main
LA_HEADER
LA_SRC
)
include(GoogleTest)
gtest_discover_tests(hello_test)
it works fine with cmake -S . -B build, but when it comes to cmake --build build following error occured:
Undefined symbols for architecture x86_64:
"Linear::Linear()", referenced from:
HelloTest_BasicAssertions_Test::TestBody() in hello_test.cc.o
it seems something wrong with my cmake file that didn't tell Linear.h where Linear.cpp located. So how to fix it ?? thanks for your help...
seems i misunderstand add_library, change three lines and it works out:
cmake_minimum_required(VERSION 3.10)
project(LinearAlgebra )
include_directories(header) # include_directories instead of add_library for .h files
add_library(LA_LIB src/Linear.cpp) # use add_library for .cpp files only!!!
set(CMAKE_CXX_STANDARD 11)
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
enable_testing()
add_executable(
hello_test
test/hello_test.cc
)
target_link_libraries(
hello_test
gtest_main
LA_LIB # link LA_LIB only..
)
include(GoogleTest)
gtest_discover_tests(hello_test)
leave it here for anyone have the same requirements.
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
My cmake configuration is not generating any protobuf src and header files.
I've already checked if the proto files can be found.
Cmakelists.txt
cmake_minimum_required(VERSION 3.0.2)
..
include(FindProtobuf REQUIRED)
file(GLOB PROTO_DEF "${CMAKE_CURRENT_SOURCE_DIR}/protobuf/*/*.proto")
foreach(file ${PROTO_DEF})
if(EXISTS ${file})
MESSAGE("YES")
else()
MESSAGE("NO")
endif()
endforeach()
SET(PROTOBUF_GENERATE_CPP_APPEND_PATH PROTOBUF)
SET(PROTOBUF_PROTOC_EXECUTABLE protoc.exe)
..
PROTOBUF_GENERATE_CPP(PROTO_SRC PROTO_INCL ${PROTO_DEF})
..
add_library(${PROJECT_NAME} STATIC ${INCLUDES} ${INTERNAL_INCLUDES} ${SRC} ${PROTO_SRC} ${PROTO_INCL})
target_link_libraries(${PROJECT_NAME} ${PROTOBUF_LIBRARIES})
I have checked the FindProtobuf.cmake and half way through:
foreach(FIL ${ARGN})
get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
get_filename_component(FIL_WE ${FIL} NAME_WE)
list(APPEND ${SRCS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.cc")
list(APPEND ${HDRS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.h")
MESSAGE(1 ${CMAKE_CURRENT_BINARY_DIR})
MESSAGE(2 ${_protobuf_include_path})
MESSAGE(3 ${ABS_FIL})
MESSAGE(4 ${PROTOBUF_PROTOC_EXECUTABLE})
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.cc"
"${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.h"
COMMAND ${PROTOBUF_PROTOC_EXECUTABLE}
ARGS --cpp_out ${CMAKE_CURRENT_BINARY_DIR} ${_protobuf_include_path} ${ABS_FIL}
DEPENDS ${ABS_FIL}
COMMENT "Running C++ protocol buffer compiler on ${FIL}"
VERBATIM )
endforeach()
You can see i've added the 4 message commands, the script reaches this point and variables show good values.
The proto files do have a depencency to the library, thus the command should get executed !?
Any ideas on this problem?
update
replacing the add_custom_command with
EXEC_PROGRAM(${PROTOBUF_PROTOC_EXECUTABLE} ARGS --cpp_out ${CMAKE_CURRENT_BINARY_DIR} ${_protobuf_include_path} ${ABS_FIL}
does generate the source and header files, must i manually activate the custom_commmands?
Regards Auke
The add_custom_command is fired (for my project) during compilation time.
Adding
SET_SOURCE_FILES_PROPERTIES(${PROTO_SRC} ${PROTO_INCL} PROPERTIES GENERATED TRUE)
gives cmake info that the files will be generated.
Alternative if you have an external proto folder:
file(GLOB PROTOBUF_FILELIST ${PROTO_INCLUDE_DIR}/*.proto)
foreach( proto_file ${PROTOBUF_FILELIST} )
get_filename_component(proto_name ${proto_file} NAME_WE)
get_filename_component(proto_path ${PROTO_INCLUDE_DIR} ABSOLUTE)
set_source_files_properties("${proto_path}/${proto_name}.pb.cc"
"${proto_path}/${proto_name}.pb.h"
PROPERTIES GENERATED TRUE)
endforeach()
See that:
https://cmake.org/cmake/help/v3.20/module/FindProtobuf.html
Example:
find_package(Protobuf REQUIRED)
include_directories(${Protobuf_INCLUDE_DIRS})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS foo.proto)
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS EXPORT_MACRO DLL_EXPORT foo.proto)
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS DESCRIPTORS PROTO_DESCS foo.proto)
protobuf_generate_python(PROTO_PY foo.proto)
add_executable(bar bar.cc ${PROTO_SRCS} ${PROTO_HDRS})
target_link_libraries(bar ${Protobuf_LIBRARIES})
Note The protobuf_generate_cpp and protobuf_generate_python functions and add_executable() or add_library() calls only work properly within the same directory.
This problem only happens when the target is a library (using add_library, add_executable seems to be fine). The PROTOBUF_GENERATE_CPP will defer the generation of protobuf sources and headers when building the library. A workaround is to define a custom target and add it as a dependency to the lib target. e.g.
add_custom_target(proto_dep DEPENDS ${PROTO_SRCS} ${PROTO_HDRS})
add_library(${PROJECT_NAME} STATIC ${OTHER_SRCS})
add_dependencies(${PROJECT_NAME} proto_dep)
target_link_libraries(${PROJECT_NAME} PRIVATE gRPC::grpc++ gRPC::grpc++_reflection protobuf::libprotobuf)
I just downloaded googletest, generated its makefile with CMake and built it. Now, I need to use it in my testing project.
With CMake, I have been advised not pointing to gtest libraries directly (using include _directories or link_directories) but use find_package() instead.
The problem is, there is no install target for the gtest makefile generated. I cannot understand how find_package(GTest REQUIRED) could work without some kind of installation. Also, putting the gtest folder as a subfolder in my project is not possible.
Thanks for any help.
This is an unusual case; most projects specify install rules.
CMake's ExternalProject_Add module is maybe the best tool for this job. This allows you to download, configure and build gtest from within your project, and then link to the gtest libraries.
I've tested the following CMakeLists.txt on Windows with Visual Studio 10 and 11, and on Ubuntu using GCC 4.8 and Clang 3.2 - it might need adjusted for other platforms/compilers:
cmake_minimum_required(VERSION 2.8.7 FATAL_ERROR)
project(Test)
# Create main.cpp which uses gtest
file(WRITE src/main.cpp "#include \"gtest/gtest.h\"\n\n")
file(APPEND src/main.cpp "TEST(A, B) { SUCCEED(); }\n")
file(APPEND src/main.cpp "int main(int argc, char **argv) {\n")
file(APPEND src/main.cpp " testing::InitGoogleTest(&argc, argv);\n")
file(APPEND src/main.cpp " return RUN_ALL_TESTS();\n")
file(APPEND src/main.cpp "}\n")
# Create patch file for gtest with MSVC 2012
if(MSVC_VERSION EQUAL 1700)
file(WRITE gtest.patch "Index: cmake/internal_utils.cmake\n")
file(APPEND gtest.patch "===================================================================\n")
file(APPEND gtest.patch "--- cmake/internal_utils.cmake (revision 660)\n")
file(APPEND gtest.patch "+++ cmake/internal_utils.cmake (working copy)\n")
file(APPEND gtest.patch "## -66,6 +66,9 ##\n")
file(APPEND gtest.patch " # Resolved overload was found by argument-dependent lookup.\n")
file(APPEND gtest.patch " set(cxx_base_flags \"\${cxx_base_flags} -wd4675\")\n")
file(APPEND gtest.patch " endif()\n")
file(APPEND gtest.patch "+ if (MSVC_VERSION EQUAL 1700)\n")
file(APPEND gtest.patch "+ set(cxx_base_flags \"\${cxx_base_flags} -D_VARIADIC_MAX=10\")\n")
file(APPEND gtest.patch "+ endif ()\n")
file(APPEND gtest.patch " set(cxx_base_flags \"\${cxx_base_flags} -D_UNICODE -DUNICODE -DWIN32 -D_WIN32\")\n")
file(APPEND gtest.patch " set(cxx_base_flags \"\${cxx_base_flags} -DSTRICT -DWIN32_LEAN_AND_MEAN\")\n")
file(APPEND gtest.patch " set(cxx_exception_flags \"-EHsc -D_HAS_EXCEPTIONS=1\")\n")
else()
file(WRITE gtest.patch "")
endif()
# Enable ExternalProject CMake module
include(ExternalProject)
# Set the build type if it isn't already
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
# Set default ExternalProject root directory
set_directory_properties(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/ThirdParty)
# Add gtest
ExternalProject_Add(
googletest
SVN_REPOSITORY http://googletest.googlecode.com/svn/trunk/
SVN_REVISION -r 660
TIMEOUT 10
PATCH_COMMAND svn patch ${CMAKE_SOURCE_DIR}/gtest.patch ${CMAKE_BINARY_DIR}/ThirdParty/src/googletest
# Force separate output paths for debug and release builds to allow easy
# identification of correct lib in subsequent TARGET_LINK_LIBRARIES commands
CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG:PATH=DebugLibs
-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE:PATH=ReleaseLibs
-Dgtest_force_shared_crt=ON
# Disable install step
INSTALL_COMMAND ""
# Wrap download, configure and build steps in a script to log output
LOG_DOWNLOAD ON
LOG_CONFIGURE ON
LOG_BUILD ON)
# Specify include dir
ExternalProject_Get_Property(googletest source_dir)
include_directories(${source_dir}/include)
# Add compiler flag for MSVC 2012
if(MSVC_VERSION EQUAL 1700)
add_definitions(-D_VARIADIC_MAX=10)
endif()
# Add test executable target
add_executable(MainTest ${PROJECT_SOURCE_DIR}/src/main.cpp)
# Create dependency of MainTest on googletest
add_dependencies(MainTest googletest)
# Specify MainTest's link libraries
ExternalProject_Get_Property(googletest binary_dir)
if(MSVC)
set(Suffix ".lib")
else()
set(Suffix ".a")
set(Pthread "-pthread")
endif()
target_link_libraries(
MainTest
debug ${binary_dir}/DebugLibs/${CMAKE_FIND_LIBRARY_PREFIXES}gtest${Suffix}
optimized ${binary_dir}/ReleaseLibs/${CMAKE_FIND_LIBRARY_PREFIXES}gtest${Suffix}
${Pthread})
If you create this as CMakeLists.txt in an empty directory (say MyTest), then:
cd MyTest
mkdir build
cd build
cmake ..
This should create a basic main.cpp in MyTest/src and create a project file (MyTest/build/Test.sln on Windows)
When you build the project, it should download the gtest sources to MyTest/build/ThirdParty/src/googletest, and build them in MyTest/build/ThirdParty/src/googletest-build. You should then be able to run the MainTest target successfully.
It is long past when the original question being asked, but for the benefit of others, it is possible to use ExternalProject to download the gtest source and then use add_subdirectory() to add it to your build. This has the following advantages:
gtest is built as part of your main build, so it uses the same compiler flags, etc. and doesn't need to be installed anywhere.
There's no need to add the gtest sources to your own source tree.
Used in the normal way, ExternalProject won't do the download and unpacking at configure time (i.e. when CMake is run), but you can get it to do so. I've written a blog post on how to do this which also includes a generalised implementation which works for any external project which uses CMake as its build system, not just gtest. You can find it here:
https://crascit.com/2015/07/25/cmake-gtest/
Update: The approach described above is now also part of the googletest documentation.
My answer is based on the answer from firegurafiku. I modified it in the following ways:
added CMAKE_ARGS to the ExternalProject_Add call so it works with msvc.
gets the gtest source from a file location rather than downloading
added portable (for MSVC and non-MSVC) definition and usage of IMPORTED_LOCATION
Addressed the problem with the call to set_target_properties not working at configure time when the INTERFACE_INCLUDE_DIRECTORIES does not yet exist because the external project has not yet been built.
I prefer keeping gtest as an external project rather than adding its source directly to my project. One reason is because I do not like having the gtest source code included when I am searching my code. Any special build flags that are needed by my code that should also be used when building gtest can be added to the definition of CMAKE_ARGS in the call to ExternalProject_Add
Here is my modified approach:
include(ExternalProject)
# variables to help keep track of gtest paths
set(GTEST_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/gtest")
set(GTEST_LOCATION "${GTEST_PREFIX}/src/GTestExternal-build")
set(GTEST_INCLUDES "${GTEST_PREFIX}/src/GTestExternal/include")
# external project download and build (no install for gtest)
ExternalProject_Add(GTestExternal
URL ${CMAKE_CURRENT_SOURCE_DIR}/../googletest
PREFIX "${GTEST_PREFIX}"
# cmake arguments
CMAKE_ARGS -Dgtest_force_shared_crt=ON
# Disable install step
INSTALL_COMMAND ""
# Wrap download, configure and build steps in a script to log output
LOG_DOWNLOAD ON
LOG_CONFIGURE ON
LOG_BUILD ON
)
# variables defining the import location properties for the generated gtest and
# gtestmain libraries
if (MSVC)
set(GTEST_IMPORTED_LOCATION
IMPORTED_LOCATION_DEBUG "${GTEST_LOCATION}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}"
IMPORTED_LOCATION_RELEASE "${GTEST_LOCATION}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}"
)
set(GTESTMAIN_IMPORTED_LOCATION
IMPORTED_LOCATION_DEBUG "${GTEST_LOCATION}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}"
IMPORTED_LOCATION_RELEASE "${GTEST_LOCATION}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}"
)
else()
set(GTEST_IMPORTED_LOCATION
IMPORTED_LOCATION "${GTEST_LOCATION}/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}")
set(GTESTMAIN_IMPORTED_LOCATION
IMPORTED_LOCATION "${GTEST_LOCATION}/${CMAKE_STATIC_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}")
endif()
# the gtest include directory exists only after it is build, but it is used/needed
# for the set_target_properties call below, so make it to avoid an error
file(MAKE_DIRECTORY ${GTEST_INCLUDES})
# define imported library GTest
add_library(GTest IMPORTED STATIC GLOBAL)
set_target_properties(GTest PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${GTEST_INCLUDES}"
IMPORTED_LINK_INTERFACE_LIBRARIES "${CMAKE_THREAD_LIBS_INIT}"
${GTEST_IMPORTED_LOCATION}
)
# define imported library GTestMain
add_library(GTestMain IMPORTED STATIC GLOBAL)
set_target_properties(GTestMain PROPERTIES
IMPORTED_LINK_INTERFACE_LIBRARIES GTest
${GTESTMAIN_IMPORTED_LOCATION}
)
# make GTest depend on GTestExternal
add_dependencies(GTest GTestExternal)
#
# My targets
#
project(test_pipeline)
add_executable(${PROJECT_NAME} test_pipeline.cpp)
set_target_properties(${PROJECT_NAME} PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})
target_link_libraries(${PROJECT_NAME} ${TBB_LIBRARIES})
target_link_libraries(${PROJECT_NAME} GTest)
There is a bit less complex solution using ExternalProject module and imported libraries feature of cmake. It checks out code from repository, builds it and creates target from built static libraries (they're libgtest.a and libgtest_main.a on my system).
find_package(Threads REQUIRED)
include(ExternalProject)
set(GTEST_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/gtest")
ExternalProject_Add(GTestExternal
SVN_REPOSITORY http://googletest.googlecode.com/svn/trunk
SVN_REVISION -r HEAD
TIMEOUT 10
PREFIX "${GTEST_PREFIX}"
INSTALL_COMMAND "")
set(LIBPREFIX "${CMAKE_STATIC_LIBRARY_PREFIX}")
set(LIBSUFFIX "${CMAKE_STATIC_LIBRARY_SUFFIX}")
set(GTEST_LOCATION "${GTEST_PREFIX}/src/GTestExternal-build")
set(GTEST_INCLUDES "${GTEST_PREFIX}/src/GTestExternal/include")
set(GTEST_LIBRARY "${GTEST_LOCATION}/${LIBPREFIX}gtest${LIBSUFFIX}")
set(GTEST_MAINLIB "${GTEST_LOCATION}/${LIBPREFIX}gtest_main${LIBSUFFIX}")
add_library(GTest IMPORTED STATIC GLOBAL)
set_target_properties(GTest PROPERTIES
IMPORTED_LOCATION "${GTEST_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${GTEST_INCLUDES}"
IMPORTED_LINK_INTERFACE_LIBRARIES "${CMAKE_THREAD_LIBS_INIT}")
add_library(GTestMain IMPORTED STATIC GLOBAL)
set_target_properties(GTestMain PROPERTIES
IMPORTED_LOCATION "${GTEST_MAINLIB}"
IMPORTED_LINK_INTERFACE_LIBRARIES
"${GTEST_LIBRARY};${CMAKE_THREAD_LIBS_INIT}")
add_dependencies(GTest GTestExternal)
You may want to replace SVN_REVISION or add LOG_CONFIGURE and LOG_BUILD options here. After GTest and GTestMain targets are created, they can be used like this:
add_executable(Test
test1.cc
test2.cc)
target_link_libraries(Test GTestMain)
or, if you have your own main() function:
add_executable(Test
main.cc
test1.cc
test2.cc)
target_link_libraries(Test GTest)
The topic is a bit old, but there appeared a new way of including external libraries in CMake.
#Requires CMake 3.16+
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG release-1.8.0
)
FetchContent_MakeAvailable(googletest)
If you want to support the earlier versions of cmake:
# Requires CMake 3.11+
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG release-1.8.0
)
FetchContent_GetProperties(googletest)
if(NOT googletest_POPULATED)
FetchContent_Populate(googletest)
add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
endif()
Then you just add
enable_testing()
add_executable(test ${SOURCES} )
target_link_libraries(test gtest_main ${YOUR_LIBS})
add_test(NAME tests COMMAND test)
Further reading: https://cmake.org/cmake/help/latest/module/FetchContent.html
When you get the libgtest-dev package via
sudo apt install libgtest-dev
The source is stored in location /usr/src/googletest
You can simply point your CMakeLists.txt to that directory so that it can find the necessary dependencies
Something like the following
add_subdirectory(/usr/src/googletest gtest)
target_link_libraries(your_executable gtest)