cmake add_library with minimum cpp standard - c++

library needs atleast cpp14 but if cpp17 is available, it unlocks more features. I tried the below.
cmake_minimum_required(VERSION 3.10)
project(dummy)
add_library(awesomelib STATIC awesomelib.cpp awesomelib.h)
target_compile_features(awesomelib INTERFACE cxx_std_14)
add_executable(dummy14 main.cpp)
target_link_libraries(dummy14 awesomelib)
target_compile_features(dummy14 PRIVATE cxx_std_14)
add_executable(dummy17 main.cpp)
target_link_libraries(dummy17 awesomelib)
target_compile_features(dummy17 PRIVATE cxx_std_17)
What I want is:
dummy14 to compile using the C++14 standard
dummy17 and awesomelib to compile using the C++17 standard
But what happens is that awesomelib is compiled (only once) according to the c++14 std.
Update
I have shown the executables in the same file for simplicity. In real setup, the library would be in a separate project/repository and the users will be in a different project. I am looking for how the library can advertise its minimum requirements. i.e it needs at least c++14 standard and depending on the user, it has to be compiled with whatever latest version the user has.

I added a few changes to your CMkaeLists.txt file and it seems to work now
cmake_minimum_required(VERSION 3.10)
project(dummy)
set (CMAKE_CXX_STANDARD 17)
add_library(awesomelib STATIC awesomelib.cpp awesomelib.h)
target_compile_features(awesomelib INTERFACE)
add_executable(dummy17 main.cpp)
target_link_libraries(dummy17 awesomelib)
target_compile_features(dummy17 PRIVATE cxx_std_17)
set (CMAKE_CXX_STANDARD 14)
add_executable(dummy14 main.cpp)
target_link_libraries(dummy14 awesomelib)
target_compile_features(dummy14 PRIVATE cxx_std_14)

Related

Cmake Top level project has two dependent projects, one child is also dependent on the other child

I have Project A at the top.
Project A requires library B and library C.
Library B also requires library C by itself.
So, in short. How can I link libraries B and C up to A by just linking B?
I have it working now by individually linking all the libraries, but I feel like there is redundancy I could get rid of.
This is part of the CMAKE for Project A at the top:
find_package(libB REQUIRED)
include_directories(${libB_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} ${libB_LIBRARY})
find_package(libC REQUIRED)
include_directories(${libC_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} ${libC_LIBRARY})
But also within libB I have this in its CMAKE:
find_package(libC REQUIRED)
include_directories(${libC_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} ${libC_LIBRARY})
I feel like there is a better way to do this, please let me know. Thank you for any help.
You can use target_include_directories (documentation) to specify include directories for your target instead of specifying them for all targets of the current directory using include_directories. Among other things, this gives the ability to provide visibility specifiers to your includes dirs, which control whether your include dirs will affect only current target (PRIVATE), only those targets which link to your target (INTERFACE) or both (PUBLIC). The same specifiers are used similarly for target_link_libraries.
So, you can use PUBLIC to avoid duplicating your lib C includes and libraries, and since it is the default specifier, you can omit it. Then, parts of your CMakeLists may look like this:
In project A CMakeLists.txt:
find_package(libB REQUIRED)
target_include_directories(${PROJECT_NAME} ${libB_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} ${libB_LIBRARY})
In lib B CMakeLists.txt:
find_package(libC REQUIRED)
target_include_directories(${PROJECT_NAME} ${libC_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} ${libC_LIBRARY})
If you, e.g., want only lib B to link to lib C, but leave public 'inheritance' of include directories, you could change the last line of lib B CMakeLists.txt excerpt:
target_link_libraries(${PROJECT_NAME} PRIVATE ${libC_LIBRARY})

Cmake - select different c++ standard for different sources

As the title suggest, I'd like to use cmake to build a project, and depending on the source file, enforcing a different c++ standard.
The reason is : I am working on a library and would like to make it c++03 compliant for compatibility, but would like to use Google test suite which requires c++11.
So the unit tests would be compiled with c++11, but I'd like my library to fail at compilation if there is reference to a c++11 only feature.
So just do that - ompile your library with one standard, and your tests with the other. Nowadays, https://stackoverflow.com/a/61281312/9072753 method should be preferred.
add_library(mylib lib1.cpp)
set_target_properties(mylib
PROPERTIES
CXX_STANDARD 03
CXX_EXTENSIONS off
)
add_executable(mytest main.cpp)
set_target_properties(mytest
PROPERTIES
CXX_STANDARD 11
CXX_EXTENSIONS off
)
target_link_libraries(mytest PRIVATE mylib)
add_test(NAME mytest COMMAND mytest)
You can do that, if they are used by different targets, using add_targrt_compile_options , as follows.
Assuming you want to use c++03 with the lib and c++11 with the exec, you may use something like what follows
add_executable(exec1 main.cpp)
add_library(lib1 STATIC lib1.cpp)
target_compile_options(lib1 PRIVATE -std=c++03)
target_compile_options(exec1 PRIVATE -std=c++11)

