vcpkg icon indicating copy to clipboard operation
vcpkg copied to clipboard

[vcpkg] Controlling top-level find_package

Open dg0yt opened this issue 3 years ago • 16 comments

Based on recent changes to vcpkg.cmake, this PR adds two behaviours which I was looking for in the past, both related to being able to control (automatic) dependencies with regard to installation order.

Tracing find_package

Merged as https://github.com/microsoft/vcpkg/pull/27982.

Controlling top-level find_package

Some projects enable behaviours automatically based on results of top-level find_package calls. To implement port feature control without patching, maintainers use CMAKE_REQUIRE_FIND_PACKAGE_<Pkg> and/or CMAKE_DISABLE_FIND_PACKAGE_<Pkg>. However, these variable maybe used as a pair, and they affect nested requirements. The second commit adds variable (template) CMAKE_FIND_PACKAGE_<Pkg> which

  • does nothing if undefined,
  • makes top-level find_package(<Pkg>) required if true,
  • makes top-level find_package(<Pkg>) disabled if false,
  • avoid CMake warnings when CMAKE_FIND_PACKAGE_<Pkg>=ON meets find_package(<Pkg> REQUIRED).

This variable can be used easily with vcpkg_check_features. Hints can be printed when tracing is enabled.

Example output

From gdal with -DVCPKG_TRACE_FIND_PACKAGE=ON -DVCPKG_FIND_PACKAGE_CryptoPP=0 -DVCPKG_FIND_PACKAGE_PROJ=1 -DVCPKG_FIND_PACKAGE_TIFF=0:

-- find_package(CryptoPP )
--   (disabled by VCPKG_FIND_PACKAGE_CryptoPP=0)
-- find_package(PROJ 9 CONFIG QUIET)
--   (required by VCPKG_FIND_PACKAGE_PROJ=1)
--   find_package(unofficial-sqlite3 CONFIG QUIET REQUIRED)
--     find_package(Threads QUIET REQUIRED)
--   find_package(TIFF QUIET REQUIRED)
--     using share/tiff/vcpkg-cmake-wrapper.cmake
--     find_package(LibLZMA REQUIRED QUIET)
--       using share/liblzma/vcpkg-cmake-wrapper.cmake
--       find_package(Threads )
--     find_package(JPEG REQUIRED QUIET)
--       using share/jpeg/vcpkg-cmake-wrapper.cmake
--     find_package(ZLIB REQUIRED QUIET)
--       using share/zlib/vcpkg-cmake-wrapper.cmake
--   find_package(CURL QUIET REQUIRED)
--     using share/curl/vcpkg-cmake-wrapper.cmake
--     find_package(OpenSSL 3 QUIET REQUIRED)
--       using share/openssl/vcpkg-cmake-wrapper.cmake
--       find_package(PkgConfig QUIET)
--       find_package(Threads )
--       find_package(Threads REQUIRED)
--     find_package(ZLIB 1 QUIET REQUIRED)
--       using share/zlib/vcpkg-cmake-wrapper.cmake
-- find_package(ZSTD NAMES zstd)
--   (could be controlled by VCPKG_FIND_PACKAGE_ZSTD)

NB: The nested find_package(TIFF) is not affected by VCPKG_FIND_PACKAGE_TIFF=0. (FTR port gdal shouldn't use VCPKG_FIND_PACKAGE_<Pkg> because it has proper feature control.)

Use cases

  • https://github.com/microsoft/vcpkg/pull/14333#discussion_r1171859003

dg0yt avatar Nov 22 '22 05:11 dg0yt

FTR: The tracing part can be removed anytime without consequence. However, the control part creates dependencies between ports and the proposed behaviour. Suggesting team review.

dg0yt avatar Nov 22 '22 11:11 dg0yt

This seems to do something similar to #27606 and I like the idea behind this change.

I don't understand how "bad" (i.e. without REQUIRED) and "good" find_package calls can be differentiated? Can you give an example on how the output of bad find_package calls would look like?

Thomas1664 avatar Nov 22 '22 15:11 Thomas1664

Still has the problem of consecutive calls being wrongly required.

find_package(<Pkg> CONFIG) <- allowed to fail. 
find_package(<Pkg> REQUIRED) 

I like the trace however.

Neumann-A avatar Nov 22 '22 15:11 Neumann-A

Still has the problem of consecutive calls being wrongly required.

True, but I haven't seen this pattern very often. (And your example even ends with REQUIRED). It is just an offer for a common pattern.

I like the trace however.

I admit it isn't always as nice. The find_modules will insert extra output in many cases. But tracing remains possible. And in particular, you can grep for -- find_package to find top-level dependencies.

I don't understand how "bad" (i.e. without REQUIRED) and "good" find_package calls can be differentiated? Can you give an example on how the output of bad find_package calls would look like?

I can't give such an example because this is out of scope (if I understand at all what you mean). The point is to see all find_package calls (for a given execution path) and to identify top-level calls.

Continuing my comment from https://github.com/microsoft/vcpkg/pull/27606, the top-level packages should refer to direct dependencies. And apart from stdout, the tracing might go to a separate file which can be post-processed.

dg0yt avatar Nov 22 '22 16:11 dg0yt

pattern very often.

All qt6 ports do that. Component based lookup is also a possibilty

Neumann-A avatar Nov 22 '22 17:11 Neumann-A

Does the indentation reflect transitive dependencies?

I can't give such an example because this is out of scope (if I understand at all what you mean). The point is to see all find_package calls (for a given execution path) and to identify top-level calls.

In the output above I would consider

-- find_package(ZSTD NAMES zstd)
-- (could be controlled by VCPKG_FIND_PACKAGE_ZSTD)

bad because the configure step wouldn't fail if ZSTD could not be found for some reason.

Thomas1664 avatar Nov 22 '22 18:11 Thomas1664

@dg0yt Could you please solve the conflict? Thank you!

Cheney-W avatar Dec 02 '22 10:12 Cheney-W

In the output above I would consider

-- find_package(ZSTD NAMES zstd)
-- (could be controlled by VCPKG_FIND_PACKAGE_ZSTD)

bad because the configure step wouldn't fail if ZSTD could not be found for some reason.

This is exactly the kind of pattern the VCPKG_FIND_PACKAGE_ZSTD is meant to control, either directly or bound to a feature via vcpkg_check_features.

dg0yt avatar Dec 11 '22 12:12 dg0yt

FTR there is another broken pattern: using include(Find<Pkg>) instead of include(<Pkg>).

  • It circumvents the wrappers.
  • It circumvents the tracking of nesting depth.

dg0yt avatar Dec 11 '22 16:12 dg0yt

Ping.

dg0yt avatar Jan 17 '23 06:01 dg0yt

@BillyONeal Can the team look at this proposal? It would simplify to resolve unguarded function calls as noted in https://github.com/microsoft/vcpkg/pull/29070#issuecomment-1406008823. The next step might be to make vcpkg_cmake_configure even warn about uncontrolled top-level find_package calls, similar to the current warning about unused variables.

dg0yt avatar Jan 27 '23 06:01 dg0yt

We do want something like this but haven't had time to seriously look yet :(. Thanks!

BillyONeal avatar Feb 01 '23 01:02 BillyONeal

We do want something like this but haven't had time to seriously look yet :(. Thanks!

@BillyONeal Do you maybe now have time to do it? :)

