Optional in-tree builds for dependencies - cmake

I have an existing codebase where a number of third-party dependencies were added as Git submodules, and their directories are directly referenced inside CMakeLists.txt, as in include_directories(../external/foo).
Some of the dependencies are large projects, like FFmpeg, and I'd rather just use the version I installed to my system with a package manager. But the maintainer of the codebase wants to be able to continue using the in-tree dependencies.
I thought a compromise would be to configure CMake to permit both, either using an installed package, or using the in-tree submodules. I think find_package can be used to find the installed package, but is there a good way to implement this behavior that isn't too hacky?

You can add an option to your cmake file that allows the user to switch between the internal FFMpeg or the system one. option(INTREE_FFMPEG "Use the intree ffmpeg" ON). The option can be set either via the cmake-gui or as a command line switch.

Related

How to handle package dependencies for some build targets when building with Copr?

I want to have a rpm package build with Copr1. My current build target list is Fedora 35, 36, rawhide and Centos 7 and Stream 8. I have not yet created the copr project.
Compiling on one of my machines, the package builds successfully on the Fedora variants with mock. The problem is that on Centos variants one of the build dependencies and some of its dependencies are not available. I have found appropriate srpm files and compiled them on one of my machines with the Centos Stream 8 (one of them required two custom patches). With those custom dependencies I am able to successfully compile the original package.
So just to be clear, the problem is that the spec file contains for example
BuildRequires: libsomething
where libsomething is available as a plain upstream package in some of the build targets while needs an additional custom repo for some other build targets.
The FAQ says the following about dependencies:
Can I depend on other packages, which are not in Fedora/EPEL?
Yes, they just need to be available in some yum repository. It can either be another Copr repo or a third-party yum repo (e.g jpackage). Click on “Edit” in your project and add the appropriate repositories into the “Repos” field. Packages from your project are available to be used at build time as well, but only for the project you are currently building and not from your other projects.
But this sounds like an all or nothing approach, and I absolutely do not want to override the already existing upstream packages, only provide them when they are missing.
So what strategy do people use to handle this?
Update: I have now created copr projects and made some attempts at building (after resolving dependencies of dependencies in several levels), but the problem is as I describe above. If I add copr://hlovdal/projectname as a build dependency then epel-8-x86_64 compiles fine because it is provided with the missing dependencies while fedora-35-x86_64 fails because the repository does not have any fedora packages. If I remove the repo epel fails while fedora succeeds.
I also attempted to add the base url from the corresponding /etc/yum.d.repo file, and only hardcode epel instead of $distname hoping that the fedora builds would just ignore non-existing/wrong repo setting, but the build does not like that and still fails.
1 Copr is Fedora's freely available build system.

cmake: package config for installing arbitrary file dependencies for a target