How to use include_directories correctly in cmake?

I have two cmake files:
CMakeLists.txt:
cmake_minimum_required(VERSION 3.13)
project(as_math_engine)
set(CMAKE_CXX_STANDARD 14)
include_directories(include/as_math_engine)
add_library(as_math_engine evaluable.h)
add_subdirectory(tests)
tests/CMakeLists.txt:
include_directories(libs)
add_executable(as_math_engine_tests src/main.cpp)
And I have this include/as_math_engine/evaluable.h file but, CMake tells me:
Cannot find source file: evaluable.h
Why is it? And how can I solve this problem?
include_directories() is best used to set include paths for multiple targets within a project, target_include_directories() is usually preferred.
There are probably better ways of setting up as_math_engine if it is going to be a header-only library.
You also need to use add_library(as_math_engine include/as_math_engine/evaluable.h) because the add_library() doesn't search for files.
Header Only Library shows how to set this up and use it to avoid these kinds of problems.
Alternatively delete both include_directories() and use target_include_directories(as_math_engine_tests PRIVATE "${CMAKE_SOURCE_DIR}/include/as_math_engine" libs) so that the as_math_engine_tests uses the proper include path.

The target name "C:/path/to/lib/file.lib" is reserved or not valid for certain CMake features

I'm trying to write a simple C++ program and include the library for GLFW.
I'm using the CLion IDE and the following is my CMakeLists.txt:
cmake_minimum_required(VERSION 3.12)
project(Engine)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_VERBOSE_MAKEFILE TRUE)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib/;)
add_library(${CMAKE_CURRENT_SOURCE_DIR}/lib/GLFW/libraries/glfw3.lib;)
add_executable(Engine main.cpp)
The error CMake throws is:
CMake Error at CMakeLists.txt:8 (add_library):
The target name
"C:/Users/callu/CLionProjects/Engine/lib/GLFW/libraries/glfw3.lib"
is reserved or not valid for certain CMake features, such as generator
expressions, and may result in undefined behavior.
My code is:
#include <iostream>
#include "GLFW/glfw3.h"
int main() {
if (!glfwInit)
std::cout << "Failure" << std::endl;
else
std::cout << "Success!" << std::endl;
return 0;
}
I've looked around and found one possible reason is because the value of the library is used elsewhere by CMake. For example, if I used "test" as a library CMake would complain because test is a predefinied thing. I fail to see how the path to the glfw3 library is predefined by CMake. (If that's the issue.)
If it matters, my directory structure is:
C:/Users/callu/CLionProjects/Engine/
../lib
../GLFW
glfw3.h
glfw3Natives.h
../libraries
glfw3.lib
glfw3dll.lib
glfw3.dll
CMakeLists.txt
main.cpp
Your problem is wrong usage of add_library. This statement is used to create a library (.dll/.lib on Windows or .so/.a on Unix) out of some set of sources, and you are trying to use it to list already compiled/linked libraries to be used for linking of your executable.
If my assumptions are correct, your CMakeLists.txt file should look as follows:
cmake_minimum_required(VERSION 3.12)
project(Engine)
set(CMAKE_CXX_STANDARD 17)
set (CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_VERBOSE_MAKEFILE TRUE)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib)
add_executable(Engine main.cpp)
target_link_libraries (Engine ${CMAKE_CURRENT_SOURCE_DIR}/lib/GLFW/libraries/glfw3.lib)
Note target_link_libraries statement after add_executable. This is what you need to tell CMake which libraries you will use for linking.
BTW,
in case you want to enforce the C++17 standard, setting just the CMAKE_CXX_STANDARD variable is not enough, since it allows for (quiet) decay. In that case you also need to set variable CMAKE_CXX_STANDARD_REQUIRED.
At the end, I would strongly suggest to read CMake documentation:
cmake-buildsystem(7) - CMake 3.12.3 Documentation
And for your case:
add library - CMake 3.12.3 Documentation
target_link_libraries - CMake 3.12.3 Documentation
If this is a target that you have already built outside the project, you have to import it with add_library(<name> IMPORTED). Besides, add_library's first argument is always <name>. That means you cannot put a path there.
I found a fix! I moved the ../../libraries directory into a common /lib directory. I also replaced lines 6 & 7 in the CMakeLists.txt with
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include;)
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib)

