What of CMake CLEAN_DIRECT_OUTPUT property? - cmake

What has become of the CMake CLEAN_DIRECT_OUTPUT property used in the set_target_properties command? The CMake Properties on Targets page doesn't mention it but it appears in some CMakeLists.txt files I've seen.
Googling for CLEAN_DIRECT_OUTPUT doesn't seem to give much insight into this question. At best, links like linux.die.net document what this property is for:
When a library is built CMake by default generates code to remove any existing library using all possible names. This is needed to support libraries that switch between STATIC and SHARED by a user option. However when using OUTPUT_NAME to build a static and shared library of the same name using different logical target names the two targets will remove each other's files. This can be prevented by setting the CLEAN_DIRECT_OUTPUT property to 1.
And that seems like an important function!
Searching for this property on cmake.org however, shows "no results found".

Fortunately, the cmake source code and its revision history can be found online. Searching the git log history, reveals a commit, dated May 1 2009 by Brad King, that sheds some light into this question.
This commit's log message is as follows:
ENH: Always imply CLEAN_DIRECT_OUTPUT target prop
This property was left from before CMake always linked using full path
library names for targets it builds. In order to safely link with
"-lfoo" we needed to avoid having both shared and static libraries in
the build tree for targets that switch on BUILD_SHARED_LIBS. This meant
cleaning both shared and static names before creating the library, which
led to the creation of CLEAN_DIRECT_OUTPUT to disable the behavior.
Now that we always link with a full path we do not need to clean old
library names left from an alternate setting of BUILD_SHARED_LIBS. This
change removes the CLEAN_DIRECT_OUTPUT property and instead uses its
behavior always. It removes some complexity from cmTarget internally.
Accordingly, the commit's source code changes show that functionality, documentation, and uses of CLEAN_DIRECT_OUTPUT was removed.
Bottom line appears to be: the CLEAN_DIRECT_OUTPUT property has been removed and instead its behavior is always used.

Related

Predeclare search location for anticipated find_library()-call

I want to include an external library as a subproject into my own project and link its target(s) statically against my own lib.
The said project somewhere in its CMake calls the following find-functions:
find_library(MBEDTLS_LIBRARY mbedtls)
find_library(MBEDX509_LIBRARY mbedx509)
find_library(MBEDCRYPTO_LIBRARY mbedcrypto)
The subproject expects mbedtls to already be installed somewhere on the system, but it didn't consider the fact that I want to link statically. My approach is to now FetchContent mbedtls and provide the find_library() calls with the prebuilt static libraries.
Now I need a way provide those find_library-calls with the right search directory, of course without modifying its source code. Can I somehow set a prefix path? I know I could probably set CMAKE_PREFIX_PATH but that seems like an ugly hack and it would probably affect other find_library()-calls within the project which also exist. Is there a more "constrained" way?
Can I somehow set a prefix path?
Setting a prefix path won't help find_library to locate the library, because command find_library searches the file at configuration stage, but the library is built only on build stage.
Instead, you may write the target name to the CACHE variable, which is passed to find_library as the first argument:
When find the result variable to be already set, find_library won't search the library file.
In most cases a project uses result of find_library in the call to target_link_libraries, so having the library target in the result variable will fit to the project's expectations.
Example:
FetchContent_Declare(mbedtls ...)
FetchContent_MakeAvailable(mbedtls)
set(MBEDTLS_LIBRARY MbedTLS::mbedtls CACHE INTERNAL "mbedtls library target")
With such setup the following
find_library(MBEDTLS_LIBRARY mbedtls)
will do nothing, since the variable MBEDTLS_LIBRARY is already set.
And if the project will use this variable like
target_link_libraries(<executable> ${MBEDTLS_LIBRARY})
then it effectively gets
target_link_libraries(<executable> MbedTLS::mbedtls)
Name of the target which should be assigned to the variable could sometime be found from the project's documentation, but otherwise you need to look into the project's sources (CMakeLists.txt).
E.g. in case of mbedtls project, the library target mbedtls is created with add_library() call, and MbedTLS::mbedtls is created as ALIAS for it.

Find out why cmake adds specific link flags

I have big project with cmake. It mostly works.
But recently some combination of compilation server vs test server broke. Investigation found that final compile/link command calls gcc (...) -licudata -licui18n -licuuc (...), this introduces dependency on shared library which is not present on test server.
How do I find out what in my project (my library, imported library, found library, whatever) adds those 3 flags to compile command?
I don't add them explicitly, so something is done automagically and I want to find it. compile_commands.json doesn't have them because linking flags don't belong in it. CMakeCache.txt has those flags in some obscure variable PC_LIBXML_STATIC_LIBRARIES:INTERNAL but removing them there doesn't affect compile/link command.
Note that this question is not about dealing with libicu specifically but about a method for investigation in general (though comments about eventual known problems with libicu would be appreciated too).
I found out that dependency graphs created by cmake can have more details that was configured for our project. Here are all options: https://cmake.org/cmake/help/latest/module/CMakeGraphVizOptions.html I expect GRAPHVIZ_EXTERNAL_LIBS, GRAPHVIZ_SHARED_LIBS are most important to set to true.
We enabled everything that was possible to enable, filtered out nothing and resulting graph was massive (to big for xdot - luckily .dot files are human readable), but showed that Boost::regex uses those 3 libraries.

