How do I pass cxxflags to Boost libraries from within my jamfile? - c++

I have a project with some requirements, one of which is to set the c++11 compiler/linker flags:
jamroot.jam:
project
: requirements
<toolset>clang:<cxxflags>"-stdlib=libc++ -std=c++11"
<toolset>clang:<linkflags>"-lc++"
# ... etc
;
lib mylibrary
: #sources
[ glob source/*.cpp ]
/boost/filesystem
/boost/system
/boost/thread//boost_thread
;
The library-specific sources are being compiled with the necessary c++11 flags, however the Boost libraries mentioned do not. This is causing no end of binary incompatibilities and linker errors.
I do not want to specify the cxxflags explicitly in either the user-config or the command line. I would like to make sure the jamroot/jamfiles are all that is necessary to build the project properly.
How do I "pass in" the desired cxxflags to the dependent Boost libraries?
Update: I recently tried using an alias to accomplish my goal. From the docs:
Another use of the alias rule is to change build properties. For example, if you want to use link statically to the Boost Threads library, you can write the following:
alias threads : /boost/thread//boost_thread : <link>static ;
However setting this up for boost_filesystem and rebuilding, say, path.cpp still omits the properties I am trying to build with.

This was resolved by setting up a feature (thanks to Steven Watanabe):
feature.feature cpp11 :
on :
composite optional propagated
;
feature.compose <cpp11>on :
<cxxflags>"-stdlib=libc++ -std=c++11"
<define>BOOST_NO_CXX11_NUMERIC_LIMITS=1
<linkflags>"-lc++"
;
project
: requirements
<cpp11>on
# ... etc
;
Apparently this is the only way to get variables to propagate to dependent libraries.

Related

CMake: Absolute lib-pathnames given to target_link_library( PRIVATE ) are exported, appear in generated INTERFACE_LINK_LIBRARIES. How do I?

In a large CMake/C++ project our major library target Foo is being added. It has many statically linked dependencies that are provided as absolute-paths to target_link_libraries(). Now I'm writing the CMake to export this library once built, including the generated CMake so a library consumer can simply use CMake find_package(). However the generated FooTargets.cmake is embeding the absolute-paths of the link-libraries instead of just the library names.
Additionally, I've already created an add_library(BarCommon INTERFACE), to wrap up the referenced 3rd-party static libs separately and that seems to be working and the client-application consumed it. That accounts for 90% of the libraries also mentioned by FOO. So really FOO's exported INTERFACE_LINK_LIBRARIES should be about 3 file names instead of 40 absolute-pathnames.
I'm confused, why is the target_link_libraries(FOO PRIVATE ${libs} is defining the exported 'INTERFACE_LINK_LIBRARIES' property?
I would like that generated INTERFACE_LINK_LIBRARIES property to be only library filenames, and optimally have finer control over which filenames end up in it. How do I control this?
Code:
Inside a large CMake Macro for generating targets we have this:
-
I call it to create library/target FOO.
_TARGET_ARGS_LIB_DEPENDS will resolve to ABSOLUTE FILE PATHNAMES for about 40 statically linked library dependencies.
add_library(${TargetName} STATIC ${AllSources} ${_TARGET_ARGS_OBJ_FILES})
<snip>
target_link_libraries(${TargetName}
PRIVATE
$<${GCC}:-Wl,--start-group>
${_TARGET_ARGS_LIB_DEPENDS}
$<${GCC}:-Wl,--end-group>
)
where that longish function returns I have just added these blocks:
target_link_directories(FOO
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
$<INSTALL_INTERFACE:foo/lib>
)
target_include_directories(FOO
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:foo/include>
)
install(TARGETS Foo
EXPORT
FooTargets
DESTINATION
foo/lib
)
There is some more 'install' boilerplate at the end of my CMakeLists.txt
After I run CMAKE, build the project and run the 'INSTALL' target CMake generated from Visual Studio I can examine generated file: local_install\Release\Windows\Foo\lib\cmake\Foo\FooTargets.cmake shows:
set_target_properties(Nexidia.Workbench.StaticLib.StaticLib PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/Foo/include"
INTERFACE_LINK_DIRECTORIES "${_IMPORT_PREFIX}/Foo/lib"
INTERFACE_LINK_LIBRARIES "\$<LINK_ONLY:\$<0:-Wl,--start-group>>;C:/full/path/to/libA.lib;C:/full/path/to/libB.lib \$<LINK_ONLY:\$<0:-Wl,--end-group>>"
)
(with the full paths to about 40 libraries).
PS. I'm reading Professional CMake: A Practical Guide, so if someone could cite the chapter/sub-section that answers this it might help.
This is because your library is static and so it cannot be correctly linked to consumers without including its dependencies on consumers' link lines. The same thing happens with the shared dependencies of shared libraries. Static dependencies of shared libraries are fully "absorbed" into the shared library and so they don't appear.
CMake is aware of all this, and so to respect the other semantics of PRIVATE, it inserts the $<LINK_ONLY:...> generator expression. This ensures that the libraries are used for linking only and that any associated INTERFACE_* properties are not propagated.
In other words, CMake is behaving correctly here.
You should resolve this by creating and linking to targets for libB and friends that you can either export as part of your main build or import into both your main build and via find_dependency in your distributed package, along with the libraries themselves, of course.
The best advice I can give you is to always link to targets in CMake, never to paths or raw library flags.

Cmake: target_compile_definitions with find_package

I have a package, libtorch, that depends on libraries that use the keyword slots for some function (e.g. in Aten). In the meantime, the project that I am using is based on Qt, which use slots as a special keyword (macro), which then interferes with torchlib.
The solution to avoid such interference is to add the QT_NO_KEYWORDS definition when importing the external library that enters in conflict with Qt: Turning the no_keyword (Qt option) ON in a cmake file to work with boost::signals
It has then to be done only on the code that depends on libtorch and not the rest, so I tried several ways to add it, like CMake Add and Remove a Macro definition to compile Shared Library/Executable and Add target properties to an existing imported library in CMake, but I can't get it to work.
target_compile_definitions seems to be available after cmake 3.11 on "IMPORTED targets"(on cmake terminology).
So why the following cmakelist extract doesn't work? (in that case, the normal QT slots code are not properly recognized as a macro):
find_package (Torch REQUIRED PATHS ${Torch_DIR})
add_executable( ${PROJECT_NAME} ${header_files} ${source_files} ${hpp_files} ${moc_list} ${generated_ui_files} ${generated_qrc_files} )
target_link_libraries(${PROJECT_NAME} ${Qt_LINK_LIBRARIES})
#add_definitions(-DQT_NO_KEYWORDS)
target_link_libraries(${PROJECT_NAME} ${TORCH_LIBRARIES})
#remove_definitions(-DQT_NO_KEYWORDS)
target_compile_definitions(torch INTERFACE QT_NO_KEYWORDS)
Possible Solutions
Option 1:
Use QT_NO_KEYWORDS and replace slots and other keywords with their equivalent uppercase macros, e.g. Q_SLOTS.
Option 2:
Don't use QT_NO_KEYWORDS and wrap the parts of your C++ code with conflicting keywords like this:
#undef slots
#include <torch/torch.h>
#define slots Q_SLOTS
Explanations
The INTERFACE keyword in target_compile_definitions() tells CMake that all targets that depend on the torch library need the QT_NO_KEYWORDS macro defined as well. This is why all sources of your executable will be compiled with -DAT_NO_KEYWORDS and the slots keyword will not be defined.
I suppose you tried to fix your problem with the commented out add_definitions()/remove_definitions() calls. Several reason why those don't work as expected:
They only affect compilation not linking. Putting them around target_link_libraries() has no effect.
They manipulate the compile flags of all targets created in the current scope (current CMakeLists.txt), no matter if they were created before or after the command gets invoked, see the docs. This means calling add_definitions() and subsequently remove_definitions() with the same arguments will result in no flags being added at all no matter at which point in your CMakeLists.txt you call them.
You should also note that commands operating on the directory level (add_*/remove_*) are considered "old style" CMake. Modern CMake aims to only operate on the target level (target_* commands). Some more "Modern CMake" suggestions:
Use target_sources() instead of variables like header_files, source_files, etc. to add sources to your targets.
Always use target_link_libraries() with a PRIVATE, PUBLIC or INTERFACE keyword. Excerpt from Craig Scotts Professional CMake book:
The target_link_libraries() command also has a few other forms, some of which have been part of CMake from well before version 2.8.11. [...] Their use is generally discouraged for new projects. The full form shown previously with PRIVATE, PUBLIC and INTERFACE sections should be preferred, as it expresses the nature of dependencies with more accuracy. [...] The above form [(without PRIVATE/PUBLIC/INTERFACE)] is generally equivalent to the items being defined as PUBLIC, but in certain situations, they may instead be treated as PRIVATE. In particular, if a project defines a chain of library dependencies with a mix of both old and new forms of the command, the old-style form will generally be treated as PRIVATE.

