[CMake] Creating a relocatable ProjectConfig.cmake when build uses absolute paths

Craig Scott craig.scott at crascit.com
Sun Jan 14 16:29:14 EST 2018


(Includes earlier reply from me not sent to the list)


On Fri, Jan 12, 2018 at 9:10 AM, Lucas Soltic <lucas.soltic at orange.fr>
wrote:
>
> On Thu, Jan 11, 2018 at 10:09 AM, Lucas Soltic <lucas.soltic at orange.fr>
> wrote:
>
>> Hello,
>>
>> I'm trying to create a relocatable package configuration file but I'm
>> having a hard time with absolute paths that are used during the build.
>> Note that I use CMake 3.10.0.
>>
>> First of all for the include path I'm using this:
>> target_include_directories(MyStaticTarget PUBLIC
>>                            $<BUILD_INTERFACE:${PROJECT_S
>> OURCE_DIR}/include>
>>                            $<INSTALL_INTERFACE:include>)
>>
>> This does work but according to the doc it should not :
>> https://cmake.org/cmake/help/latest/manual/cmake-generator
>> -expressions.7.html?highlight=build_interface#output-expressions
>> The doc of BUILD_INTERFACE says: "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."
>>
>
> I'm with you to here.
>
>
>> So according to the doc, when compiling the target MyTarget, the include
>> dir I gave should not be used. Which seems consistent with the other uses
>> of INTERFACE wording in CMake language. But it actually behaves like PUBLIC
>> rather than INTERFACE. This is fine to me but inconsistent… :)
>>
>
> This part is less clear. What is MyTarget? Is it something you link to
> MyStaticTarget? If so, then the include dir *should* be used. In the
> target_include_directories() call above, the content of the generator
> expressions will both be treated as PUBLIC if they expand to something
> non-empty. Don't be confused by the word INTERFACE in the generator
> expressions, it is the PUBLIC before them that is controlling the
> visibility/transitivity of these include paths. Think of BUILD_INTERFACE as
> meaning "only when I'm building with this thing" and INSTALL_INTERFACE as
> "only when this thing is installed (i.e. put this in an export file but
> don't use it in the build I created it in)".
>
>
> Arg I messed up my original e-mail. "MyTarget" is the same as
> "MyStaticTarget". I just didn't rename everywhere…
> I rather have a clear vision about what INTERFACE, PUBLIC and PRIVATE
> keywords mean when used in target_...() commands so I understand that my
> paths will be used both by MyStaticTarget and targets that depend on it.
> It's just that BUILD_INTERFACE's documentation is misleading. Apart from
> that I deduced the same as what you said :)
>
>
> The second point is about libraries to link when using my project. My
>> project exports a *static* library that depends on other libraries.
>> These libraries are found (when generating my project through cmake)
>> through calls like find_package(OpenGL). This provides a variable that
>> contains an absolute non-relocatable path.
>>
>> I've tried doing things like this:
>> target_link_libraries(MyStaticTarget PRIVATE
>>                       $<BUILD_INTERFACE:${OpenGL_absolute_path}>
>>                       $<INSTALL_INTERFACE:${OpenGL_r
>> elocatable_link_flag}>)
>>
>> According to https://cmake.org/pipermail/cmake/2016-May/063400.html, for
>> static libraries, PRIVATE link dependencies become "PUBLIC", which is ok.
>>
>
> They only sort-of become PUBLIC. Only the linking part gets promoted to
> PUBLIC, not things like include paths, compiler defines or compiler options.
>
>
> Indeed. So far so good :)
>
>
>
>
>> And I could see that the link flag for this "private" dependency got
>> exported in the generated config.
>>
>
>
> Short version: I *think* if you use the import libraries that the
> FindOpenGL module provides rather than the variables, you will have more
> success.
>
>
> Long version (this is going to get quite involved, sorry): If your static
> library relies on an external library and you want that external dependency
> to be part of the exported definition of your target, then you'll have to
> do some extra work in how you provide your installed package. If you are
> using install(EXPORT) to produce files that other projects pick up via
> find_package(), then you probably want to be using find_dependency() from
> the CMakeFindDependencyMacro
> <https://cmake.org/cmake/help/latest/module/CMakeFindDependencyMacro.html>
> module from inside your package's XXXConfig.cmake file. This will define
> the imported targets that your package's import target should, I believe,
> be expecting to exist and rely on as an interface dependency. Sketching it
> out, it might look something like this (untested, tired brain, so no
> guarantees!):
>
> # Contents of XXXConfig.cmake:
>
> include(CMakeFindDependencyMacro)
> find_dependency(OpenGL)   # Makes sure the OpenGL::GL import target exists
> include(XXXexports)       # The file you are probably already creating now
> for your package
>
> I think the exported target will already have OpenGL::GL in the list of
> interface libraries for the MyStaticTarget import target created by
> XXXexports in the above, but if not it should be easy enough to add that to
> the end of the XXXConfig.cmake sample code (maybe a call to
> target_link_libraries() or set_property(TARGET MyStaticTarget...)).
>
> Back in your original project, the target_link_libraries() call should be
> simplified down to:
>
>
> find_package(OpenGL REQUIRED)
>
> target_link_libraries(MyStaticTarget PRIVATE OpenGL::GL)
>
>
> The MyStaticTarget will then have a link dependency on OpenGL::GL in both
> the build tree and when installed. It's very likely I've made errors in the
> above, but hopefully there's enough there for you to find your way to a
> solution.
>
>
> I like this solution, but I can't use it as is. For 2 reasons: OpenGL was
> one example, but I also have other dependencies that don't provide an
> imported target, and the second reason is that this imported target for
> OpenGL is only available since CMake 3.8 and I must support at least up to
> 3.5.
>
> Also I said "as is" because this makes me think that I could potentially
> define, in my project, an IMPORTED or INTERFACE library for OpenGL that
> would be used in a target_link_libraries() in my project. And in
> <Project>Config.cmake it would allow me to make use of the
> find_dependency() you suggested to create again this target.
>

