Packaging of linker scripts: Should .so belong to -devel packages instead of following .so.1 files - packaging

Separating packages into main and -devel parts looks like a good established practice, which allows to save some space for those who are not building software. On Linux, a library that uses SONAME versioning can have two or more files representing a single library, e.g.:
libABC.so - linker script, e.g. INPUT (libABC.so.1)
libABC.so.1 - real binary
Where .so file is really used at the build time only, redirecting actual dependency to the versioned file. If so, shouldn't it be placed into e.g. libABC-devel.rpm along with include files? It looks inappropriate to put both files to a run-time package, e.g. libABC.rpm.
E.g. one observation, if you inspect ls /usr/lib/lib*.so*, many of versioned files do not have corresponding lib*.so pair.
I also noticed that e.g. Fedora and Conda-forge maintainers replace these linker scripts with symlinks (1, 2) and package them along with the main binary. Is there any reason to do that despite that original library builds linker scripts instead?
I'll accept an answer which summarizes arguments for one or another way of packaging linker scripts into runtime/devel for sake of discussion with package maintainers.

Related

CMake: Error including library from another package [duplicate]

I am writing a C++ library (header-only) and am using CMake to generate my (Visual Studio) project and solution files. I'm also writing a test suite, which is part of the same CMake project.
My problem occurs when I call target_include_directories() on the target that represents my header-only library, so that consumers of my library may find its header files. I get the following error message (even though generation is NOT aborted).
CMake Error in CMakeLists.txt:
Target "Fonts" INTERFACE_INCLUDE_DIRECTORIES property contains path:
"D:/Projects/GPC/fonts/include"
which is prefixed in the source directory.
(D:/Projects/GPC/Fonts being the top-level directory of my library project. Btw the problem remains if I move my header files to the top directory.)
The offending line in my CMakeLists.txt is this (adapted for simplicity):
target_include_directories(Fonts INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")
I do not understand what I'm doing wrong. Without target_include_directories(), code of consumer projects simply can't include my header files (unless in installed form, but I haven't gotten to that yet, and in any case I want to be able to use my library from its build tree, without installation.)
I feel like I'm missing something basic here; yet I've searched for hours without finding a solution or explanation.
The origin of the problem is not the target_include_directories command itself, but the attempt to install a target that has a public or interface include directory prefixed in the source path (i.e. the include directory is a subdirectory of your ${PROJECT_SOURCE_DIR}.)
While it is perfectly fine and desirable to use absolute paths when building the library from scratch, a third party library that pulls in a prebuilt version of that library will probably want to use a different include path. After all, you do not want all of your users to mirror the directory structure of your build machine, just to end up in the right include path.
CMake's packaging mechanism provides support for both of these use cases: You may pull in a library directly from the build tree (that is, check out the source, build it, and point find_package() to the directory), or from an install directory (run make INSTALL to copy built stuff to the install directory and point find_package() to that directory). The latter approach needs to be relocatable (that is, I build and install on my machine, send you the resulting directory and you will be able to use it on your machine from a different directory structure), while the former is not.
This is a very neat feature, but you have to account for it when setting up the include directories. Quoting the manual for target_include_directories:
Include directories usage requirements commonly differ between the
build-tree and the install-tree. The BUILD_INTERFACE and
INSTALL_INTERFACE generator expressions can be used to describe
separate usage requirements based on the usage location. Relative
paths are allowed within the INSTALL_INTERFACE expression and are
interpreted relative to the installation prefix. For example:
target_include_directories(mylib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/mylib>
$<INSTALL_INTERFACE:include/mylib> # <prefix>/include/mylib
)
The BUILD_INTERFACE and INSTALL_INTERFACE generator expressions do all the magic:
$<INSTALL_INTERFACE:...>
Content of ... when the property is exported using install(EXPORT), and empty otherwise.
$<BUILD_INTERFACE:...>
Content of ... when the property is exported using export(), or when the target is used by another target in the same buildsystem.
Expands to the empty string otherwise.

What is cpack --config CPackSourceConfig.cmake actually doing?

CMake Tutorial
In Step 6 of the CMake tutorial, it says "To create a source distribution you would type" cpack --config CPackSourceConfig.cmake. I tried this command and get four tarballs: output.tar.bz, output.tar.gz, output.tar.xzand output.tar.Z.
The tarball is just a compression of the current directory. The Makefile packed contains absolute path of my current system. It means I need to run cmake again if I distribute it to other system.
So what is the point of using cpack?
One of the point of using CPack for create source package is to not including version-control files into resulted tarball (see CPACK_SOURCE_IGNORE_FILES variable).
Also it is possible to futher modify list of files, intended for source package. E.g., one can exclude internal documentation (which is useful for developers only) but include some pre-generated files, so user doesn't require to have additional tools, like Flex/Bison, to build source package.
Though CPack (and especially its source package functionality) is poorly documented (see, e.g., this mailing and this bugreport), source package generation is relatively flexible.

What is the CMake install time?

A quote from the official documentation:
"Specify rules to run at install time."
What exactly is install time?
The problem for me: I am on Linux, software is installed from packages that are just dependencies and data. There is no CMake that can do anything here. So installation time of software is out of scope from CMake. So what exactly do they mean?
Building a CMake project can roughly be divided into three phases:
Configure time. This includes everything that happens while running cmake itself. This phase is concerned with inspecting certain properties of the host system and generating the specific build files for that platform under the selected configuration.
Build time. This includes everything that happens while actually building your project from the files generated by CMake (like, when running cmake --build or make). This is where all of the actual compilation and linking happens, so at the end of the build phase, you have a usable binary.
Install time. This includes everything that happens when running the INSTALL target generated by CMake (like, when running cmake --build --target install or make install). This takes care of copying the binaries that were generated into the build tree to a different directory. Note that the build tree contains a lot of stuff that is no longer needed once the build is completed if you are only interested in running the binary. Examples include all intermediate build artifacts, like the build files generated during the configure phase or the intermediate object files created during the build phase. Furthermore, the install phase might include additional steps to ensure that the binaries produced during the build are portable. For instance, on Linux systems you might want to remove the build directory from the shared library search path in the binary and replace it with a portable equivalent. So the install phase might do more than just copy all the important files to a new directory. It could also include additional steps that change the binaries to make them more portable.
Note that the last phase is optional. If you do not want to support calling make install but prefer another deployment mechanism, you simply don't use the install command in your CMake script and no INSTALL target will be generated.
I'd like to expand the answer, which ComicSansMS gave you, a little bit.
As he mentioned - CMake generates an extra target called install for the make tool (when you use a Makefile-based generator).
It may look weird for you as a package system is used for Linux. However the install target is still useful or even necessary:
When you develop your application you may need to install (move binaries and possibly some include files) to a certain location so some of your projects may see each other. For example, you may develop a library and a set of non-related applications which use it. Then this library must be installed somewhere to be visible. It doesn't mean you need to put it to the /usr directory; you may use your /home.
The process of Linux package preparation requires an install step. For example, the RPM packaging system does three main steps when the rpm package file is being built: the project is configured, then is compiled and linked and finally is being installed to a certain location. All files from this location are being packed to the rpm file.

Package & library management & installation, and interface with cmake

I have a specific question which serves as context for a more general question.
There is a scientific package called LAMMPS, and it is usually used as an executable. However, it supports use as a "library". To try to do things right, I put it in /usr/local/lib/lammps. It contains a lammps/src/ directory, which has around 40 source files. Using the instructions provided, I compiled lammps as a .so file in lammps/src/liblammps_serial.so.
I also have separate code in "~/code/ljtube/". This uses cmake to try to find the library. Thus, I wrote a FindLAMMPS.txt so that I could use
FIND_PACKAGE (lammps)
in my CMakeLists. I modified the libtool config file to search in /usr/local/ successfully. I found that it searches in /usr/local/lib/ for a .so file and in /usr/local/include/ for a .h file. So I made a dynamic link to the .so file in /usr/local/lib/, and I copied the .h file from the lammps/src/ to /usr/local/include/.
CMake can now find those two files, but it cannot link to anything else in lammps/src/. It seems absurd to need to make a separate FIND_PACKAGE for each of the .h's I want to include (group.h, fix.h, force.h, pair.h, etc.). It also seems ridiculous to dump the whole package of .h files into the /usr/local/include/ directory. I will be using this code both locally and on a cluster, and possibly distributing it to other group members.
How can I make CMake find what I want to find without hard coding in the location of /usr/local/lib/lammps/src/? Phrased more generically, how should I manage large packages like these to make them easy to link to in the code I write, even if the original developer did not use the best conventions?
(As a side note, I am using a shared library because it seems like the right choice, but I'm not especially married to it. Should I be using a static library? Is there a way for CMake to find an already-compiled library relative to the current source directory, and might that be a better way to implement this? I know that I will be using LAMMPS in multiple projects, so having a local shared copy superficially seems to make the most sense.)
Normally a find_package call yields a variable specifying the path to the "includes" folder of the package. This would then be added in the caller's CMakeLists.txt via include_directories.
For example, to use find_package for boost, you could do:
find_package(Boost) # sets ${Boost_INCLUDE_DIRS} and ${Boost_LIBRARIES}
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})
add_executable(foo foo.cc)
target_link_libraries(foo ${Boost_LIBRARIES})
endif()
Regarding your side note, you could use find_library and/or find_path to find the library and its headers given a known location.
Both these commands can be invoked in such a way as to avoid searching in common locations, e.g. by setting PATHS to the known location and using NO_DEFAULT_PATH in the find commands.
Another alternative is for your projects to make use of the ExternalProject_Add function which is described in more detail in this article. From this article:
The ExternalProject_Add function makes it possible to say “download this project from the internet, run its configure step, build it and install it”
A downside to this approach is that each of your projects would end up with its own copy of the third party sources and lib.

Is there any interactive shell for module development in cmake?

CMake is awesome, especially with lots of modules (FindOOXX). However, when it comes to write a FindXXX module, a library XXX which your project depends, it's not that easy to handle for non-cmake-expert. I sometimes encounter a library without support to CMake, and I like to make one for it. I'm wondering if there is any interactive shell while writing/testing cmake modules?
Are you writing FindXXX for project "XXX" or is "XXX" a dependency of your project that you're trying to find? If the former, you should instead write a file called XXX-config.cmake (or XXXConfig.cmake) and install it into one of the directories mentioned in the docs for find_package. In general, XXX-config.cmake files are for projects which are expected to be found by CMake (and installed by the project itself) and FindXXX.cmake files are for projects which don't support CMake (and usually have to support finding any version of XXX).
That said, for FindXXX.cmake, usually you just need a few find_file (e.g., for headers), some find_library calls, or even just a single pkg_check_module from FindPkgConfig.cmake followed by a find_package_handle_standard_args call (use include(FindPackageHandleStandardArgs) to get it). FPHSA makes writing proper Find modules a breeze.
For XXX-config.cmake files, I have typically used configure_file to generate two versions: one for the install (which includes your install(EXPORT) file) and one for the build tree (generated by export() calls). Using this, other useful variables can be accurately set such as things like "which exact version of Boost was used" or "was Python support compiled in" so that dependent projects can get a better picture of what the dependency looks like.
I have also recently discovered that CMake ships with the CMakePackageConfigHelpers module which is supposed to help with making these files. There looks to be quite a bit of documentation for it.