Why CMake's add_executable requires at least one source file? [duplicate]

I would like to build an executable from static libraries (i. e. .a-files) only. This is possible, because the main() function is contained in one of these libraries.
The add_executable() function requires me to provide at least one source file. But this is not what I want to do.
There is no way to do it without a hack. You need at least one *.c or *.cpp file.
What I do is make a dummy null.cpp file (zero bytes) and use that. You can also use /dev/null but that only works on Linux.
file(WRITE null.cpp "")
add_executable(tester
null.cpp
)
target_link_libraries(tester
-Wl,--whole-archive
libtest1
libtest2
libtest3
libtest4
-Wl,--no-whole-archive
gtest_main
)
There are mainly two reasons why a source file is enforced by CMake:
To determine the LINKER_LANGUAGE from the file ending(s)
Not all compilers do support an object/library only link step (for details see below)
And if you move the main() function to library please keep the following in mind: Why does the order in which libraries are linked sometimes cause errors in GCC?
So if you build the libraries with CMake in the same project, I would recommend to change your libraries (at least the one containing your main() function) to an object library:
cmake_minimum_required(VERSION 2.8.8)
project(NoSourceForExe)
file(WRITE main.cc "int main() { return 0; }")
add_library(MyLibrary OBJECT main.cc)
add_executable(MyExecutable $<TARGET_OBJECTS:MyLibrary>)
The add_library() documentation lists a warning here:
Some native build systems may not like targets that have only object files, so consider adding at least one real source file to any target that references $<TARGET_OBJECTS:objlib>.
But those are rare and listed in Tests/ObjectLibrary/CMakeLists.txt:
# VS 6 and 7 generators do not add objects as sources so we need a
# dummy object to convince the IDE to build the targets below.
...
# Xcode does not seem to support targets without sources.
Not knowing which host OS(s) you are targeting, you may just give it a try.
References
CMake Object Lib containing main
CMake/Tutorials/Object Library

