I wrote a project using CMake (with ninja and Visual Studio 2017 C++ compiler), with two modules lib_A and lib_B
lib_B depends one lib_A.
Both lib_B and lib_A define std::vector < size_t >.
Finally, the compiler told me: LNK2005 lib_A: std::vector < size_t > already defined in lib_B
I searched answers, and they gave the solution to add link flag /FORCE:MULTIPLE, page1 and page2.
I tried all these, but none of them work.
Use target_link_libraries
with target_link_libraries(lib_B lib_A INTERFACE "/FORCE:MULTIPLE")
compiler tells me The INTERFACE, PUBLIC or PRIVATE option must appear as the second argument, just after the target name.
with target_link_libraries(lib_B INTERFACE "/FORCE:MULTIPLE" lib_A )
compiler tells me ninja: error: '/FORCE:MULTIPLE', needed by 'lib_B', missing and no known rule to make it
Use CMAKE_EXE_LINKER_FLAGS
withset(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} "/FORCE:MULTIPLE")
compile tells me LINK : warning LNK4012: value “x64;/FORCE:MULTIPLE” is invalid, must be one of "ARM, EBC, HYBRID_X86_ARM64X64, or X86" omit this option"
Use set_target_properties
with CMake code
get_target_property(TEMP lib_B COMPILE_FLAGS)
if(TEMP STREQUAL "TEMP-NOTFOUND")
SET(TEMP "") # Set to empty string
else()
SET(TEMP "${TEMP} ") # A space to cleanly separate from existing content
endif()
# Append our values
SET(TEMP "${TEMP} /FORCE:MULTIPLE" )
set_target_properties(lib_B PROPERTIES COMPILE_FLAGS ${TEMP} )
The compiler tells me cl: command line error D8021 : invalid parameter "/FORCE:MULTIPLE"
If I change /FORCE:MULTIPLE to -Wl,--allow-multiple-definition, compiler tells me similar result.
Could anyone help me?
Does add link flag with any error?
You can use target_link_options in CMake ≥ 3.13 or set_target_properties with the LINK_FLAGS property before.
i.e. target_link_options(${PROJECT_NAME} PUBLIC $<$<CXX_COMPILER_ID:MSVC>:/FORCE:MULTIPLE>)
This also uses generator expressions to only apply the flag for MSVC.
But it seems like both your libraries are shared (DLL), but you are statically linking the runtime to both.
I don’t think that that’s a good idea.
Try either linking to the runtime dynamically for both libs if you want to dynamically link to them, or use the static runtime but build both libraries as static library too.
Adding the following line worked for me:
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /FORCE:MULTIPLE")
Related
I am trying to compile a project using cmake. I need to use an external static library in src1.c so I utilized target_link_libraries to link it to the object file. However, I am getting some complains about missing functions in src1.c which should be in the external library.
add_library(input_output OBJECT
src/src1.c
src/src2.c
src/src3.c)
find_library(EXTERNAL_LIB NAMES libexternallib.a PATHS ~/lib)
target_link_libraries(input_output PRIVATE
"${EXTERNAL_LIB}")
So I am not sure what I should do at this point. the logic sounds right at least
UPDATE1: I also added the external library directly into the linker command
add_compile_options(-Wall -Wextra --std=c99 -L~/lib -lexternallib)
add_link_options(-L~/lib -lexternallib)
but this added the library flags before the object file for the linker command, which leads to some other problem with ordering the linker arguments
It's hard to say what is really the reason for your error here, but I'll give it a try.
First of all, you don't need to link manually with add_compile_options or add_link_options. The cmake command target_link_libraries is doing that for you.
What I could imagine, because of the missing functions, that you need to add a header include directory from the dependency.
e.g.:
target_include_directories(input_output PRIVATE /path/to/external_library_header)
I'm trying to write a CMakeLists.txt to compile my Arduino-Projects to get to know cmake better.
I defined the Arduino-Core library as an imported interface and try to link my own target against it. The problem is, that, when calling make the avr-gcc is provided with the specified include-paths via -isystem instead of -I. This results in several errors.
CMakeLists.txt (minimal-version to reproduce the problem)
cmake_minimum_required(VERSION 3.1)
set(ARDUINO_DIR "/opt/arduino/arduino-1.8.13")
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_C_COMPILER ${ARDUINO_DIR}/hardware/tools/avr/bin/avr-gcc)
set(CMAKE_CXX_COMPILER ${ARDUINO_DIR}/hardware/tools/avr/bin/avr-g++)
set(CMAKE_SYSTEM_NAME NONE)
set(CMAKE_SYSTEM_PROCESSOR NONE)
add_library(Arduino::Core INTERFACE IMPORTED)
target_include_directories(Arduino::Core INTERFACE
"${ARDUINO_DIR}/hardware/arduino/avr/cores/arduino/"
"${ARDUINO_DIR}/hardware/arduino/avr/variants/eightanaloginputs/"
)
file(GLOB_RECURSE ARDUINO_CORE_SRC "${ARDUINO_DIR}/hardware/arduino/avr/cores/arduino/*.c[p]*")
file(GLOB_RECURSE ARDUINO_CORE_ASM "${ARDUINO_DIR}/hardware/arduino/avr/cores/arduino/*.S")
target_sources(Arduino::Core INTERFACE ${ARDUINO_CORE_SRC} ${ARDUINO_CORE_ASM})
project(Blinky)
set(${PROJECT_NAME}_SRC
src/Blink.cpp
)
add_executable(${PROJECT_NAME} ${${PROJECT_NAME}_SRC})
target_link_libraries(${PROJECT_NAME} Arduino::Core)
Here is my Blink.cpp:
#include <Arduino.h>
void setup()
{
pinMode(13,OUTPUT);
}
void loop()
{
digitalWrite(13,HIGH);
delay(1000);
digitalWrite(13,LOW);
delay(1000);
}
make --trace gives me the following output:
[ 5%] Building CXX object CMakeFiles/Blinky.dir/src/Blink.cpp.obj
/opt/arduino/arduino-1.8.13/hardware/tools/avr/bin/avr-g++ -isystem /opt/arduino/arduino-1.8.13/hardware/arduino/avr/cores/arduino -isystem /opt/arduino/arduino-1.8.13/hardware/arduino/avr/variants/eightanaloginputs -std=gnu++11 -o CMakeFiles/Blinky.dir/src/Blink.cpp.obj -c /tmp/so/src/Blink.cpp
As you can see, linking against the imported target includes the dependencies with -isystem even though I haven't declared SYSTEM anywhere? How can I prevent that?!
Is it, because it is an INTERFACE IMPORTED target?
I tried taget_include_directories(Arduino::Core PRIVATE ${my_include_dirs}) but obviously that is not allowed for INTERFACE-Targets.
Thanks in advance for every hint.
P.S. please note, that I'm aware, that this would not compile my arduino-code. This is just a mcve to show you my problem.
edit
I'm using Arduino-SDK 1.8.13 and cmake 3.18.2
It is because it is an imported target.
From the CMake docs on buildsystems at the section for "include directories and usage requirements" (one of the last paragraphs in the section):
When the INTERFACE_INCLUDE_DIRECTORIES of an imported target is consumed, the entries in the property are treated as SYSTEM include directories, as if they were listed in the INTERFACE_SYSTEM_INCLUDE_DIRECTORIES of the dependency. This can result in omission of compiler warnings for headers found in those directories. This behavior for Imported Targets may be controlled by setting the NO_SYSTEM_FROM_IMPORTED target property on the consumers of imported targets, or by setting the IMPORTED_NO_SYSTEM target property on the imported targets themselves.
See the docs for include_directories and target_include_directories for other documentation on SYSTEM.
As stated in the docs, you can solve this by modifying consumers of the imported target like set_target_property(${PROJECT_NAME} PROPERTIES NO_SYSTEM_FROM_IMPORTED TRUE) or set_property(TARGET ${PROJECT_NAME} PROPERTY NO_SYSTEM_FROM_IMPORTED TRUE).
But there's another option not mentioned in the docs I quoted that I think may be better: In the docs for NO_SYSTEM_FROM_IMPORTED, it says:
See the IMPORTED_NO_SYSTEM target property to set this behavior on the target providing the include directories rather than consuming them.
I think it's better because NO_SYSTEM_FROM_IMPORTED will change the "system-ness" of all imported targets that the consumer links to, which may not be desirable. Perhaps a similar argument could be made against IMPORTED_NO_SYSTEM, but I personally can't think of a good backing for such an argument at the moment.
Now my CMakeList.txt looking like this.
cmake_minimum_required(VERSION 3.6)
project(RabbitMQClient)
set(CMAKE_CXX_STANDARD 11)
set(SOURCE_FILES main.cpp)
include_directories(src)
include_directories(dependentFile)
add_executable(RabbitMQClient ${SOURCE_FILES})
find_library(SimpleAmqpClient SimpleAmqpClient.2.lib PATHS ./SimpleAmqpClientLib/SimpleAmqpClient.2.lib)
set(IMPORTED_IMPLIB ./SimpleAmqpClientLib)
target_link_libraries(RabbitMQClient PUBLIC SimpleAmqpClient)
when Linking the RabbitMQClient it complains.
cannot find -lSimpleAmqpClient
I want to use the SimpleAmqpClient library in the project,however not quite familiar with cmake not sure the find_library,IMPORTED_IMPLIB,PUBLIC in target_link_libraries was correctly used.Any help would be appreciate.
You have messed up with variables, targets and properties.
Proper usage of IMPORTED libraries for linking would be:
# This command sets *variable* SimpleAmqpClient_LIBRARY
find_library(SimpleAmqpClient_LIBRARY SimpleAmqpClient.2.lib
PATHS ${CMAKE_SOURCE_DIR}/SimpleAmqpClientLib # Specify a *directory*, not a library *file*
)
# Next, create an IMPORTED *target*
add_library(SimpleAmqpClient SHARED IMPORTED)
# And set IMPORTED_LIB *property* for this target
set_target_properties(SimpleAmqpClient PROPERTIES IMPORTED_LIB ${SimpleAmqpClient_LIBRARY})
# Then use library *target* for linking with
target_link_libraries(RabbitMQClient PUBLIC SimpleAmqpClient)
However, there some simplifications which could be done:
Normally, find_library is used when you don't know complete path to the library file. E.g., its directory may be different on different machines, or its prefix/extension may be different on different plaforms.
If you know complete path to the library, just use this path directly (e.g., assign it to the variable).
Normally, property IMPORTED_LOCATION is used for specify library to link with. Property IMPORTED_LIB is specific for Windows .dlls, when linking requires not a library file (.dll), but some other one (.lib).
However, CMake perfectly understands .lib file in IMPORTED_LOCATION property even for Windows .dlls, so your code need not distinguish SHARED Windows libraries from others: just use IMPORTED_LOCATION property in all cases.
Simplified version of the code:
# Create an IMPORTED library *target*
add_library(SimpleAmqpClient IMPORTED)
# Set IMPORTED_LOCATION *property* for this target
set_target_properties(SimpleAmqpClient PROPERTIES
IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/SimpleAmqpClientLib/SimpleAmqpClient.2.lib)
# Then use library *target* for linking with
target_link_libraries(RabbitMQClient PUBLIC SimpleAmqpClient)
I'm trying to compile the MultiBoost Library with C++11 but I can't make it work. The problem seems to be with the BZip2 Library that is used internally. More specificly there is a wrapper called Bzip2Wrapper to provide a c++ interface to the C library. All the files of the C library are included in the same folder. When using the default make file everything works but when I change
project(multiboost)
to
project(multiboost CXX)
I get the following errors:
libMultiBoostLib.a(Serialization.cpp.o): In function `Bzip2WrapperReader::open(char const*)':
Serialization.cpp:(.text._ZN18Bzip2WrapperReader4openEPKc[_ZN18Bzip2WrapperReader4openEPKc]+0x97): undefined reference to `BZ2_bzReadOpen'
Serialization.cpp:(.text._ZN18Bzip2WrapperReader4openEPKc[_ZN18Bzip2WrapperReader4openEPKc]+0xc5): undefined reference to `BZ2_bzReadClose'
libMultiBoostLib.a(Serialization.cpp.o): In function `Bzip2WrapperReader::close()': ...
The CMakeList file looks like this
# Bzip2
file(GLOB bzip2_SRCS "${BASEPATH}/Bzip2/*.cpp" "${BASEPATH}/Bzip2/*.c" "${BASEPATH}/Bzip2/*.h")
add_library(Bzip2Lib STATIC ${bzip2_SRCS})
#add_library(bzip2 SHARED ${bzip2_lib_SRCS})
...
# adding library to the exec
target_link_libraries(multiboost MultiBoostLib Bzip2Lib)
Any ideas what could go wrong? I don't even know what the problem is.
Thanks!
This does not look like a C++11 error but an error in the Build system.
I have not looked at the Code, but from the output you added something like this
target_link_libraries(MultiBoostLib PUBLIC Bzip2Lib)
should add the missing dependency from libMultiBoostLib on libBzip2Lib.
I found the problem. I was adding "CXX" to my project description which disabled the use of C. Therefore the libraries (in C) could not be compiled. Changing it to "project(name C CXX)" solved this issue. I then also needed to include the line "set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")" to enable C++11 support. Now everything is working.
Thanks a lot!
I was wondering if there is an Xcode or GCC preprocessor symbol for the target name of the application.
For example if I'm building an application called "MonkeyChicken", is there a preprocessor symbol such that
printf( __TARGET_NAME__ )
outputs:
MonkeyChicken
I don't believe there is any built-in (gcc has no idea what you're building when you compile a file), but you can always create one using GCC_PREPROCESSOR_DEFINITIONS in an xcconfig file (you are using xcconfig, right?) Something like this should work as you indicate above:
GCC_PREPROCESSOR_DEFINITIONS = __TARGET_NAME__=\"$(PRODUCT_NAME)\"