[vcpkg] Document vcpkg cmake wrappers#19692
Conversation
|
|
||
| ```cmake | ||
| list(REMOVE_ITEM ARGS "NO_MODULE") | ||
| list(REMOVE_ITEM ARGS "CONFIG") |
There was a problem hiding this comment.
Shouldn't this be added instead of removed?
There was a problem hiding this comment.
In fact, it is added.
One cannot add it blindly, because it may already be in the list. The given code removes it blindly (which is always allowed), and then explicitly adds CONFIG to the actual _find_package call.
| - CMake macro `find_dependency` from module `CMakeFindDependencyMacro` cannot | ||
| be used. |
There was a problem hiding this comment.
I think it would be good to explain why this module is inappropriate. My understanding was the opposite--that it was the preferred approach.
There was a problem hiding this comment.
It only correctly works in the original implemented find_package function and the wrapper is outside of it.
There was a problem hiding this comment.
It is intended for config files.
Cf. CMake's documentation:
Note The call to return() makes this macro unsuitable to call from Find Modules.
https://cmake.org/cmake/help/v3.21/module/CMakeFindDependencyMacro.html
(Reminder to self: CMake spelling is "Find Modules".)
There was a problem hiding this comment.
Right, I know what the intention is. But because the wrappers are include()ed, I don't understand why:
Note The call to return() makes this macro unsuitable to call from Find Modules.
Is still relevant. That line implies that calling return() in a find module is a bad thing(TM), but the wrappers are included, so I must be missing something.
There was a problem hiding this comment.
@Neumann-A got it right, but it probably needs an extra section in this guide because I already fell over it on another occassion.
It only correctly works in the original implemented
find_packagefunction
and the wrapper is outside of it.
The point is that find_dependency relies on Package File Interface Variables which are defined by CMake's original implementation of find_package: CMAKE_FIND_PACKAGE_NAME, <PackageName>_FIND_QUIETLY etc.
But the wrapper is included from a macro in vcpkg.cmake, and the Package File Interface Variables are not available. find_dependency simply fails to achieve the side effects which are its key feature.
There was a problem hiding this comment.
Thanks for the explanation. I think a synopsis of that would be great in the documentation.
| by the user. In order to not limit the usability of vcpkg, wrappers must not use | ||
| language features of CMake newer than version 3.4 unless really necessary. |
There was a problem hiding this comment.
Should be dependent on the CMake version the port internally uses itself. If the ports CMakeLists.txt is requiring 3.17 I think we should also require that in the wrapper since the -Config.cmake will probably not work with an earlier version
There was a problem hiding this comment.
Indeed. Now this needs some good wording.
"In general, wrappers shall not use language features of CMake newer than version 3.4. When loading config files, wrappers may use language features of the version required by the config files or port. In this case, an explicit version check and error message shall be added in order to inform vcpkg users."
|
|
||
| - If a wrapper needs to create variables, these variables shall be prefixed | ||
| with `Z_VCPKG`. They must not be used without initialization. | ||
| - If a wrapper creates CMake targets, these targets shall be namespaced by |
There was a problem hiding this comment.
| - If a wrapper creates CMake targets, these targets shall be namespaced by | |
| - If a wrapper creates additional/new CMake targets, these targets shall be namespaced by |
There was a problem hiding this comment.
I think I understand the intention: "not provided by CMake or upstream". Is a single extra word enough to make it clear for new contributors?
There was a problem hiding this comment.
Yeah that was the intention. we are free to add the same targets before/as a Find does to fix the target before the module messes it ups. This only works because all modules do a if(NOT TARGET <sometarget>) check
| To capture the features in the installed wrapper, `vcpkg-cmake-wrapper.cmake.in` | ||
| must contain a line like: | ||
|
|
||
| ```cmake | ||
| set(Z_VCPKG_FEATURES "@FEATURES@") | ||
| ``` |
There was a problem hiding this comment.
why declare an extra variable? theoretically it is often used as if(<somefeature>).
There was a problem hiding this comment.
This is written with the tiff wraper in mind. There aren't variables like <somefeature> which reflect the individual features - it would be necessary to create them first.
But the preceding paragraph might be extended reflect the recommendation to prefer <somefeature> variables (replacements) if they exist:
"If the portfile sets variables reflecting individual dependencies, it is recomended to use these variables directly:
if(@FEATURE_VARIABLE@)
Alternatively, the wrapper may capture the FEATURES variable:
..."
There was a problem hiding this comment.
There aren't variables like which reflect the individual
vcpkg_check_features does a create variables. So we could either use those or just directly pass -DZ_VCPKG_FEATURES=${FEATURES} via vcpkg_configure_make. No need to create Z_VCPKG_FEATURES in the wrapper
There was a problem hiding this comment.
vcpkg_check_features does a create variables.
Just one variable, and not in a way which has any added value here: Basically, it transforms variable FEATURES to some <OUT_VAR> which will e.g. contain -DENABLE_ZLIB=ON where FEATURES has an item zlib.
When <OUT_VAR> is passed to cmake, cmake will create a (cache) variables - in project mode while building the port. But these variables are not available in the wrapper which runs in consuming projects, and also not in the portfile which calls configure_file.
There was a problem hiding this comment.
So we could either use those or just directly pass
-DZ_VCPKG_FEATURES=${FEATURES}viavcpkg_configure_make.
This goes to the ported software, but this software knows nothing about wrappers (without patching).
And the software consuming port tiff knows nothing about the features built into port tiff - it must be baked into the tiff wrapper.
There was a problem hiding this comment.
ah yeah forgot that the wrapper needs to be hardcoded. I still prefer if(NOT "@<somefeature>@" STREQUAL "")
There was a problem hiding this comment.
Okay. Well, I don't see a <somefeature> most of the time.
To use FEATURES without another variable, I can offer once more:
if(";@FEATURES@;" MATCHES ";my-feature;")
Last not least we may add a vcpkg function if this leads to a clean approach.
There was a problem hiding this comment.
Okay. I now realized that "vcpkg_check_features does create variables", and how. It is documented.
However, I'm really surprised:
- There are three examples in the documentation - and none uses anything but
FEATURE_OPTIONS. - There is no need to make
OUT_FEATURE_OPTIONSa mandatory parameter when the function has useful effects even without it.
But I don't want to start a vcpkg_check_features here. I will update this PR to avoid @FEATURES@.
There was a problem hiding this comment.
I tested this for port tiff. There is one aspect which I dislike about using the individual variables from vcpkg_check_features directly: The if statements in the generated wrapper don't give any hint about the actual condition:
-if("zip" IN_LIST _tiff_features)
+if(ON)
The upside is that typos in variable names become quickly visible, because empty replacements lead to illegal syntax (if()).
There was a problem hiding this comment.
One more lesson from the tiff changes:
if(ON) needs policy CMP0012. This means uo to three more lines (push, set, pop), to cater for ports which have a very low cmake_minimum_required(VERSION ...) (here: port libgeotiff).
| ## Enforce loading of exported config files | ||
|
|
||
| If a port exports config files which provide accurate information as expected | ||
| by users of the find module, wrappers may enforce the loading of the config | ||
| files by adjusting the argument list: | ||
|
|
||
| ```cmake | ||
| list(REMOVE_ITEM ARGS "NO_MODULE") | ||
| list(REMOVE_ITEM ARGS "CONFIG") | ||
| list(REMOVE_ITEM ARGS "MODULE") | ||
| _find_package(${ARGS} CONFIG) | ||
| ``` |
There was a problem hiding this comment.
| ## Enforce loading of exported config files | |
| If a port exports config files which provide accurate information as expected | |
| by users of the find module, wrappers may enforce the loading of the config | |
| files by adjusting the argument list: | |
| ```cmake | |
| list(REMOVE_ITEM ARGS "NO_MODULE") | |
| list(REMOVE_ITEM ARGS "CONFIG") | |
| list(REMOVE_ITEM ARGS "MODULE") | |
| _find_package(${ARGS} CONFIG) | |
| ``` |
This is not allowed in general. Make normal Find<Modules>.cmake work with the wrapper instead!
There was a problem hiding this comment.
Not allowed, or simply not possible? I tried to express the limitations in the first part of the sentence.
I agree with the recommendation to make regular Find Modules just work. Loading config files is a road into the CMake language features jungle, limiting usability.
There was a problem hiding this comment.
Not allowed, or simply not possible?
not possible. Since you do not know if a custom Find with a same name is used which sets completely different variables/targets. by rerouting to config it is not possible to patch just the Find but the whole build-system needs to be patched.
ras0219-msft
left a comment
There was a problem hiding this comment.
This is awesome, thank you so much for writing all this tribal knowledge down!
I think a lot of the suggestions above are really great, so I'm marking this PR as a draft while you have a chance to look through them and apply the ones you'd like. Once you're ready for this to be merged, just mark it ready for review :)
|
On Some find modules and wrappers use Apart from matching the particular find module behaviour, I wonder if it would be preferred to generally avoid However, I have no idea if this works with the multi-config generators.
(I keep coming back to the idea of encapsulating this stuff in a helper function. Such a function might provide a user variable to change behaviour when needed in a user project.) |
In general I never found a valid case of
This breaks multi configuration generators. |
| If not implemented in the find module, the wrapper must take care of selecting | ||
| the right configuration for the `<Pkg>_LIBRARIES` variable. Remember that this | ||
| depends on the behaviour of the find module in the lowest supported version of | ||
| CMake. | ||
|
|
||
| ```cmake | ||
| include(SelectLibraryConfigurations) | ||
| select_library_configurations(<Pkg>) | ||
| unset(<Pkg>_FOUND) # https://gitlab.kitware.com/cmake/cmake/-/issues/22509 | ||
| ``` |
There was a problem hiding this comment.
select_library_configurations should only be called after find_package(). Either the module itself already called it then it is unnecessary or the module used <PKG>_LIBRARY to maybe set IMPORTED_LOCATION of a target. In this case we cannot modify <PKG>_LIBRARY before find_package()
There was a problem hiding this comment.
This section is about (older) modules (versions) that do not call select_library_configurations but do use find_library(<PKG>_LIBRARY ...). So the "Either" part is out of scope.
I try to reflect the argument against calling select_library_configurations and setting cache variable <PKG>_LIBRARY before find_package() in this case. IMPORTED_LOCATION is documented to be a Full path, not a list. This is in line with the problem for get_filename_component(... <PKG>_LIBRARY ...). However, <PKG>_LIBRARY has no multi-config alternative, but IMPORTED_LOCATION has.
I don't think this can generally be resolved. But maybe the scope of support for older versions of CMake in user projects should be explicitly limited to
- that CMake version being at least 3.4,
- the config files used by the project supporting that CMake version,
- the user project, and the CMake Find modules used by the project,
- either not using config-unaware CMake features (e.g.
target propertyIMPORTED_LOCATION,get_filename_component(... <PKG>_LIBRARY ...)) - or using single-config generators.
- either not using config-unaware CMake features (e.g.
(This is not for the wrapper documentation but for the CMake requirements.)
That's why it could be only an opt-in alternative. But to implement a global choice, it needs a single function implementing that choice. |
|
Any progress here? |
|
Well, I could do an update, based on the experiences collected. |
|
Consider to finish this PR now? |
|
Ping for response. |
|
Please temporary close this PR if you don't have spare time to finish it. |
|
Closing this PR since it seems that no progress is being made. Please reopen if work is still being done. |
|
I cannot reopen. Stop closing my drafts. |
|
This is incredibly useful information and it's a shame it has been sitting for so long! Thank you so much for writing it, @dg0yt. Since our new docs are in Microsoft/vcpkg-docs, I've taken liberty to migrate the work over to a branch MicrosoftDocs/vcpkg-docs@main...ras0219-msft:vcpkg-docs:pr-19692. This branch currently contains two commits:
I've attempted to apply all the critical comments in this PR, though I've probably missed some things. Most importantly, I've rewritten the recommendation on how to bake in features: This resolves both the problem of @dg0yt How would you like to proceed with the work you've done here? My suggestions are:
|
|
I'm hesitating to put time into this at the moment. TBH I would prefer to discourage the current form of wrappers and get to an implementation based on vcpkg find modules which call into external find modules.
|
|
Closing this PR for now, as our docs have moved. Please open a new PR there if you are still working on this. Thanks! |
What does your PR fix?
This PR fixes the lack of documentation for vcpg cmake wrappers. It is a start - some topics remain to be added.
Which triplets are supported/not supported? Have you updated the CI baseline?
not relevant
Does your PR follow the maintainer guide?
yes
If you have added/updated a port: Have you run
./vcpkg x-add-version --alland committed the result?not relevant