What to use instead of `qt5_use_modules`?

The Qt 5 CMake manual states that the qt5_use_modules macro is deprecated:
This macro is obsolete. Use target_link_libraries with IMPORTED targets instead.
... But qt5_use_modules does more than simply specify link libraries: it specifies include directories, necessary compile flags, and more (see the full description in the linked documentation above).
Assuming, then, that the variable QTMODULES contains some list of Qt modules that should be used for a project, what is the "recommended" way to replace the following "deprecated" CMake line?
qt5_use_modules(${myProjectName} ${QTMODULES})
The following does NOT work, primarily because it does not add any Qt include paths:
target_link_libraries(${myProjectName} IMPORTED ${QTMODULES})
Does the QTMODULES variable need to be manually iterated over, so that include_directories can be called for each individual module name? This seems like a major step backward from qt5_use_modules, which is simple and "just works." Am I missing something?
The message about using IMPORTED targets actually refers to the generated targets that Qt5's CMake modules provide for you, not that you should be setting the IMPORTED property on the target_link_libraries macro. For example, something like:
target_link_libraries(${myProjectName} Qt5::Core Qt5::Widgets)
will take care of adding all the necessary include paths, link paths, and libraries for using the Qt5Core and Qt5Widgets modules.

Using Boost.build to include a library

I am using boost.build to compile a c++ code which references a library, CGNS, but am having some difficulty with using boost.build to do so. CGNS compiles to a library, with a folder for the platform, for example [path]/LINUX for the linux build. I would like to include the library [path]/LINUX/libcgns.a in the build. I would like this to be cross-platform, so that the LINUX directory is referenced for LINUX builds and the WIN directory is used for WIN builds (I believe there are platform conditionals for this).
I managed to include the library header files, but how do I go about the conditional include of the library? My simple test Jamroot.jam, where main.cpp is just an example from the CGNS docs.
exe CGNSTest
: src/main.cpp
: <include>../Dependencies/cgnslib ;
Also, I would like to build in the CGNS library into my binary (static reference?)
Using two references, http://www.highscore.de/cpp/boostbuild/, and http://www.boost.org/doc/tools/build/doc/userman.pdf, I created something that works, but it may not be the ideal.
lib cgns
: # sources
: # requirements
<name>cgns
<target-os>linux:<search>../Dependencies/cgnslib/LINUX
<target-os>windows:<search>../Dependencies/cgnslib/WIN32
: # default-build
: # usage-requirements
<include>./../Dependencies/cgnslib ;
alias static_libraries : cgns : <link>static ;
exe CGNSTest
: src/main.cpp static_libraries
: <target-os>windows:<linkflags>/NODEFAULTLIB:MSVCRTD ;