autoantwort avatar Apr 13 '23 10:04 autoantwort

@BillyONeal Do you maybe now have time to do it? :)

I don't expect it to happen in this planning cycle because getting a good answer to the tools situation, possibly solving the alternatives problem, and some artifacts stuff blowing up in important partners' faces are all higher priority things. I think Neumann-A had a starting point on what it would look like. (Different toolchain file that uses the new cmake dependency provider thingy they added a version or two ago)

BillyONeal avatar Apr 13 '23 18:04 BillyONeal

Oh I got confused as to what this was actually doing. Still not sure on priority of it vs. those other bits I mentioned.

BillyONeal avatar Apr 13 '23 18:04 BillyONeal

The second commit adds variable (template) CMAKE_FIND_PACKAGE_<Pkg> which

I guess you mean VCPKG_FIND_PACKAGE_<Pkg>?

autoantwort avatar Feb 11 '24 13:02 autoantwort

Is there some use case I'm missing where it should be REQUIRED only at top level?

I think the case is in your post: We have no control over find_package in transitive dependencies. They may intentionally try repeated calls with different parameters (CONFIG, NO_CONFIG, VERSION, COMPONENTS X, HINTS ...). REQUIRED would make the first attempt required.

And the transitive code comes from other ports (= dependencies) or CMake. It changes independently. What builds now might fail when a dependency is updated. Maintainers of independent ports generally do not like to fix dependent ports. It is better to not increase coupling.

dg0yt avatar Jun 22 '24 05:06 dg0yt

If this PR gets a chance now, I can try to add a unit test.

dg0yt avatar Jun 22 '24 05:06 dg0yt

They may intentionally try repeated calls with different parameters (CONFIG, NO_CONFIG, VERSION, COMPONENTS X, HINTS ...). REQUIRED would make the first attempt required.

Makes sense.

If this PR gets a chance now, I can try to add a unit test.

The PR is getting a chance now :). The holdup has always been that we only had one reviewer who was confident enough in this CMake surgery to do a real review of something like this but he is rarely available to do reviews like that since he has manager-y things to do (that being @ras0219-msft ). In the last couple of years I think I finally understand this area well enough to review it.

BillyONeal avatar Jun 25 '24 02:06 BillyONeal

@ras0219-msft is concerned that this exposes a 'new feature' to people who aren't using vcpkg at all and wants these to only do something when they are inside a port somehow. Copying out the command line from vcpkg_cmake_configure should work though, so perhaps we need a Z_BUILDING_IN_A_PORT or similar to trigger this.

BillyONeal avatar Jul 02 '24 23:07 BillyONeal

Also I withdraw requesting "TOP_LEVEL" in the name but I would still like "LOCK". I can make that change if you want...

BillyONeal avatar Jul 02 '24 23:07 BillyONeal

Copying out the command line from vcpkg_cmake_configure should work though, so perhaps we need a Z_BUILDING_IN_A_PORT or similar to trigger this.

It doesn't depend on building ports, it depends on using the vcpkg toolchain.

I can make that change if you want...

If you can do it now, it might speed it up. I will avoid pushes until July 14.

dg0yt avatar Jul 03 '24 07:07 dg0yt