CMake add_custom_command with target_link_libraries - cmake

For a number of reasons I have to manually generate a static library through a custom command.
However, it seems that the custom command is only executed when a target specifically requests its output files.
If I try to link the generated static library with target_link_libraries, CMake complains that it cannot find a rule to generate it.
# Building library on the fly
add_custom_command(OUTPUT mylib.a
COMMAND ...
)
add_executable(myexe main.cpp)
target_link_libraries(myexe mylib.a) # Fails miserably
I imagine I have to insert a target or dependency somehow between the add_custom_command call and the target_link_libraries one, but I cannot understand how to do so correctly.

For preserve dependency between executable and library file, you need to use full path to the library file when link with it:
target_link_libraries(my_exe ${CMAKE_CURRENT_BINARY_DIR}/mylib.a)
When use relative path, CMake expects library to be found by the linker (according to its rules), so CMake cannot adjust dependency with the library file in that case..

I've had to do this to invoke MATLAB's RTW to build DLLs for me. The function I used was add_custom_target.
add_custom_target(Name [ALL] [command1 [args1...]]
[COMMAND command2 [args2...] ...]
[DEPENDS depend depend depend ... ]
[BYPRODUCTS [files...]]
[WORKING_DIRECTORY dir]
[COMMENT comment]
[VERBATIM] [USES_TERMINAL]
[COMMAND_EXPAND_LISTS]
[SOURCES src1 [src2...]])
For you it may look like this:
add_custom_target(MyLib ALL
<Put your command here>
COMMENT "Building MyLib"
)
add_executable(MyExe main.cpp)
target_link_libraries(MyExe MyLib)
If that doesn't work I've heard that you can use add_library() to create a dummy library. Then, use set_target_properties() to create an INTERFACE property for it.
Refences:
add_custom_target

Related

With 'find_package': how do I know which variables have to be used for linking?

