Take the following example as a start:
...
add_library(Foo ...)
install(TARGETS Foo EXPORT FooTargets
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
...
)
install(EXPORT FooTargets
FILE lib/cmake/Foo
...
)
Running this with
$ mkdir build; cd build
$ cmake -DCMAKE_BUILD_TYPE=Release .. (or in Debug)
$ cmake --build .
$ cmake --install . --prefix my/custom/prefix
This will create the files:
my/custom/prefix/lib/cmake/Foo/FooTargets.cmake
my/custom/prefix/lib/cmake/Foo/FooTargets-release.cmake (Or debug, respectively)
my/custom/prefix/lib/libFoo.a
And from what I managed to understand from the FooTargets.cmake file, it globs for all FooTargets-*.cmake files and includes() them all.
In turn, the FooTargets-release.cmake file is the one that references the libFoo.a file.
In the docs about the install command, it says that you can add the CONFIGURATIONS option to the install TARGETS command, so that if we change the above:
install(TARGETS Foo EXPORT FooTargets
CONFIGURATIONS Debug
LIBRARY DESTINATION lib/Debug
ARCHIVE DESTINATION lib/Debug
This will install the libFoo.a file in my/custom/path/lib/Debug/libFoo.a. Now let's say I want the Release library to be installed in lib/Release and the Debug library be installed in lib/Debug, and that when the downstream project will consume my package, it will have the right library depending on its configuration - i.e. - debug build will link against the Debug library, same for release.
What I thought I can do is:
install(TARGETS Foo EXPORT FooTargets
CONFIGURATIONS Debug
LIBRARY DESTINATION lib/Debug
ARCHIVE DESTINATION lib/Debug
)
install(TARGETS Foo EXPORT FooTargets
CONFIGURATIONS Release
LIBRARY DESTINATION lib/Release
ARCHIVE DESTINATION lib/Release
)
install(EXPORT FooTargets
DESTINATION lib/cmake/Foo
...
)
And what will happen is that when building in Debug, the FooTargets-debug.cmake will be generated, pointing to the lib/Debug/libFoo.a, and when building in Release, the FooTargets-release.cmake will be generated, pointing to the lib/Release/libFoo.a. The FooTargets.cmake will then check what configuration is the consuming project is building with, and include the right configuration.
When I try doing the above, I get:
-- Configuring done
CMake Error: install(EXPORT "FooTargets" ...) includes target "Foo" more than once in the export set.
-- Generating done
CMake Generate step failed. Build files cannot be regenerated correctly.
How is this should be done??
EDIT
I found out, in a not-very-straightforward way, that when I build the consuming project as such:
$ cmake -DCMAKE_BUILD_TYPE=Debug ..
and like this:
$ cmake -DCMAKE_BUILD_TYPE=Release ..
I am linking against only the relevant library. Basically how this works is that the FooTargets.cmake file is included by the FooConfig.cmake file, which is called by find_package. The FooTargets.cmake file is the one that defines the imported target:
add_library(Foo IMPORTED)
This file then calls all the FooTargets-*.cmake, which adds the relevant library to some list called _IMPORT_CHECK_FILES_FOR_FOO.
What this file also does, is:
set_property(TARGET Foo::Foo APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
Apparently there is some property IMPORTED_CONFIGURATIONS that holds the imported configurations.
I suppose that somewhere down the road, find_package takes this list and filters it according to the CMAKE_BUILD_TYPE variable, and this way links only the relevant library.
What I still don't understand, is how to make it so that upon Release builds (of Foo), the FooTargets-release.cmake will be created, pointing to lib/Release/Foo.a, and the same for debug builds.
It seems that CMake went half-way with this, unless I'm seriously missing something.
It seems per-CONFIGURATIONS installs are not easily fit to EXPORT semantic.
However, in simple cases per-configuration's specific can be achieved by using generator expressions in DESTINATION:
install(TARGETS Foo EXPORT FooTargets
LIBRARY DESTINATION lib/$<CONFIG>
ARCHIVE DESTINATION lib/$<CONFIG>
)
install(EXPORT FooTargets
DESTINATION lib/cmake/Foo
...
)
The code above will install libraries into lib/Debug for Debug configuration, and into lib/Release for Release configuration.
Related
When I use set(CMAKE_DEBUG_POSTFIX "d"), the build and install targets work as expected. But in the libfooTargets-debug.cmake file with the exported targets, there is a path to libfoo and not libfood.
I exported the targets like this:
install(TARGETS libfoo EXPORT libfoo-targets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin)
install(EXPORT libfoo-targets FILE libfooTargets.cmake DESTINATION ${CMAKE_INSTALL_PREFIX})
which creates and installs libfooTargets.cmake and libfooTargets-debug.cmake when building in debug mode, and libfooTargets.cmake and libfooTargets-release.cmake when building in release mode.
Both libfooTargets-release.cmake and libfooTargets-debug.cmake reference the name without a postfix as:
list(APPEND _IMPORT_CHECK_FILES_FOR_libfoo "${_IMPORT_PREFIX}/lib/libfoo.lib" )
and thus a program linking against the debug target still uses the release-build library and I would need to install release and debug versions into different folders to be able to link against the debug target.
How can I get the exported targets to work with a debug postfix?
I could of course try to change the library name depending on CMAKE_RELEASE_TYPE or a CONFIGURATION generator expression, but this will probably break the multi-configuration features in MSVC and other IDEs supporting different targets and seems not to work in the sense of how the exported targets feature is meant to simplify and unify the build.
I suspect that the install(EXPORT ...) command somehow drops the CMAKE_DEBUG_POSTFIX or does not implement it for generating the libfooTargets-{release,debug}.cmake files, but possibly I overlooked how to make this variable visible to the generator of the exported targets or something like this.
All target code
cmake_minimum_required(VERSION 3.11.1)
project(foo)
include(CMakePackageConfigHelpers)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(CMAKE_DEBUG_POSTFIX "d")
# ...
add_library(libfoo STATIC somesource.cpp someheader.h)
target_include_directories(libfoo PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)
target_link_libraries(libfoo
somelibrary
)
target_include_directories (libfoo PUBLIC
somelibrary_header_dirs
)
install(TARGETS libfoo EXPORT libfoo-targets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin)
install(EXPORT libfoo-targets FILE libfooTargets.cmake DESTINATION ${CMAKE_INSTALL_PREFIX})
configure_package_config_file(libfooConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/libfooConfig.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_PREFIX})
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libfooConfig.cmake DESTINATION ${CMAKE_INSTALL_PREFIX})
install(DIRECTORY include/ DESTINATION include FILES_MATCHING PATTERN "*.h")
The platform is a Windows 10 with cmake 3.11.1 and MSVC 2015. Of course the most general solution is probably the best one.
According to the documentation of the install command, you need to reference the configuration that you are interested in:
[...] If a CONFIGURATIONS option is given then the file will only be installed when one of the named configurations is installed. Additionally, the generated import file will reference only the matching target configurations. [...]
So, you need to add the CONFIGURATIONS option in both install commands and duplicate the commands for each configuration you want to install and export.
I use find_package() to find some library. How do I know which variables it defines, so I could use them for linking with that library?
For example, the following CMakeLists.txt successfully is run by CMake, but then make fails at the linking stage, because ${DOCOPT_LIBRARIES} was empty.
cmake_minimum_required(VERSION 3.1)
project(test)
set(PROJECT_LIBS "")
find_package(docopt REQUIRED)
include_directories(${DOCOPT_INCLUDE_DIRS})
set(PROJECT_LIBS ${PROJECT_LIBS} ${DOCOPT_LIBRARIES})
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} ${PROJECT_LIBS})
But I have no idea what else to try?
The usage of results of find_package(XXX) depends on the script which is actually used by this command:
FindXXX.cmake.
This file is either shipped with the project which wants to use it, or with CMake. Usually, such "Find" scripts start with some description about their usage.
XXXConfig.cmake or xxx-config.cmake.
"Config" scripts are usually installed with the package. Normally, they are generated by CMake using specific CMake commands. You need to consult the package's documentation for use such scripts properly.
If the package doesn't document the usage of its "Config" script, then you may only guess about its usage.
Reading a "Config" script itself could be difficult: CMake includes into it a lot of "sugar", which helps portability and compatibility.
Normally, a "Config" script provides some IMPORTED library target(s), which can be used with target_link_libraries call.
One may scan the project's CMakeLists.txt for a line like
install(TARGETS <targets...> EXPORT ...)
Here <targets...> denotes targets, which can be accessed after find_package() call.
Also scan for a line like
install(EXPORT ...)
if it has NAMESPACE option, then its value prepends all exported targets.
Example:
For docopt package, its CMakeLists.txt contains a line
install(TARGETS docopt EXPORT ${export_name} DESTINATION ${CMAKE_INSTALL_LIBDIR})
thus its docopt target is exported.
As installation of the export file
install(EXPORT ${export_name} DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/docopt")
doesn't contain NAMESPACE option, the export target is accessible with its original name:
find_package(docopt REQUIRED)
...
add_executable(${PROJECT_NAME} main.cpp)
# Use *IMPORTED* 'docopt' target
target_link_libraries(${PROJECT_NAME} docopt)
You have a CMake-enabled library project. You need to use it in another library or executable. How to use CMake to find and link to the library? You may have the following preferences:
write the least possible amount of boiler-plate code
decouple the internal details of the linked library from the consuming target
Ideally, the usage of the library should look like this:
add_executable(myexe ...)
target_link_libraries(myexe mylib::mylib)
Let me demonstrate a possible solution on a concrete example:
The myapp project
We have an executable target myapp. We're linking it with mylib, which is built in its own build tree. In the CMakeLists.txt of myapp we find and specify mylib as a dependency of myexe:
find_package(mylib REQUIRED)
...
add_executable(myexe ...)
target_link_libraries(myexe mylib::mylib)
Let's see how to set up mylib and the build of myexe to make this work.
The mylib project
The directory layout of mylib:
mylib
- CMakeLists.txt
- mylib.c
+ include
- mylib.h # single public header
In the CMakeLists.txt of mylib we need to create the target and specify its source files:
add_library(mylib mylib.c include/mylib.h)
The public header mylib.h will be included as #include "mylib.h" both by mylib and the clients of mylib:
mylib itself and other targets built in mylib's CMake project (for example tests) need to find include/mylib.h from the mylib source tree
clients of mylib built in their own projects (like myexe) need to find include/mylib.h at its installed location
CMake allows us to specify both include paths for mylib:
target_include_directories(mylib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
We're using the PUBLIC option here since this header is needed on the public interface of mylib. Use PRIVATE for include paths internal to mylib.
The INSTALL_INTERFACE specifies a path relative to the install root, that is, CMAKE_INSTALL_PREFIX. To actually install the public header:
set_target_properties(mylib PROPERTIES PUBLIC_HEADER include/mylib.h")
We also need to install the library itself and the so-called config-module and related files. The config-module is the file which will be used by consuming projects, like myapp to find mylib and get all the parameters needed to link with it. It is similar to the pkg-config's .pc files.
We need two, related install commands. The first one:
install(TARGETS mylib
EXPORT mylib-targets
PUBLIC_HEADER DESTINATION include
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin)
The list of destinations needed to cover all the standard install locations of static libraries, dll's and so's. If you're sure your library will be built exclusively as a static lib, a single DESTINATION lib would make it.
The interesting part is the EXPORT option. It assigns the list of targets (currently, it's only mylib) to the identifier mylib-targets. This identifier will be used in the next command to generate and install some special files which make find_package(mylib) work in the consuming projects:
install(EXPORT mylib-targets
NAMESPACE mylib::
FILE mylib-config.cmake
DESTINATION lib/cmake/mylib)
This command generates multiple files:
one file for each build configuration (Debug, Release, etc..) which describes the library file and configuration-dependent parameters
a file which describes the configuration-agnostic parameters and also includes all the config-dependent files. Since this file can also be used as a config-module on its own we simply rename it as mylib-config.cmake
The files will be installed into ${CMAKE_INSTALL_PREFIX}/lib/cmake/mylib which is one of the many standard locations the find_package(mylib) command will search for mylib-config.cmake.
Building mylib
We need to specify an install location in the variable CMAKE_INSTALL_PREFIX:
mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX=$PWD/../out ../mylib
and build and install the library:
cmake --build . --target install
Building myexe
myexe needs to know where to look for mylib. The variable CMAKE_PREFIX_PATH can be a list of paths. We need to specify the previous install location:
mkdir build
cd build
cmake -DCMAKE_PREFIX_PATH=$PWD/../out ../myexe
cmake --build .
A note on building multiple configurations
Usually we need to build multiple configurations (Debug, Release). A critical issue is to specify configuration-dependent filenames or install locations. For example, you can set the default value of the DEBUG_POSTFIX property for the library project:
set(CMAKE_DEBUG_POSTFIX d)
The debug version of the mylib library file will be named libmylibd.lib (or mylibd.lib on Windows). The generated EXPORT files will contain the modified filenames.
If you're using makefile-style CMake generators you can control the build configuration by setting the CMAKE_BUILD_TYPE variable:
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$PWD/../out ../mylib
cmake --build . --target install
You may need seperate build directories for each configuration or you can re-use the same build dir. In that case, to play it safe it's best to explicitly clean before build:
cmake --build . --target install --clean-first
If you're using a multiconfig IDE generator, like Xcode or Visual Studio, you need to specify the configuration in build time:
cmake -DCMAKE_INSTALL_PREFIX=$PWD/../out ../mylib
cmake --build . --target install --config Release
References
You can clone and build this repository which contains the mylib and myexe projects (tested on Windows and Linux).
Check out the CMake documentation. The most important related commands are:
add_library
target_link_libraries
find_package
install
target_include_directories
target_compile_definitions
and two detailed articles:
Build System
Packages
The docs for CMake 3.2.2 state, that it is possible to use generator expressions for the install(<FILES|PROGRAMS> ...) signature. I was trying to use generator expressions in other signatures of install(), but apparently it is not working. I would like to do something like this:
install(TARGETS foo EXPORT fooConfig
RUNTIME DESTINATION "Bin/$<CONFIG>"
LIBRARY DESTINATION "Lib/$<CONFIG>"
ARCHIVE DESTINATION "Lib/$<CONFIG>"
I also tried calling install() multiple times like this:
install(TARGETS foo EXPORT fooConfig
RUNTIME DESTINATION "Bin/Debug" CONFIGURATIONS Debug
LIBRARY DESTINATION "Lib/Debug" CONFIGURATIONS Debug
ARCHIVE DESTINATION "Lib/Debug" CONFIGURATIONS Debug
install(TARGETS foo EXPORT fooConfig
RUNTIME DESTINATION "Bin/Release" CONFIGURATIONS Release
LIBRARY DESTINATION "Lib/Release" CONFIGURATIONS Release
ARCHIVE DESTINATION "Lib/Release" CONFIGURATIONS Release
install(TARGETS foo EXPORT fooConfig
RUNTIME DESTINATION "Bin/MinSizeRel" CONFIGURATIONS MinSizeRel
LIBRARY DESTINATION "Lib/MinSizeRel" CONFIGURATIONS MinSizeRel
ARCHIVE DESTINATION "Lib/MinSizeRel" CONFIGURATIONS MinSizeRel
install(TARGETS foo EXPORT fooConfig
RUNTIME DESTINATION "Bin/RelWithDebInfo" CONFIGURATIONS RelWithDebInfo
LIBRARY DESTINATION "Lib/RelWithDebInfo" CONFIGURATIONS RelWithDebInfo
ARCHIVE DESTINATION "Lib/RelWithDebInfo" CONFIGURATIONS RelWithDebInfo
This causes CMake to emit an error along the lines of Target 'foo' exported more than once in 'fooConfig'.
I cannot use CMAKE_BUILD_TYPE here either without updating the CMake cache and re-running the build. Rather than that, I want to use the Visual Studio batch-build feature, which builds multiple configurations for me.
There was a little hack that I tried too. I noticed that CMake's generated INSTALL project is simply invoking a CMake script ${CMAKE_BINARY_DIR}/cmake_install.cmake with the argument -DBUILD_TYPE=$(Configuration). So I tried the following:
install(TARGETS foo EXPORT fooConfig
RUNTIME DESTINATION "Bin/\${BUILD_TYPE}"
LIBRARY DESTINATION "Lib/\${BUILD_TYPE}"
ARCHIVE DESTINATION "Lib/\${BUILD_TYPE}"
This actually worked well for the installation. However, the installed export script, i.e. the output of install(EXPORT fooConfig DESTINATION .) now also tries to use ${BUILD_TYPE}, which is not set at the time the user includes that script...
If anyone knows another way to accomplish my goals, please let me know.
Unluckily the install command only supports generator expressions for the list of files to be installed, but not for the destination directory.
I would recommend on sticking with your little hack, but use CMAKE_INSTALL_CONFIG_NAME instead of CMAKE_BUILD_TYPE, i.e.:
install(TARGETS foo EXPORT fooConfig
RUNTIME DESTINATION "Bin/\${CMAKE_INSTALL_CONFIG_NAME}"
LIBRARY DESTINATION "Lib/\${CMAKE_INSTALL_CONFIG_NAME}"
ARCHIVE DESTINATION "Lib/\${CMAKE_INSTALL_CONFIG_NAME}"
CMAKE_INSTALL_CONFIG_NAME is set to the actual build configuration used for installing in the generated cmake_install.cmake script.
The generated export scripts (e.g., fooConfig-debug.cmake) can be automatically fixed by adding a patch script to the installation process. Generate a file patch_export_files.cmake with the following contents in the source directory:
file(GLOB_RECURSE _configFiles "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/fooConfig-*.cmake")
foreach(_configFile ${_configFiles})
file (READ "${_configFile}" _contents)
string (REGEX MATCH "configuration \"[A-Za-z]+\"" _configName "${_contents}")
if (_configName MATCHES "\"([A-Za-z]+)\"")
message(STATUS "Patching: ${_configFile}")
string (REPLACE "\${CMAKE_INSTALL_CONFIG_NAME}" "${CMAKE_MATCH_1}" _patchedContents "${_contents}")
file (WRITE "${_configFile}" "${_patchedContents}")
endif()
endforeach()
The patch script need to be run at installation time by adding an install(SCRIPT ... call after the install(EXPORT ...:
install(EXPORT fooConfig DESTINATION .)
...
install(SCRIPT patch_export_files.cmake)
The patch script first parses the configuration that the generated export script is valid for from its header comment and then replaces each use of ${CMAKE_INSTALL_CONFIG_NAME} with the configuration name.
I am working on a project which requires builds with different configurations. The way I have achieved this is using "ExternalProject".
I have created a sample repository to show you the idea:
https://github.com/mpaluru/cmake_multiple_build_configs_example
(Linux is the environment I use mostly and don't have access to Visual Studio.)
If you pass on the -G flag in the top level CMakeLists.txt, you should be able to generate your VS files. I have tested this on Linux and "make -j" works fine. Both the Debug and Release configurations build in parallel.
Summary:
You create a new superbuild project which calls your project using ExternalProject_Add with different CMAKE_BUILD_TYPE.
And based on the build type, you pass different definitions, or do install differently.
I'm trying to get a cross-plattform build system working using CMake. Now the software has a few dependencies. I compiled them myself and installed them on my system.
Some example files which got installed:
-- Installing: /usr/local/share/SomeLib/SomeDir/somefile
-- Installing: /usr/local/share/SomeLib/SomeDir/someotherfile
-- Installing: /usr/local/lib/SomeLib/somesharedlibrary
-- Installing: /usr/local/lib/SomeLib/cmake/FindSomeLib.cmake
-- Installing: /usr/local/lib/SomeLib/cmake/HelperFile.cmake
Now CMake has a find_package() which opens a Find*.cmake file and searches after the library on the system and defines some variables like SomeLib_FOUND etc.
My CMakeLists.txt contains something like this:
set(CMAKE_MODULE_PATH "/usr/local/lib/SomeLib/cmake/;${CMAKE_MODULE_PATH}")
find_package(SomeLib REQUIRED)
The first command defines where CMake searches after the Find*.cmake and I added the directory of SomeLib where the FindSomeLib.cmake can be found, so find_package() works
as expected.
But this is kind of weird because one of the reasons why find_package() exists is to get away from non-cross-plattform hard coded paths.
How is this usually done? Should I copy the cmake/ directory of SomeLib into my project and set the CMAKE_MODULE_PATH relatively?
Command find_package has two modes: Module mode and Config mode. You are trying to
use Module mode when you actually need Config mode.
Module mode
Find<package>.cmake file located within your project. Something like this:
CMakeLists.txt
cmake/FindFoo.cmake
cmake/FindBoo.cmake
CMakeLists.txt content:
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
find_package(Foo REQUIRED) # FOO_INCLUDE_DIR, FOO_LIBRARIES
find_package(Boo REQUIRED) # BOO_INCLUDE_DIR, BOO_LIBRARIES
include_directories("${FOO_INCLUDE_DIR}")
include_directories("${BOO_INCLUDE_DIR}")
add_executable(Bar Bar.hpp Bar.cpp)
target_link_libraries(Bar ${FOO_LIBRARIES} ${BOO_LIBRARIES})
Note that CMAKE_MODULE_PATH has high priority and may be usefull when you need to rewrite standard Find<package>.cmake file.
Config mode (install)
<package>Config.cmake file located outside and produced by install
command of other project (Foo for example).
foo library:
> cat CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(Foo)
add_library(foo Foo.hpp Foo.cpp)
install(FILES Foo.hpp DESTINATION include)
install(TARGETS foo DESTINATION lib)
install(FILES FooConfig.cmake DESTINATION lib/cmake/Foo)
Simplified version of config file:
> cat FooConfig.cmake
add_library(foo STATIC IMPORTED)
find_library(FOO_LIBRARY_PATH foo HINTS "${CMAKE_CURRENT_LIST_DIR}/../../")
set_target_properties(foo PROPERTIES IMPORTED_LOCATION "${FOO_LIBRARY_PATH}")
By default project installed in CMAKE_INSTALL_PREFIX directory:
> cmake -H. -B_builds
> cmake --build _builds --target install
-- Install configuration: ""
-- Installing: /usr/local/include/Foo.hpp
-- Installing: /usr/local/lib/libfoo.a
-- Installing: /usr/local/lib/cmake/Foo/FooConfig.cmake
Config mode (use)
Use find_package(... CONFIG) to include FooConfig.cmake with imported target foo:
> cat CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(Boo)
# import library target `foo`
find_package(Foo CONFIG REQUIRED)
add_executable(boo Boo.cpp Boo.hpp)
target_link_libraries(boo foo)
> cmake -H. -B_builds -DCMAKE_VERBOSE_MAKEFILE=ON
> cmake --build _builds
Linking CXX executable Boo
/usr/bin/c++ ... -o Boo /usr/local/lib/libfoo.a
Note that imported target is highly configurable. See my answer.
Update
Example
If you are running cmake to generate SomeLib yourself (say as part of a superbuild), consider using the User Package Registry. This requires no hard-coded paths and is cross-platform. On Windows (including mingw64) it works via the registry. If you examine how the list of installation prefixes is constructed by the CONFIG mode of the find_packages() command, you'll see that the User Package Registry is one of elements.
Brief how-to
Associate the targets of SomeLib that you need outside of that external project by adding them to an export set in the CMakeLists.txt files where they are created:
add_library(thingInSomeLib ...)
install(TARGETS thingInSomeLib Export SomeLib-export DESTINATION lib)
Create a XXXConfig.cmake file for SomeLib in its ${CMAKE_CURRENT_BUILD_DIR} and store this location in the User Package Registry by adding two calls to export() to the CMakeLists.txt associated with SomeLib:
export(EXPORT SomeLib-export NAMESPACE SomeLib:: FILE SomeLibConfig.cmake) # Create SomeLibConfig.cmake
export(PACKAGE SomeLib) # Store location of SomeLibConfig.cmake
Issue your find_package(SomeLib REQUIRED) commmand in the CMakeLists.txt file of the project that depends on SomeLib without the "non-cross-platform hard coded paths" tinkering with the CMAKE_MODULE_PATH.
When it might be the right approach
This approach is probably best suited for situations where you'll never use your software downstream of the build directory (e.g., you're cross-compiling and never install anything on your machine, or you're building the software just to run tests in the build directory), since it creates a link to a .cmake file in your "build" output, which may be temporary.
But if you're never actually installing SomeLib in your workflow, calling EXPORT(PACKAGE <name>) allows you to avoid the hard-coded path. And, of course, if you are installing SomeLib, you probably know your platform, CMAKE_MODULE_PATH, etc, so #user2288008's excellent answer will have you covered.
How is this usually done? Should I copy the cmake/ directory of SomeLib into my project and set the CMAKE_MODULE_PATH relatively?
If you don't trust CMake to have that module, then - yes, do that - sort of: Copy the find_SomeLib.cmake and its dependencies into your cmake/ directory. That's what I do as a fallback. It's an ugly solution though.
Note that the FindFoo.cmake modules are each a sort of a bridge between platform-dependence and platform-independence - they look in various platform-specific places to obtain paths in variables whose names is platform-independent.
You don't need to specify the module path per se. CMake ships with its own set of built-in find_package scripts, and their location is in the default CMAKE_MODULE_PATH.
The more normal use case for dependent projects that have been CMakeified would be to use CMake's external_project command and then include the Use[Project].cmake file from the subproject. If you just need the Find[Project].cmake script, copy it out of the subproject and into your own project's source code, and then you won't need to augment the CMAKE_MODULE_PATH in order to find the subproject at the system level.