cmake target created with ADD_CUSTOM_TARGET() generated multiple times in parallel mode - cmake

I have project name libtld where I first create a tool¹:
project(tld_parser)
add_executable(${PROJECT_NAME}
../tools/tldc.cpp
tld_compiler.cpp
tld_file.cpp
tld_strings.c
)
Then I use that tool to generate the tld_data.c file, which is a table with all the TLDs (in my newer version of the library, this is a copy of the RIFF/TLDS binary file which is to be compiled internally as a fallback). Here is the add_custom_command() I use to generate said file:
project(tld_data)
set(TLD_DATA_C ${PROJECT_BINARY_DIR}/tld_data.c)
file(GLOB_RECURSE TLD_FILES ${CMAKE_SOURCE_DIR}/conf/tlds/*.ini)
add_custom_command(
OUTPUT ${TLD_DATA_C}
COMMAND "tld_parser"
"--source"
"${CMAKE_SOURCE_DIR}/conf/tlds"
"--verify"
"--output-json"
"--include-offsets"
"--c-file"
"${TLD_DATA_C}"
"${PROJECT_BINARY_DIR}/tlds.tld"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
MAIN_DEPENDENCY tld_parser
DEPENDS ${TLD_FILES}
)
add_custom_target(${PROJECT_NAME} ALL DEPENDS ${TLD_DATA_C})
define_property(SOURCE
PROPERTY GENERATED
BRIEF_DOCS "The tld_data.c file is a table of all the TLDs defined in conf/tlds/... .ini files."
FULL_DOCS "In the new version, the tld_data.c is always generated on the fly since it can be done with just C/C++."
)
Next I want to generate the tld dynamic and static libraries.
set(LIBTLD_SOURCES
tld.cpp
tld_compiler.cpp
${TLD_DATA_C}
tld_domain_to_lowercase.c
tld_emails.cpp
tld_file.cpp
tld_object.cpp
tld_strings.c
)
##
## TLD library
##
project(tld)
configure_file(
tld.h.in
${PROJECT_BINARY_DIR}/tld.h
)
add_library(${PROJECT_NAME} SHARED
${LIBTLD_SOURCES}
)
set_target_properties(${PROJECT_NAME} PROPERTIES
VERSION ${LIBTLD_VERSION_MAJOR}.${LIBTLD_VERSION_MINOR}
SOVERSION ${LIBTLD_VERSION_MAJOR}
)
install(
TARGETS ${PROJECT_NAME}
LIBRARY DESTINATION lib
COMPONENT runtime
)
install(
FILES ${PROJECT_BINARY_DIR}/tld.h
DESTINATION include
COMPONENT development
)
##
## TLD static library
##
project(tld_static)
add_library(${PROJECT_NAME} STATIC
${LIBTLD_SOURCES}
)
add_dependencies(${PROJECT_NAME}
tld
)
# We need the -fPIC to use this library as extension of PHP, etc.
set_target_properties(tld_static PROPERTIES COMPILE_FLAGS -fPIC)
install(
TARGETS ${PROJECT_NAME}
ARCHIVE DESTINATION lib
COMPONENT development
)
I added the add_dependencies() to the tld_static project to depend on tld thinking that would help my case, but I still see two run of the tld_parser...
I also had a test that directly includes the tld_data.c file (that way I can directly verify that the tld() function returns what I would expect from the exact source data).
Adding the add_dependencies() to the test worked as expected. Instead of getting the tld_data.c file created three times, now it's only twice.
project(tld_internal_test)
add_executable(${PROJECT_NAME}
tld_internal_test.cpp
)
add_dependencies(${PROJECT_NAME}
tld
)
add_test(
NAME ${PROJECT_NAME}
COMMAND ${PROJECT_NAME}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
By killing the parallel build feature, it works as expected, however, there should be no good reasons for me to do that (see this cmake question for reference).
What I'm wondering, though, is:
How do you debug such an issue?
cmake's Makefile's are enormous, so reading those is quite a killer. If I knew what to search for, I suppose I could at least look at specific targets instead of trying to understand all that's happening in there (most of which has nothing to do with my issue).
¹ The code is in a branch at the moment. I'm hoping to have it all wrapped up soon at which point it will replace the main branch. The tld() function interface will not have changed very much. The implementation, however, will be quite different, more flexible, dynamically updatable.

Related

How to install object files using CMake

There is an object library in CMake:
add_library( librevolta_runtime_x86 OBJECT
crt0.S
crti.S
crtn.S
)
install( TARGETS librevolta_runtime_x86 EXPORT revolta
OBJECTS DESTINATION "${CMAKE_INSTALL_PREFIX}/lib"
)
which is linked to my static library:
add_library( revolta STATIC )
target_link_libraries( revolta
PUBLIC
librevolta-runtime-x86
)
# Install all the revolta headers into include directory and copy the built library
install( TARGETS revolta EXPORT revolta
FILE_SET HEADERS DESTINATION "${CMAKE_INSTALL_PREFIX}/include"
ARCHIVE DESTINATION "${CMAKE_INSTALL_PREFIX}/lib"
)
# Export librevolta targets
install( EXPORT revolta DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/cmake/revolta" )
The problem is once the library revota is build and installed, the result is:
<CMAKE_INSTALL_PREFIX>/lib/objects-Debug/librevolta-runtime-x86/crt0.S.obj
<CMAKE_INSTALL_PREFIX>/lib/objects-Debug/librevolta-runtime-x86/crti.S.obj
<CMAKE_INSTALL_PREFIX>/lib/objects-Debug/librevolta-runtime-x86/crtn.S.obj
But I need to make up the installation like:
<CMAKE_INSTALL_PREFIX>/lib/crt0.o
<CMAKE_INSTALL_PREFIX>/lib/crti.o
<CMAKE_INSTALL_PREFIX>/lib/crtn.o
Note: <CMAKE_INSTALL_PREFIX> is just the placeholder for the path being specified in the CMake variable.
So I need to:
Rename the object files from *.S.obj to simple *.o
Adapt the path so that all the object files are installed into pure <CMAKE_INSTALL_PREFIX>/lib/ directory
Due to target_link_libraries(...) dependency, make up the
librevolta_runtime_x86 the member of revolta export set
Many thanks to anyone willing to help me. Martin
EDIT: The simple macro as promised
macro(install_objects OBJFILE_LIST)
foreach(OBJFILE ${${OBJFILE_LIST}})
# Available since CMake 3.20 you can use get_filename_component
# To get any specific information you can wish for about a file
# Before 3.20 you would have to use string(REGEX MATCH ...)
# get_filename_component(OBJ_DIR ${OBJFILE} DIRECTORY) <-- path
# get_filename_component(OBJ_EXT ${OBJFILE} EXT) <-- extension
get_filename_component(OBJ_NAME ${OBJFILE} NAME)
# You can uncomment the message to see what you can get
# MESSAGE(STATUS "OBJ_DIR: ${OBJ_DIR} OBJ_NAME: ${OBJ_NAME} OBJ_EXT: ${OBJ_EXT}")
# This is just an example, but you can also use ${OBJ_DIR} for example to specify a better path
install(FILES ${OBJFILE} DESTINATION [wherever_you_want_them] RENAME "${OBJ_NAME}.o")
endforeach(OBJFILE)
endmacro(install_objects)
#example usage
set(MY_OBJECTS
"test.s.obj"
"somepath/test2.S.obj"
"whatever/whatever/test3.s.OBJ"
)
install_objects(MY_OBJECTS)
The above MY_OBJECTS would give you the following variables:
-- OBJ_DIR: OBJ_NAME: test.s.obj OBJ_EXT: .s.obj
-- OBJ_DIR: somepath OBJ_NAME: test2.S.obj OBJ_EXT: .S.obj
-- OBJ_DIR: whatever/whatever OBJ_NAME: test3.s.OBJ OBJ_EXT: .s.OBJ
As you can see it is pretty robust and keeps your CMakeLists.txt clean. Just make sure to create your OBJECT_LIST using pre-defined variables such as ${CMAKE_BINARY_DIR}
This will simplify your object files installs. IMO it also answers both ad1) ad2)
Ad3) I'm not sure what is your issue here or what exactly is happening, as the question itself is focused on installing objects.
Is there any specific reason why you wish to install the intermediate object files? This is an important question because a static library when linked will be unpacked and optimized - take a look at this question/answer.
The OBJECT libraries are a useful thing in CMake when you know that some parts of your codebase would be compiled multiple times (due to the fact that it uses the same code). It makes sense to couple these parts into a reusable object library.
All of this aside: What you are looking for is
INSTALL(FILES file-name.abc DESTINATION dest-folder RENAME file-name.xyz)

CMake: Modifying library output directory of a target from a sub-project

I have a top-level CMakeLists.txt which includes another third party project from a subdirectory, like
add_subdirectory(ext/third_party/cmake)
third_party contains a library target which I want to build, but I want to modify some properties and want to avoid to modify the original CMake file. I do not want to link some of my own targets to that library, I'd rather want that third party library to be build with some modified properties and then put it into a custom output directory. So I do
set_target_properties(libthird_party PROPERTIES
# some properties that successfully get applied here
LIBRARY_OUTPUT_DIRECTORY "/my/output/dir")
I can see that other properties are successfully applied and the build is modified to my needs correctly, but the generated output library is not put into the directory I set. What could be the reason for that?
If this is a totally wrong or bad approach please also feel free to propose a better approach for my goal.
Figured it out myself with help from the comments. I was trying to modify a static library target, which is not affected by LIBRARY_OUTPUT_DIRECTORY (which only applies to dynamic libraries) but which needs the setting ARCHIVE_OUTPUT_DIRECTORY. So the corrected call is
set_target_properties(libthird_party PROPERTIES
# some properties that successfully get applied here
ARCHIVE_OUTPUT_DIRECTORY "/my/output/dir")
You have to deal with [LIBRARY, ARCHIVE, EXECUTABLE] x [Single, Multi]config generator x [Unix, Windows]way.
note: On Windows everything (.dll, .exe) is on the same directory while on Unix you generally have a bin and a lib directories.
include(GNUInstallDirs)
# Single config (e.g. makefile, ninja)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR})
if(UNIX)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
else()
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR})
endif()
# For multi-config build system (e.g. xcode, msvc, ninja-multiconfig)
foreach(OUTPUTCONFIG IN LISTS CMAKE_CONFIGURATION_TYPES)
string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG}
${CMAKE_BINARY_DIR}/${OUTPUTCONFIG}/${CMAKE_INSTALL_BINDIR})
if(UNIX)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG}
${CMAKE_BINARY_DIR}/${OUTPUTCONFIG}/${CMAKE_INSTALL_LIBDIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG}
${CMAKE_BINARY_DIR}/${OUTPUTCONFIG}/${CMAKE_INSTALL_LIBDIR})
else()
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG}
${CMAKE_BINARY_DIR}/${OUTPUTCONFIG}/${CMAKE_INSTALL_BINDIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG}
${CMAKE_BINARY_DIR}/${OUTPUTCONFIG}/${CMAKE_INSTALL_BINDIR})
endif()
endforeach()

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 Fortran Module Directory to be used with add_library

I have a CMake 3.5.2 project that creates a library: libtest.a, which is then linked to by an executable.
The source code is Fortran, and the libtest.a produces a module file: "main.mod"
The executable also needs to include this main.mod file, so to make main.mod accessible, when building the project I set the variable, CMAKE_Fortran_MODULE_DIRECTORY to a known location, and add it to the relevant include paths.
This works great when building the entire project, main.mod is built in a known location, and it is there for whatever code needs it. My usage, however, makes it necessary to only build libtest.a by itself, and leave the executable to be built by a downstream user sometimes.
The issue I am having is that when I go into the libtest source and treat it as its own CMake project, the library will build and install, but the main.mod file is always left in the BINARY_DIR and is not built in the CMAKE_Fortran_MODULE_DIRECTORY, dispite setting in the the CMakeList.txt within libtest.
Is the Fortran_MODULE_DIRECTORY only honored when add_executable() is being called? And just ignored for the library builds alone? Or am I missing something.
Thanks for the help.
EDIT: This will reproduce my issue.
test_mod.f90:
module main
implicit none
real, parameter :: pi=3.2
end module main
tt.f90:
program test
use main
implicit none
real :: a, area
a =10
area = a * 100
end program test
CMakeList.txt:
CMAKE_minimum_required( VERSION 3.5 )
enable_language( Fortran )
project( tt )
file( GLOB test_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.f90 )
add_library( tt STATIC ${test_SOURCES} )
set( CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Mod )
install( TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/Lib/ )
If I build and install the above code, I will get a libtt.a library installed in the Lib directory, however my main.mod will remain in my build directory and is not build in a Mod folder.
Here I assume that the "user" uses cmake to build the project while having access to the source of your project.
The steps to a working build.
There is a CMakeLists.txt file for libtest that specifies CMAKE_Fortran_MODULE_DIRECTORY. This should be enough for main.mod to appear there.
There is a CMakeLists.txt file for buiding the "client" program. This file should include the libtest project with add_subdirectory.
Add target_link_libraries(NAME_OF_PROGRAM NAME_OF_LIBRARY). This only takes care of the linking of libraries and is not sufficient (for solution B below anyway) for the module to be known to the client program.
Now, make your own adventure:
Solution A: in the libtest CMakeLists.txt, place the module where "all modules will go", for instance set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) (you need to do this also for the "client" CMakeLists.txt). The variable ${CMAKE_BINARY_DIR} is set by the "client" cmake invocation and will be the same for all included cmake projects. This directory will be listed in the build commands for Fortran programs.
Solution B: in the libtest CMakeLists.txt, place the module of this library in a dedicated directory. You can achieve this, for instance, with set(CMAKE_Fortran_MODULE_DIRECTORY ${PROJECT_BINARY_DIR}/modules). You need then to manually specify this location with include_directories(PATH_THAT_DEPENDS_ON_THE_NAME_OF_THE_SUBPROJECT) in the client CMakeLists.txt.
If you wish the library to be installable, you need to specify paths for installing the library and the module file. With Fortran, you should think of this with the target OS, compiler and architecture in mind.
Links to the CMake documentation:
PROJECT_BINARY_DIR
CMAKE_Fortran_MODULE_DIRECTORY
CMAKE_BINARY_DIR
Following the addition of your sample code, the following modification should do it:
CMAKE_minimum_required( VERSION 3.5 )
enable_language( Fortran )
project( tt )
set( CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Mod )
file( GLOB test_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.f90 )
add_library( tt STATIC ${test_SOURCES} )
install( TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/Lib/ )
install(DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} DESTINATION ${CMAKE_CURRENT_SOURCE_DIR})
Make sure that set( CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Mod ) occurs before any add_library line.
Add install(DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}) to actually install the .mod file. Module files (as header files in C) have to installed in addition to the library file.
The setup you created is a bit unusual in that you locate everything within the source build whereas "usual" install locations are made relative to CMAKE_INSTALL_PREFIX

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.