I use find_package() to find some library. How do I know which variables it defines, so I could use them for linking with that library?
For example, the following CMakeLists.txt successfully is run by CMake, but then make fails at the linking stage, because ${DOCOPT_LIBRARIES} was empty.
cmake_minimum_required(VERSION 3.1)
project(test)
set(PROJECT_LIBS "")
find_package(docopt REQUIRED)
include_directories(${DOCOPT_INCLUDE_DIRS})
set(PROJECT_LIBS ${PROJECT_LIBS} ${DOCOPT_LIBRARIES})
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} ${PROJECT_LIBS})
But I have no idea what else to try?
The usage of results of find_package(XXX) depends on the script which is actually used by this command:
FindXXX.cmake.
This file is either shipped with the project which wants to use it, or with CMake. Usually, such "Find" scripts start with some description about their usage.
XXXConfig.cmake or xxx-config.cmake.
"Config" scripts are usually installed with the package. Normally, they are generated by CMake using specific CMake commands. You need to consult the package's documentation for use such scripts properly.
If the package doesn't document the usage of its "Config" script, then you may only guess about its usage.
Reading a "Config" script itself could be difficult: CMake includes into it a lot of "sugar", which helps portability and compatibility.
Normally, a "Config" script provides some IMPORTED library target(s), which can be used with target_link_libraries call.
One may scan the project's CMakeLists.txt for a line like
install(TARGETS <targets...> EXPORT ...)
Here <targets...> denotes targets, which can be accessed after find_package() call.
Also scan for a line like
install(EXPORT ...)
if it has NAMESPACE option, then its value prepends all exported targets.
Example:
For docopt package, its CMakeLists.txt contains a line
install(TARGETS docopt EXPORT ${export_name} DESTINATION ${CMAKE_INSTALL_LIBDIR})
thus its docopt target is exported.
As installation of the export file
install(EXPORT ${export_name} DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/docopt")
doesn't contain NAMESPACE option, the export target is accessible with its original name:
find_package(docopt REQUIRED)
...
add_executable(${PROJECT_NAME} main.cpp)
# Use *IMPORTED* 'docopt' target
target_link_libraries(${PROJECT_NAME} docopt)

Can not generate a CMake command

My CMakeLists.txt:
cmake_minimum_required(VERSION 2.6)
project(main)
SET(MAIN main)
SET(MAIN_OUT "${CMAKE_CURRENT_BINARY_DIR}/out.txt")
add_executable(${MAIN} main.cpp)
# command is unknown
add_custom_command(OUTPUT ${MAIN_OUT}
POST_BUILD
COMMAND ./${MAIN} > ${MAIN_OUT}
DEPENDS ${MAIN}
)
After compiling, I just want to be able to type
make out.txt
However, cmake seems to be unaware of this target ("no rule"). In the build directory, a call of
grep out.txt -r *
finds no files containing out.txt. How can I make my target callable? I know this has probably asked before, but I have not found it.
If you want to be able to type "make out.txt", you probably want add_custom_target instead of add_custom_command. This creates a target which can be built, and in building executes the specified commands.
Rather than call this target "out.txt" which would misleadingly make it look like a text file instead of a target, I'd recommend something more like "RunMain" or "GetOutputOfMain".
If you can specify a recent version of CMake as the minimum, you can use "generator expressions" within the command part of your add_custom_target call. This isn't documented for add_custom_target, but you can read about generator expressions in the docs for add_custom_command. I'm not sure what the minimum required version of CMake should be set to in order to have generator expressions available.
So, your CMakeLists.txt could be changed to something like:
cmake_minimum_required(VERSION 2.8.10)
project(Test)
add_executable(MyExe main.cpp)
set(MainOut "${CMAKE_CURRENT_BINARY_DIR}/out.txt")
add_custom_target(RunMain $<TARGET_FILE:MyExe> > ${MainOut}
COMMENT "Running MyExe with output redirected to ${MainOut}")
# Ensure MyExe is built before trying to build the custom target
add_dependencies(RunMain MyExe)
Then just do make RunMain to generate out.txt.
If you don't want to specify such a high minimum version, you can use the obsolete LOCATION target property instead:
get_target_property(MyExeLocation MyExe LOCATION)
add_custom_target(
RunMain ${MyExeLocation} > ${MainOut}
COMMENT "Running ${MyExeLocation} with output redirected to ${MainOut}")

CMake to produce -L<path> -l<lib> link flags for static libraries

I'm using CMake 2.8 in order to build an application based on MQX OS (using CodeWarrior).
The CMake project basically builds a set of static libraries (let's say LIB1 and LIB2).
I then reference these libraries in the final executable cmake rule:
target_add_executable(X ${some_sources})
target_link_libraries(X LIB1 LIB2)
My problem is that some symbols are defined in more that one library.
Thus, a link command like:
mwldarm <args> -o <output> <objects> /path/to1/libLIB1.a /path/to2/libLIB2.a
would lead to multiple definition of symbols error.
Instead, I would like CMake to generate a link command like:
mwldarm <args> -o <output> <objects> -L/path/to1 -L/path/to2 -lLIB -lLIB2
Question: How to get the following variables from CMAKE?
Libraries directories flags (ex: -L/path/to1 -L/path/to2)
Libraries link flags (ex: -lLIB -lLIB2)
I've read stuff concerning RPATH but it seems to concern shared libraries only. Am I right?
Thanks for advance.
I do appreciate.
It seems that policy CMP0003 may be what you need.
To use it add the following line near the beginning of your CMakeLists.txt:
CMAKE_POLICY( SET CMP0003 OLD )
Another possibility is to directly set the dependencies and search path, however it's not the cleanest way. Assuming you libraries are called liba.a and libb.a, then:
LINK_DIRECTORIES( ${paths_to_search_for} )
TARGET_ADD_EXECUTABLE(X ${some_sources} )
ADD_DEPENDENCIES(X LIB1 LIB2)
TARGET_LINK_LIBRARIES(X a b )
Note that in this case a and b are not cmake targets, therefore a little machinery is needed to correctly set the dependencies.
Part of the design of CMake is that it links with full paths. Why is that a problem?
Toggling the behavior with the policy is not the correct approach.
http://www.cmake.org/gitweb?p=cmake.git;a=commitdiff;h=cd4fa896b
I think CMP0003 is used to switch on/off the function of adding searching path automatically as described in the official document
Libraries linked via full path no longer produce linker search paths.
rather than about replacing the path name with -l.
When linking a library, if the library is a target CMake known, CMake always replaces related -L and -l options with the library's path name. This may not be a problem for linking static libraries. But for a executable to link a shared library, it's may be a problem. Then I found a hacking method, code like below, to solve the problem linking a shread library using -L and `-l' rather than absolute path.
# Find out the link.txt
set(LINK_TXT "${CMAKE_BINARY_DIR}/${ToLinkLib}/CMakeFiles/${ToLinkLIb}.dir/link.txt")
# Add the searching path into link command
add_custom_command(TARGET ${YourTarget} PRE_BUILD
COMMAND sed ARGS -ie "\"s;[[:blank:]]-l; -L${LIBRARY_OUTPUT_PATH} -l;\"" ${LINK_TXT}
DEPENDS ${LINK_TXT}
COMMENT "Hacking CMake: edit __link.txt__ to use -l instead of path to link internal library ...")
# NOTE: Dont't missing the `-l'.
target_link_libraries(${YourTarget} -l${ToLinkLib})
Of course, this is just a hacking so may not be working well with all versions of CMake.
UPDATED: why linking a shared library may be a problem?
When I run a executable cross compiled for android, which linking a shared library built by the same CMake scripts, I've encounter a problem of linking failed. After I used the above hacking method to get a new version, I can run my executable with a command like below
$ LD_LIBRARY_PATH=. ./the_exe opts

How do I tell CMake to link in a static library in the source directory?

I have a small project with a Makefile which I'm trying to convert to CMake, mostly just to get experience with CMake. For purposes of this example, the project contains a source file (C++, though I don't think the language is particularly relevant) and a static library file which I've copied from elsewhere. Assume for argument's sake that the source code to the library is unavailable; I only have the .a file and the corresponding header.
My handmade Makefile contains this build rule:
main: main.o libbingitup.a
g++ -o main main.o libbingitup.a
which works fine. How do I tell CMake to reproduce this? Not literally this exact makefile, of course, but something that includes an equivalent linking command. I've tried the obvious but naive ways, like
add_executable(main main.cpp libbingitup.a)
or
add_executable(main main.cpp)
target_link_libraries(main libbingitup.a)
as well as various things with link_directories(.) or add_library(bingitup STATIC IMPORTED) etc. but nothing so far that results in a successful linkage. What should I be doing?
Version details: CMake 2.8.7 on Linux (Kubuntu 12.04) with GCC 4.6.3
CMake favours passing the full path to link libraries, so assuming libbingitup.a is in ${CMAKE_SOURCE_DIR}, doing the following should succeed:
add_executable(main main.cpp)
target_link_libraries(main ${CMAKE_SOURCE_DIR}/libbingitup.a)
If you don't want to include the full path, you can do
add_executable(main main.cpp)
target_link_libraries(main bingitup)
bingitup is the same name you'd give a target if you create the static library in a CMake project:
add_library(bingitup STATIC bingitup.cpp)
CMake automatically adds the lib to the front and the .a at the end on Linux, and .lib at the end on Windows.
If the library is external, you might want to add the path to the library using
link_directories(/path/to/libraries/)
I found this helpful...
http://www.cmake.org/pipermail/cmake/2011-June/045222.html
From their example:
ADD_LIBRARY(boost_unit_test_framework STATIC IMPORTED)
SET_TARGET_PROPERTIES(boost_unit_test_framework PROPERTIES IMPORTED_LOCATION /usr/lib/libboost_unit_test_framework.a)
TARGET_LINK_LIBRARIES(mytarget A boost_unit_test_framework C)
I want to add to the other comments, the project name is the first argument. I had a project called cmakecompile and i wanted to add libusb to it, the code is as follows,
add_executable(cmakecompile main.c)
target_link_libraries(cmakecompile "D:/msys2/mingw64/lib/libusb-1.0.a")
the project had just only a main.c file, the first parameter in target_link_libraries is the name of your project and the second parameter is the path of the library.
Note that may help: Since i am compiling under windows, i had to install msys2 because the library you have has to be compiled with the same compiler. For example if you get libusb and try to use it in a qt-creator project, it will not work and you may get unreferenced functions, therefore i had to install msys2 and install libusb from inside msys2, install make and create a QT Cmake project and compile from Qt creator using the msys2 make.
The full cmakelists.txt is as follow
cmake_minimum_required(VERSION 3.5)
project(cmakecompile LANGUAGES C)
add_executable(cmakecompile main.c)
target_link_libraries(cmakecompile "D:/msys2/mingw64/lib/libusb-1.0.a")

Using CMake with ifort compiler

I am using CMake 2.8.7 on a Linux machine with Intel 11.0 compilers. I am trying to use CMake for the first time as I would like to build this project on both Windows and Linux machines.
I though of using a simple approach first and used a standard Hello World example:
My src/HelloWorld.f90:
!Test helloworld in Fortran using Cmake
program hello
print *, "Hello World!"
end program hello
My main CMakeLists.txt:
# States that CMake required version must be greater than 2.8.7
cmake_minimum_required(VERSION 2.8.7)
enable_language (Fortran)
project(helloworld Fortran)
add_subdirectory(src)
SET_TARGET_PROPERTIES(helloworld PROPERTIES LINKER_LANGUAGE FORTRAN)
My src/CMakeLists.txt:
cmake_minimum_required(VERSION 2.8.7)
# Include the directory itself as a path to include directories
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# For a large number of source files you can create it in a simpler way
# using file() function:
file(GLOB helloworld_SOURCES *.f90)
I still get an error which says CMAKE_FORTRAN_LINK_EXECUTABLE variable missing. I looked at Abinader's CMake tutorial#1, but haven't had success so far.
any suggestions?? Thanks in advance !
Not a direct answer, as I've never used fortran with cmake, but I can see a few issues here.
First of all: where is your target helloworld defined? project is not a target.
Secondly: where do you use helloworld_SOURCES variable?
Try a more regular way. In your src/CMakeLists.txt add line at the end of file with:
add_executable(helloworld ${helloworld_SOURCES})
Also remove SET_TARGET_PROPERTIES(helloworld PROPERTIES LINKER_LANGUAGE FORTRAN) from main one as it should not be necessary.
Last advice: try not to use file(GLOB ). It is better to define list of all files manualy.
Probably the upper-case "FORTRAN", when setting the linker language is the problem. Try writing it as "Fortran" as in the enable_language statement. CMake derives the variables it uses from the language and this causes CMake to look for CMAKE_FORTRAN_LINK_EXECUTABLE instead of CMAKE_Fortran_LINK_EXECUTABLE.
As also mentioned by Michal, the add_executable has to be added to your CMakeLists.txt.
I tested your issue with the following CMake configurations files
main CMakeLists.txt:
# States that CMake required version must be greater than 2.8.7
cmake_minimum_required(VERSION 2.8.7)
enable_language (Fortran)
project(helloworld Fortran)
add_subdirectory(src)
src/CMakeLists.txt:
cmake_minimum_required(VERSION 2.8.7)
add_executable(helloworld HelloWorld.f90)
under Linux for following versions:
ifort (IFORT) 16.0.0.20150815
cmake version 2.8.12.2
I prefer to use cmake-gui. There you can define the ifort compiler as follows:
After definition of source code and binary folder, e.g. build, press "Configure" and select
Click "Next" and define the following compilers
Click "Finish" and "Generate".
Go to build/src folder and execute make. The helloworld executable is generated with ifort successfully and could be called here.
Hint: If ifort is already the default native compiler on your Linux computer then you don't have to specify it in cmake-gui and can go ahead with the first option "Use default native compilers".
Hope it helps.
Let's try this step-by-step:
1) Your Fortran Hello, world is OK!
src/hello.f90
!Test helloworld in Fortran using Cmake
program hello
print *, "Hello World!"
end program hello
2) Now let's write the "inner" CMakeLists.txt
src/CMakeLists.txt
add_executable(helloworld hello.f90)
set_target_properties(
helloworld
PROPERTIES
LINKER_LANGUAGE Fortran
RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/build)
Here we've created an executable file, which you haven't in your question. Also, we've set its linker language to Fortran (it's case-sensitive parameter!) and the output directory for the compiled file.
3) Now we'll create the "main" CMakeLists.txt
CMakeLists.txt
# States that CMake required version must be greater than 2.8.7
cmake_minimum_required(VERSION 2.8.7)
project(helloworld Fortran)
add_subdirectory(src)
Here we've specified the src subdirectory with inner CMakeLists.txt and the compiler language - it's enough to use project() function, there's not need to use it together with enable_language().
4) Finally, let's build our code in out-of-source manner and run it!
cmake -S . -B build
cmake --build build
./build/helloworld