I am creating a cmake package config file (a Foo-config.cmake) for a pre-existing .dll not created by cmake.
The annoying thing is that the .dll depends on some data files.
When a user consumes my package in his own cmake project, I want the INSTALL target to install both the .dll and data files to his specified install location. I don't want him to have to write extra install() rules to do that.
Is it good practice to write the install() rules directly in my Foo-config.cmake? Or is there a better way to do this, maybe with set_target_properties()? I just couldn't find the appropriate property for associating arbitrary file dependencies to a target.
In an alternate universe where this .dll didn't already exist and I had to create it myself using cmake, would I need to create a custom Foo-config.cmake, or is there something in cmake that can automatically generate it for me to achieve the same thing?
FWIW the .dll is an internal legacy library and is normally built by Visual Studio and uploaded in a .zip file to our internal artifactory. I want us to migrate away from manually pulling down .zip files from artifactory and manually integrating the files into Visual Studio projects.
I've since found that there are a couple different ways to do this:
In the config file, simply create one or more variables for the files/dirs you want to install. Then install those using install(FILES) and/or install(DIRECTORY). More info: https://stackoverflow.com/a/46361538/189341
Use file(GET_RUNTIME_DEPENDENCIES). More info:
https://discourse.cmake.org/t/installing-a-pre-built-module-and-its-various-dependencies/5227
How to use cmake file( GET_RUNTIME_DEPENDENCIES in an install statement?
Is it good practice to write the install() rules directly in my Foo-config.cmake?
No.
From 480 *-config.cmake and *Config.cmake files on my system none calls install().
Or is there a better way to do this, maybe with set_target_properties()?
No.
In an alternate universe where this .dll didn't already exist and I had to create it myself using cmake, would I need to create a custom Foo-config.cmake
No. This is unrelated to if you create a .dll or not. If .dll exists, there is no need to create Foo-config.cmake anyway. It is your choice that you want to (or make users to) use find_package.
is there something in cmake that can automatically generate it for me
No.
If you don't intent to support find_package features - VERSION OPTIONAL_COMPONENTS PATHS HINTS CONFIGS etc. - then just go with include(). find_package is just include() with some extra options.
If you want to have install() in your find_package, then just protect it with a variable, like if (FOO_DO_INSTALL) install(....) endif().

Where to install FindLib.cmake

I'm creating a library (called fmi2) which I intend to install on my local machine (via a package manager) and then link to with a bunch of other libraries.
I'll be providing a Findfmi2.cmake file so that I can call find_package(fmi2) from my other libraries, but where should this file be conventionally installed?
Here are some choices I've considered and their problems:
/usr/share/cmake-3.8/Modules/Findfmi2.cmake
Advantage: find_package(fmi2) will just work
Disadvantage: Only works for one version of cmake
/usr/share/cmake/Modules/Findfmi2.cmake
Advantage: Should work for any version of cmake
Disadvantage: This is not a default folder. We would need to add set(CMAKE_MODULES_PATH /usr/share/cmake/Modules) and this kills any portability.
${CMAKE_CURRENT_SOURCE_DIR}/cmake/Findfmi2.cmake
Advantage: Portable, just need to add set(CMAKE_MODULES_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
Disadvantage: Not system-wide. Need to manually add this file to each library that uses it. This duplicates files in my framework.
You are authoring content in CMake. You don't need a FindModule. That is designed to find external non-CMake outputs.
This Stackoverflow post from ruslo should help you understand the difference between find_package() module mode and config mode. It also answers your question about paths for FindModules, i.e. they are copied into your CMake project, not discovered system-wide, unless they are part of the official FindModules bundled with CMake in the "Modules" directory.
Modern CMake documentation now finally contains good examples to create a config mode package: cmake-packages
If you want explicit full examples, though using slightly older syntax for the config.cmake files, ruslo has more on Github.

FindGLM.cmake not in glm 0.9.7, is it a deprecated way to find libraries in CMAKE?

So looking through the newest release of GLM 0.9.7, I dont see a FindGLM.cmake file anywhere, used to easily include GLM in CMAKE. I could always use an old version of it found online but the following commit had me stumped:
https://github.com/g-truc/glm/commit/62a7daddcf082f754000fc5e42d7bcdf93c895f7
Commit message is "Removed obsolete FindGLM". So, did the developer just dump it or are there in fact a new way to find libraries in CMAKE?
Yes, CMake Find modules (FindXyz.cmake files) are deprecated in favour of Package Config files (usually named XyzConfig.cmake). The original philosophy is that Find modules are shipped and maintained by CMake, while Package Config files are shipped and maintained by the package they are intended to find.
CMake's find_package command actually has two modes: Module mode (legacy, using Find modules) and Config mode (preferred, using Package Config files).
For the client consuming the package, little should change (unless more customisation is desired, which is offered by the Config mode of find_package).
Notice that the very commit to which you linked not only drops FindGLM.cmake, but also adds a glmConfig.cmake file.

A layout for maven project with a patched dependency

Suppose, I have an opensource project that depends on some library, that must be patched in order to fix some issues. How do I do that? My ideas are:
Have that library sources set up as a module, keep them in my vcs. Pros: simple. Cons: some third party sources in my repo, might slow down build process, hard to find a patched place (though can be fixed in README)
Have a module, like in 1, but keep patched source files only, compile them with orignal library jar in classpath and somehow replace *.class files in library jar on build. Pros: builds faster, easy to find patched places. Cons: hard to configure, that jar hackery is non-obvious (library jar in repository and in my project assembly would be different)
Keep patched *.class files in main/resources, and replace on packaging like in 2). Pros: almost none. Cons: binaries in vcs, hard to recompile a patched class as patch compilation is not automated.
One nice solution is to create a distinct project with patched library sources, and deploy it on local/enterprise repository with -patched qualifier. But that would not fit for an opensourced project that is meant to be easily buildable by anyone who checks out its sources. Or should I just say "and also, before you build my project, please check out that stuff and run mvn install".
One nice solution is to create a distinct project with patched library sources, and deploy it on local/enterprise repository with -patched qualifier. But that would not fit for an opensourced project that is meant to be easily buildable by anyone who checks out its sources. Or should I just say "and also, before you build my project, please check out that stuff and run mvn install".
This is what I would do (and actually what I do) for both a corporate and an opensource project. Get the sources, put them under version control in a distinct project, patch them, rebuild the patched library (and include this information in the version, something like X.Y.Z-patched), deploy it to a repository (you could use SVN for this, a la Google Code1), declare the repository in your POM and update the dependency to point on your patched version.
With this approach, you can say to your users: check out my code and run mvn install and they will just get the patched version without any extra action. This is IMHO the cleanest way (not error prone, no class path order mess, no increase of the build time, etc).
1 Lots of people are deploying their code to their hosted subversion repository (how-to in this post).
One nice solution is to create a distinct project with patched library sources, and deploy it on local/enterprise repository with -patched qualifier. But that would not fit for an opensourced project that is meant to be easily buildable by anyone who checks out its sources. Or should I just say "and also, before you build my project, please check out that stuff and run mvn install".
I'd agree with this and Pascal's answer. Some additional notes:
you may use dependency:unpack on the original artifact and then combine that with your compiled classes if you don't want to rebuild the whole dependant project
in either case, your pom.xml will need to correctly represent the dependencies of that library
you can still integrate this as part of your project's build to avoid the 'deploy to a repository' step
make sure you honour the constraints of the project's license when doing all this!