Indeed. At the point where you call find_package(OpenGL), you could then
test for the existence of the OpenGL::GL library and if it doesn't exist,
you can define it yourself. This should allow you to use the import library
with CMake versions earlier than the one where the OpenGL import library
support was added.




>
> The only downside I can see with this is that the <Project>Config.cmake
> file will need to be kept synchronized with the dependencies that are used
> in various places in my project, instead of having this done automatically
> (and thus always synchronized).
>

Pretty much. I think there have been some discussions in the issue tracker
and/or merge requests over the past year related to how to propagate
dependencies similar to this topic. It's a difficult area with no easy
solution unfortunately.




> But the problem is that whatever I put for INSTALL_INTERFACE, it is
>> ignored and not written to the generated cmake config file. The generated
>> CMake config file just takes into account what I put for BUILD_INTERFACE.
>> Contrary to what happened with include directories.
>>
>
> This sounds strange. Can you provide a simple, complete project that
> reproduces the problem? If so, please open an issue in the bug tracker and
> attach it.
>
>
> I reproduced it with a minimal example, and found out what is causing the
> issue.
> At the beginning of the project, if I put cmake_minimum_required(VERSION
> 2.8.3) (which is what the project I'm working in currently uses) then in
> the generated config, only what's specified in BUILD_INTERFACE generator
> expression is taken into account.
>
> If I change the requirement to version 3.0, the generated config file
> contains what I specified for INSTALL_INTERFACE generator expression.
> I never realized that this command had an effect on CMake policies. At
> least now everything makes sense :)
>



-- 
Craig Scott
Melbourne, Australia
https://crascit.com
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://cmake.org/pipermail/cmake/attachments/20180115/cea29e50/attachment.html>


More information about the CMake mailing list