Is CMake "set(XXXX CACHE ...)" a bug when used in a library?

I'm using CMake in my project, as does one of my project's third-party library (included with add_subdirectory() for convenience). I have been having strange build issues, and I think I've tracked them down to the following line in the third-party library's top-level CMakeLists.txt:
set(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "")
This sets CMAKE_DEBUG_POSTFIX for my entire project (except for any subtrees where it's explicitly set), which breaks my build. Worse, the build behavior is order- and time-dependent, with the value being changed in the middle of my build after a clean build, and not after a rebuild. (Lots of fun to track down.)
The library sets lots of cache variables whose name begins with "XXXX_", where "XXXX" is the name of the library. That's fine by me, as it's unlikely these variables will be used by others' code. But, it seems antisocial to set commonly-used variables globally when your code is meant to be a component of someone else's project. It also is fragile; if I use set(XXXX <aValue>) in my top-level CMakeLists.txt then the library's set(XXXX CACHE...) statements will be ignored.
Instead, the library should just use set(CMAKE_DEBUG_POSTFIX "d"), which sets the variable for all of the library's code tree, and nobody else's.
Is this a bug in the library's build code? Should libraries that aim to be good CMake citizens avoid CACHE variables except for their clearly-named private variables?
For libraries that intend to be usable as a subproject (via add_subdirectory or FetchContent), I would say it is a bug to set such cache variables without a check that the project is top-level. On the other hand, a project that does not intend to be usable this way should explicitly check and issue a fatal error (or maybe an author warning) in this case. So either way, I would argue there's a bug and you should notify the maintainers.
In CMake 3.21+ the variable PROJECT_IS_TOP_LEVEL works. In earlier versions, you can write:
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" PROJECT_IS_TOP_LEVEL)
to get the same variable. Then check:
if (PROJECT_IS_TOP_LEVEL)
# Either this: (AUTHOR_WARNING acceptable, too)
message(FATAL_ERROR "Subproject inclusion not supported")
# or:
set(CMAKE_* ... CACHE ...)
endif ()

Cmake not setting RPATH when adding link_library with -L

When setting link libraries in the following manner
target_link_libraries (SOME_TARGET -L/somedir -lfoo)
cmake doesn't handle RPATHs. Is using '-L' and '-l' not best practice, or actually plain wrong? When creating my own Find*.cmake I usually use find_library() but the find script I got doesn't do this and resorts to the above form using '-L' and '-l'.
The documentation doesn't really explain how RPATHs are gathered, also the documentation isn't really clear how it handles "-l" and "-L" the only pointer you get is
"Item names starting with -, but not -l or -framework, are treated as
linker flags"
Specifying toolchain-dependent flags like -l and -L is generally not recommended, as it breaks portability and might have different effects than you expect.
The correct way to set the linker path would be the link_directories command.
The idiomatic solution in CMake is to use find_library for locating the library and then pass the full path to the linker, so you do not need to worry about link directories at all.
Now, the RPATH is a different beast, as it also determines where dynamic libraries can be located at runtime. Usually, the default settings work reasonably fine here. If you ever find yourself in the unfortunate situation where it does not, there is a number of target properties and CMake variables influencing this:
There are a few properties used to specify RPATH rules. INSTALL_RPATH
is a semicolon-separated list specifying the rpath to use in installed
targets (for platforms that support it). INSTALL_RPATH_USE_LINK_PATH
is a boolean that if set to true will append directories in the linker
search path and outside the project to the INSTALL_RPATH.
SKIP_BUILD_RPATH is a boolean specifying whether to skip automatic
generation of an rpath allowing the target to run from the build tree.
BUILD_WITH_INSTALL_RPATH is a boolean specifying whether to link the
target in the build tree with the INSTALL_RPATH. This takes precedence
over SKIP_BUILD_RPATH and avoids the need for relinking before
installation. INSTALL_NAME_DIR is a string specifying the directory
portion of the “install_name” field of shared libraries on Mac OSX to
use in the installed targets. When the target is created the values of
the variables CMAKE_INSTALL_RPATH, CMAKE_INSTALL_RPATH_USE_LINK_PATH,
CMAKE_SKIP_BUILD_RPATH, CMAKE_BUILD_WITH_INSTALL_RPATH, and
CMAKE_INSTALL_NAME_DIR are used to initialize these properties.
(From the set_target_properties docs)
Also, you might want to have a look at the CMake Wiki page for RPATH handling.
The whole RPATH business is unfortunately rather complex and a thorough explanation would require far more space than is appropriate for a StackOverflow answer, but I hope this is enough to get you started.
Basically, You're using target_link_libraries() wrong. According to documentation, You should provide target, libraries and maybe some CMake specific linkage flags.
For example something like that:
target_link_libraries(my_build_target somedir/foo.so)
If You're using Your own crafted Find*.cmake solutions, it's usualy being done like this:
find_library(foo)
//build main target somewhere here
//now link it:
target_link_libraries(my_build_target ${FOO_LIBRARIES})
NOTE: I assume Your crafted Find*.cmake files follows these guidelines and fills CMake variables like SOMELIB_LIBRARIES, and/or SOMELIB_INCLUDE_DIRS, etc.
NOTE2: for my personal opinion, target_link_directories() is pain in a butt and You should avoid using it if not really needed. It's difficult to maintain and uses paths relative to current source directory.

Xcode search paths for public headers in dependencies

I am trying to clean up some of my projects, and one of the things that are puzzling me is how to deal with header files in static libraries that I have added as "project dependencies" (by adding the project file itself). The basic structure is like this:
MyProject.xcodeproj
Contrib
thirdPartyLibrary.xcodeproj
Classes
MyClass1.h
MyClass1.m
...
Now, the dependencies are all set up and built correctly, but how can I specify the public headers for "thirdPartyLibrary.xcodeproj" so that they are on the search path when building MyProject.xcodeproj. Right now, I have hard-coded the include directory in the thirdPartyLibrary.xcodeproj, but obviously this is clumsy and non-portable. I assume that, since the headers are public and already built to some temporary location in ~/Library (where the .a file goes as well), there is a neat way to reference this directory. Only.. how? An hour of Googling turned up blank, so any help is greatly appreciated!
If I understand correctly, I believe you want to add a path containing $(BUILT_PRODUCTS_DIR) to the HEADER_SEARCH_PATHS in your projects build settings.
As an example, I took an existing iOS project which contains a static library, which is included just in the way you describe, and set the libraries header files to public. I also noted that the PUBLIC_HEADERS_FOLDER_PATH for this project was set to "/usr/local/include" and these files are copied to $(BUILT_PRODUCTS_DIR)/usr/local/include when the parent project builds the dependent project. So, the solution was to add $(BUILT_PRODUCTS_DIR)/usr/local/include to HEADER_SEARCH_PATHS in my project's build settings.
HEADER_SEARCH_PATHS = $(BUILT_PRODUCTS_DIR)/usr/local/include
Your situation may be slightly different but the exact path your looking for can probably be found in Xcode's build settings. Also you may find it helpful to add a Run Script build phase to your target and note the values of various settings at build time with something like:
echo "BUILT_PRODUCTS_DIR " $BUILT_PRODUCTS_DIR
echo "HEADER_SEARCH_PATHS " $HEADER_SEARCH_PATHS
echo "PUBLIC_HEADERS_FOLDER_PATH " $PUBLIC_HEADERS_FOLDER_PATH
.
.
.
etc.
I think that your solution is sufficient and a generally accepted one. One alternative would be to have all header files located under an umbrella directory that can describe the interface to using the depended-on libraries and put that in your include path. I see this as being similar to /usr/include. Another alternative that I have never personally tried, but I think would work would be to create references to all the headers of thirdPartyLibrary from MyProject so that they appear to be members of the MyProject. You would do this by dragging them from some location into MyProject, and then deselecting the checkbox that says to copy them into the project's top level directory. From one perspective this seems feasible to me because it is as if you are explicitly declaring that your project depends on those specific classes, but it is not directly responsible for compiling them.
One of the things to be wary of when addressing this issue is depending on implementation-specific details of Xcode for locating libraries automatically. Doing so may seem innocuous in the meantime but the workflows that it uses to build projects are subject to change with updates and could potentially break your project in subtle and confusing ways. If they are not well-defined in some documentation, I would take any effect as being coincidental and not worth leveraging in your project when you can enforce the desired behavior by some other means. In the end, you may have to define a convention that you follow or find one that you adopt from someone else. By doing so, you can rest assured that if your solution is documented and reproducible, any developer (including yourself in the future) can pick it up and proceed without tripping over it, and that it will stand the testament of time.
The way we do it is to go into build target settings for the main project and add:
User Header Search Path = "Contrib"
and check that it searches recursively. We don't see performance problems with searching recursively even with many (10-15 in some projects) dependencies.