How to overwrite macro definition in CMake

I am on Windows 10, Visual Studio 2015. Suppose I am building library A with CMakeLists looking like
cmake_minimum_required(VERSION 3.7)
project(A)
set(DLLIMPORT "__declspec(dllimport)")
set(DLLEXPORT "__declspec(dllexport)")
set(PROJECT_SRCS
${PROJECT_SOURCE_DIR}/src/TestA.cpp)
set(PROJECT_INCS
${PROJECT_SOURCE_DIR}/include/TestA.h)
add_library(${PROJECT_NAME} SHARED ${PROJECT_SRCS} ${PROJECT_INCS})
target_compile_definitions(${PROJECT_NAME} INTERFACE
WINDOWS_DLL_API=${DLLIMPORT})
target_compile_definitions(${PROJECT_NAME} PRIVATE
WINDOWS_DLL_API=${DLLEXPORT})
target_include_directories(${PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include>)
I am defining the macro WINDOWS_DLL_API as dllexport when it's building library A, and defining WINDOWS_DLL_API as dllimport for external applications that is linking library A. The problem is when I have another library B that is also linking A, I don't know how to overwrite WINDOWS_DLL_API back to dllexport. Below is my attempt of my CMakeLists for library B,
cmake_minimum_required(VERSION 3.7)
project(B)
set(DLLEXPORT "__declspec(dllexport)")
set(PROJECT_SRCS
${PROJECT_SOURCE_DIR}/src/TestB.cpp)
set(PROJECT_INCS
${PROJECT_SOURCE_DIR}/include/TestB.h)
add_library(${PROJECT_NAME} SHARED ${PROJECT_SRCS} ${PROJECT_INCS})
target_include_directories(${PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include>)
target_link_libraries(${PROJECT_NAME} A)
# does not work
target_compile_definitions(${PROJECT_NAME} PRIVATE
WINDOWS_DLL_API=${DLLEXPORT})
What is the right way to do it?
Concept of INTERFACE option for command target_compile_definitions (and for other target_* CMake commands) is to enforce something for all users of the library, both executables and libraries.
Intention to clear enforcement for at least single library's user means that the conception is used in a wrong way. And other approaches should be used instead.
In given case, you need to use different macro names for libraries A and B. And it is better to remove INTERFACE option completely, so even non-CMake users of your library will be happy.
TestA.h:
#ifdef BUILD_A
#define WINDOWS_DLL_API_A __declspec(dllexport)
#else
#define WINDOWS_DLL_API_A __declspec(dllimport)
#endif
...
WINDOWS_DLL_API_A void foo(void);
...
TestB.h:
#ifdef BUILD_B
#define WINDOWS_DLL_API_B __declspec(dllexport)
#else
#define WINDOWS_DLL_API_B __declspec(dllimport)
#endif
// Assume usage of A is here.
#include <TestA.h>
...
WINDOWS_DLL_API_B void bar(void);
A/CMakeLists.txt:
cmake_minimum_required(VERSION 3.7)
project(A)
...
add_library(${PROJECT_NAME} SHARED ...)
target_compile_definitions(${PROJECT_NAME} PRIVATE "BUILD_${PROJECT_NAME}=1")
B/CMakeLists.txt:
cmake_minimum_required(VERSION 3.7)
project(B)
...
add_library(${PROJECT_NAME} SHARED ...)
target_compile_definitions(${PROJECT_NAME} PRIVATE "BUILD_${PROJECT_NAME}=1")
target_link_libraries(${PROJECT_NAME} A)
See also this answer, which provides more detailed header, which works on Windows platforms too.
Note, that when the library B includes header from A, it treats foo() as imported, and this is correct: the function is defined in A, not in B. With your approach (even if you would manage to redefine WINDOWS_DLL_API for B), library B would incorrectly treat foo() as exported.
This is an advantage of the conception: intention to overcome a conception signals that you do something wrong.
Just wanted to add my piece of code I'm using (compatible with CMake versions prior to 2.8.12).
In my root CMakeLists.txt file I have:
if (MSVC)
add_definitions(-DWINDOWS_DLL_API=__declspec\(dllexport\))
else()
add_definitions(-DWINDOWS_DLL_API=)
endif()
In the (sub-)project's CMakeLists.txt using the DLL I've put:
if (MSVC)
remove_definitions(-DWINDOWS_DLL_API=__declspec\(dllexport\))
add_definitions(-DWINDOWS_DLL_API=__declspec\(dllimport\))
endif()
The MSVC checks are necessary in my case because I also cross-compile.
Reference
CMake - override compile flags for single files