Using Precompiled Header for the part of source files (CMake) - cmake

file(GLOB SRC *.cpp)
file(GLOB SRC2 ../xx/*.cpp)
target_precompile_headers(myapp PRIVATE "$<ONLY_FOR_SRC>:stdafx.h>")
I tried several CMake Generative Expressions on ONLY_FOR_SRC above, however, they do not work. I mean to make the files in SRC compiled with precompiled header, while the files in SRC2 being compiled without precompiled header.
What is supposed to be put on where ONLY_FOR_SRC is above?

One of the proper ways would be to use object libraries to separate compilation requirements:
file(GLOB SRC *.cpp)
file(GLOB SRC2 ../xx/*.cpp)
add_library(myapp-src OBJECT "${SRC}")
add_library(myapp-src2 OBJECT "${SRC2}")
target_precompile_headers(myapp-src PRIVATE stdafx.h)
target_link_libraries(myapp PRIVATE myapp-src myapp-src2)
You can set different include dirs, linked libraries then link all object files together.

Related

Providing include directory outside source folder for static library users

I am developing a simple static C library for learning purposes using cmake.
Some projects like GLFW provide an include folder on the root, so library users can copy it and use it as an include directory.
In my library, I want to have an include folder on the root, so when I use the library on other projects, I can just copy this folder and set it as an include directory.
Here is a simplified folder structure of my library:
include
+--mylib.h
src
+--myheader.h
+--mysource.c
+--CMakeLists.txt
CmakeLists.txt
The src folder has my headers and implementation files, and a CMakeLists.txt for building a static library out of mysource.c.
The CMakeLists on the root folder just sets the project and adds src as a subdirectory.
I want the mylib.h file to have a #include <myheader.h>.
Here's a detour to talk about how I want to use it when it's done.
The idea is that when using the lib on another project, I can have something like this:
deps
+--include
+--mylib.h
src
+--main.c
And in the main.c file, include mylib.h and use what's defined on myheader.h
Here the detour ends, and I'm talking about my actual lib project again.
How can I achieve this using cmake? As far as I know, the mylib.h file needs to know it's including files from the src diretory, but I see no way of setting that, as for exemple in GLFW this directory does not have a CMakeLists.txt.
I am gonna quess that this is a design issue since it would make sense to you if you would have installed the library to a system before you tried to use it. That is, not using add_subdirectory() but find_library() at usage.
First, if you are using a external library, but not installing it, you would include all files in you deps-folder. All files then include source-files and so on and will be compiled besides you main.c. This is done with add_subdirectory(deps/MyLib) and later also included in you main-project.
Example:
add_subdirectory(deps/MyLib EXCLUDE_FROM_ALL)
target_link_libraries(${PROJECT_NAME} PRIVATE MyLib)
target_include_directories(${PROJECT_NAME} PRIVATE MyLib)
If you do not want to compile it all the time, then you must instruct cmake where it can find headers and library-files. Preferred way is to use find_library() which does some magic for you. Since you do not mention any installation i will assume that it does not exist and your only option is then to use add_subdirectory().
"I can just copy this folder and set it as an include directory."
CMake wants to handle these things for you so you should never copy headers around. You should either use add_subdirectory() to include a project/headers or make use of the find_library() which make sure you find where the headers are in the system.
I suggest that you push yourself to learn howto install a library into a system and how to utilize it later, but only by using find_library(). Then the library will be global for all projects and also not duplicated.
Adding some kind of psudo-code in hope it all makes more sense. Although it is around add_subdirectory() since the code for installing is quite large unfortunately.
CMakeLists.txt for main.c
cmake_minimum_required(VERSION 3.8)
project(MyLibTest)
add_executable(${PROJECT_NAME}
src/main.c
)
add_subdirectory(external/MyLib EXCLUDE_FROM_ALL)
target_link_libraries(${PROJECT_NAME} PRIVATE MyLib)
target_include_directories(${PROJECT_NAME} PRIVATE MyLib)
CMakeLists.txt for library
cmake_minimum_required(VERSION 3.8)
project(MyLib)
add_library(${PROJECT_NAME} STATIC
src/MyLib.c
)
target_include_directories(${PROJECT_NAME}
PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)
The structure for the project would then be:
/
external/MyLib
external/MyLib/src
MyLib.c
external/MyLib/include
MyLib.h
src
main.c
CMakeLists.txt

Including headers in Visual Studio project via TARGET_INCLUDE_DIRECTORIES? [duplicate]

I had a project which uses CMake as build tool and made a simple template for me and my collegues to use. As I searched for best and easy to use practices online, I've came across different approaches to make a library.
In this template, I've listed header files and source files in two seperate variables, and I'm not passing the headers to add_library command - just sources. And then I use set_target_properties with PUBLIC_HEADER variable to give the header-file list.
So far it seems to work, but I wonder if I'm making thing unnecessarily complex. Some people online give header files to add_library command as well and doesn't even use set_target_properties and such.
In short:
should we include header files to add_library or should we not (as a best practice)? And impacts of the two usage.
what is purpose being served by adding headers in the add_library/add_executable? As they seem working even without it (seems forward declaration and symbols only). confirm on understanding please.
(Here is the template I'm talking about:)
cmake_minimum_required(VERSION 3.1.0)
project(lae CXX C)
set(CMAKE_CXX_STANDARD 14)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
)
set(SOURCE_FILES
...
)
set(HEADER_FILES
...
)
set( PRIVATE_HEADER_FILES
...
)
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} )
set( REQUIRED_LIBRARIES
...
)
target_link_libraries(${PROJECT_NAME} ${REQUIRED_LIBRARIES} )
SET_TARGET_PROPERTIES(
${PROJECT_NAME}
PROPERTIES
FRAMEWORK ON
SOVERSION 0
VERSION 0.1.0
PUBLIC_HEADER "${HEADER_FILES}"
PRIVATE_HEADER "${PRIVATE_HEADER_FILES}"
ARCHIVE_OUTPUT_DIRECTORY "lib"
LIBRARY_OUTPUT_DIRECTORY "lib"
OUTPUT_NAME ${PROJECT_NAME}
)
In our projects we use a "simple" way of yours - add_library with both headers and sources.
If you add only sources, then you won't see headers in IDE-generated project.
However, when installing, we have to do it like that, using two install commands:
install(TARGETS library_name
LIBRARY DESTINATION lib)
install(FILES ${PUBLIC_HEADERS}
DESTINATION include/library_name)
If you want to do it as a single command, you can use set_target_properties with PUBLIC_HEADER, as you suggested.
Then, this kind of install is possible:
install(TARGETS library_name
LIBRARY DESTINATION lib
PUBLIC_HEADER DESTINATION include/library_name)
Choose the one you like the most and stick to it.

Cmake add_library ALIAS

I am trying to figure out exactly what this line is for in the cmake file of this github json project,
add_library(${NLOHMANN_JSON_TARGET_NAME} INTERFACE)
add_library(${PROJECT_NAME}::${NLOHMANN_JSON_TARGET_NAME} ALIAS ${NLOHMANN_JSON_TARGET_NAME})
Specifically with this example, what does this allow in this cmake file that otherwise would not be possible?
I see no other references to ${PROJECT_NAME}::${NLOHMANN_JSON_TARGET_NAME} in this CMakeLists.cmake, so I am confused as to what exactly this achieves.
Edit:
The key thing that this achieves, that the comment did not make obvious to me, is that it makes the targets work with the namespaces when the project is used through add_subdirectory()
Without the alias, you can still add the library via add_subdirectory however in the target_link_libraries command you would need to omit the namespace:
project(mySuperApp)
set(mySuperApp_SRC src/main.c)
add_subdirectory(thirdparty/json)
add_executable(${PROJECT_NAME} ${mySuperApp_SRC})
target_link_libraries(${PROJECT_NAME} PRIVATE nlohmann_json)
If you did that but then decided to use find_package to include the library (as opposed to add_subdirectory), you would need to change target_link_libraries to use the namespaced targets i.e.
project(mySuperApp)
set(mySuperApp_SRC src/main.c)
find_package(nlohmann_json REQUIRED)
add_executable(${PROJECT_NAME} ${mySuperApp_SRC})
target_link_libraries(${PROJECT_NAME} PRIVATE nlohmann_json::nlohmann_json)
by adding the alias, the target_link_libraries using the namespaced version (i.e. nlohmann_json::nlohmann_json) will work in either case and not require a change if you later decide to switch from find_package to add_subdirectory).
It allows you to add the library with find_package OR add_subdirectory using the same target name for both:
# creates nlohmann_json::nlohmann_json
find_package(nlohmann_json REQUIRED)
if (nlohmann_json_NOT_FOUND)
# creates nlohmann_json AND nlohmann_json::nlohmann_json
add_subdirectory(thirdparty/json)
endif()
add_executable(your_target_name ${your_target_sources})
target_link_libraries(your_target_name PRIVATE nlohmann_json::nlohmann_json)
Without the alias, you would need:
# creates nlohmann_json::nlohmann_json
find_package(nlohmann_json REQUIRED)
if (NOT nlohmann_json_FOUND)
# creates only nlohmann_json
add_subdirectory(thirdparty/json)
endif()
add_executable(your_target_name ${your_target_sources})
if (nlohmann_json_FOUND)
target_link_libraries(your_target_name PRIVATE nlohmann_json::nlohmann_json)
else()
target_link_libraries(your_target_name PRIVATE nlohmann_json)
endif()
This will allow using nlohmann/json project by adding it into your super project with add_subdirectory(...)
For example simple project structure:
<root project>\
\thirdparty\json <<-- git submodule to https://github.com/nlohmann/json
\include\
\src\
CMakeLists.txt
In your project CMakeLists.txt
...
project(mySuperApp)
set(mySuperApp_SRC src/main.c)
# can under some conditions...
add_subdirectory(thirdparty/json)
add_executable(${PROJECT_NAME} ${mySuperApp_SRC})
target_link_libraries(${PROJECT_NAME} PRIVATE nlohmann_json::nlohmann_json)
Using git's blame function shows that line was added in this commit: 33a2154, which has the following comment attached:
CMake convention is to use a project namespace, i.e. Foo::, for imported
targets. When multiple targets are imported from a project, this looks
like Foo::Bar1 Foo::Bar2, etc. This adds the nlohmann_json:: namespace to
the exported target names.
This also allows the generated project config files to be used from the
build directory instead of just the install directory.

CMake all_object_files target

I want to create a CMake target to build all object files.
To do this, I first modified all targets to build an object file lib and link against it:
add_library(exe_object_files OBJECT ${sources})
add_executable(exe $<TARGET_OBJECTS:exe_object_files>)
Since I have multiple executables, I added a target for all object files and added the targets for each executable as dependency:
add_custom_target(all_exe_object_files)
add_dependencies(all_exe_object_files exe_object_files)
This adds a all_exe_object_files target I can make, but making the target does nothing.
Then I tried adding the object files as sources:
get_target_property(all_exe_object_files_sources all_exe_object_files SOURCES)
if(NOT all_exe_object_files_sources)
set(all_exe_object_files_sources)
endif()
list(APPEND all_exe_object_files_sources $<TARGET_OBJECTS:exe_object_files>)
set_target_properties(all_exe_object_files PROPERTIES SOURCES "${all_exe_object_files_sources}")
This did not help.
I guess the reason is that add_library(... OBJECT ...) behaves similar to add_custom_command and the files are only build if a target uses them (and my custom target probably does not count as using the generated files).
What am I doing wrong and how can I create a target to build all object files?

Why add header files into ADD_LIBRARY/ADD_EXECUTABLE command in CMake

I had a project which uses CMake as build tool and made a simple template for me and my collegues to use. As I searched for best and easy to use practices online, I've came across different approaches to make a library.
In this template, I've listed header files and source files in two seperate variables, and I'm not passing the headers to add_library command - just sources. And then I use set_target_properties with PUBLIC_HEADER variable to give the header-file list.
So far it seems to work, but I wonder if I'm making thing unnecessarily complex. Some people online give header files to add_library command as well and doesn't even use set_target_properties and such.
In short:
should we include header files to add_library or should we not (as a best practice)? And impacts of the two usage.
what is purpose being served by adding headers in the add_library/add_executable? As they seem working even without it (seems forward declaration and symbols only). confirm on understanding please.
(Here is the template I'm talking about:)
cmake_minimum_required(VERSION 3.1.0)
project(lae CXX C)
set(CMAKE_CXX_STANDARD 14)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
)
set(SOURCE_FILES
...
)
set(HEADER_FILES
...
)
set( PRIVATE_HEADER_FILES
...
)
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} )
set( REQUIRED_LIBRARIES
...
)
target_link_libraries(${PROJECT_NAME} ${REQUIRED_LIBRARIES} )
SET_TARGET_PROPERTIES(
${PROJECT_NAME}
PROPERTIES
FRAMEWORK ON
SOVERSION 0
VERSION 0.1.0
PUBLIC_HEADER "${HEADER_FILES}"
PRIVATE_HEADER "${PRIVATE_HEADER_FILES}"
ARCHIVE_OUTPUT_DIRECTORY "lib"
LIBRARY_OUTPUT_DIRECTORY "lib"
OUTPUT_NAME ${PROJECT_NAME}
)
In our projects we use a "simple" way of yours - add_library with both headers and sources.
If you add only sources, then you won't see headers in IDE-generated project.
However, when installing, we have to do it like that, using two install commands:
install(TARGETS library_name
LIBRARY DESTINATION lib)
install(FILES ${PUBLIC_HEADERS}
DESTINATION include/library_name)
If you want to do it as a single command, you can use set_target_properties with PUBLIC_HEADER, as you suggested.
Then, this kind of install is possible:
install(TARGETS library_name
LIBRARY DESTINATION lib
PUBLIC_HEADER DESTINATION include/library_name)
Choose the one you like the most and stick to it.