conan-io / conan

Conan - The open-source C and C++ package manager
https://conan.io
MIT License
8.25k stars 980 forks source link

[bug] Unable to set cmake_config_version_compat property from consumer conanfile #14807

Closed samuel-emrys closed 1 year ago

samuel-emrys commented 1 year ago

Environment details

Steps to reproduce

  1. conan new cmake_lib -d name=foo -d version=0.1.0
  2. Apply the following changes:
    
    ---
    diff --git a/CMakeLists.txt b/CMakeLists.txt
    index f4cdb97..d491176 100644
    --- a/CMakeLists.txt
    +++ b/CMakeLists.txt
    @@ -1,13 +1,11 @@
    cmake_minimum_required(VERSION 3.15)
    project(foo CXX)

+find_package(armadillo 9.800.0)

add_library(foo src/foo.cpp) target_include_directories(foo PUBLIC include)

+target_link_libraries(foo armadillo::armadillo)

set_target_properties(foo PROPERTIES PUBLIC_HEADER "include/foo.h") install(TARGETS foo) diff --git a/conanfile.py b/conanfile.py index 1f8e43a..e6310ec 100644 --- a/conanfile.py +++ b/conanfile.py @@ -33,8 +33,12 @@ class fooRecipe(ConanFile): def layout(self): cmake_layout(self)

$ cat build/Release/generators/armadillo-config-version.cmake
set(PACKAGE_VERSION "12.2.0")

if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
    set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()
    if("12.2.0" MATCHES "^([0-9]+)\\.")
        set(CVF_VERSION_MAJOR ${CMAKE_MATCH_1})
    else()
        set(CVF_VERSION_MAJOR "12.2.0")
    endif()

    if(PACKAGE_FIND_VERSION_MAJOR STREQUAL CVF_VERSION_MAJOR)
        set(PACKAGE_VERSION_COMPATIBLE TRUE)
    else()
        set(PACKAGE_VERSION_COMPATIBLE FALSE)
    endif()

    if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION)
        set(PACKAGE_VERSION_EXACT TRUE)
    endif()
endif()

Logs

$ conan build .

======== Input profiles ========
Profile host:
[settings]
arch=x86_64
build_type=Release
compiler=clang
compiler.cppstd=20
compiler.libcxx=libstdc++11
compiler.version=16
os=Linux
[conf]
tools.build:compiler_executables={'c': '/usr/bin/clang', 'cpp': '/usr/bin/clang++'}

Profile build:
[settings]
arch=x86_64
build_type=Release
compiler=clang
compiler.cppstd=20
compiler.libcxx=libstdc++11
compiler.version=16
os=Linux
[conf]
tools.build:compiler_executables={'c': '/usr/bin/clang', 'cpp': '/usr/bin/clang++'}

======== Computing dependency graph ========
Graph root
    conanfile.py (foo/0.1.0): /home/user/projects/test/config-version-test/conanfile.py
Requirements
    armadillo/12.2.0#9e60c960e487af50970eda6b169a7a90 - Cache
    openblas/0.3.20#bc54895c0f1f0a58c8ea414a3be55950 - Cache

======== Computing necessary packages ========
Requirements
    armadillo/12.2.0#9e60c960e487af50970eda6b169a7a90:d1f701e1085136c6bf96d01d466b8251a03bfda5#b3bb81d2ff3a59c782040fd60c10a902 - Cache
    openblas/0.3.20#bc54895c0f1f0a58c8ea414a3be55950:cc2c5af420e7ff29920090410acccedc80665287#981031f2dbb0371a39c4bfdb7bbd9f90 - Cache

======== Installing packages ========

======== Installing packages ========
openblas/0.3.20: Already installed! (1 of 2)
openblas/0.3.20: Setting OpenBLAS_HOME environment variable: /home/user/.conan2/p/b/openbac3f4148410b0/p
armadillo/12.2.0: Already installed! (2 of 2)
WARN: deprecated: Usage of deprecated Conan 1.X features that will be removed in Conan 2.X:
WARN: deprecated:     'env_info' used in: openblas/0.3.20
WARN: deprecated:     'cpp_info.names' used in: openblas/0.3.20

======== Finalizing install (deploy, generators) ========
conanfile.py (foo/0.1.0): Calling generate()
conanfile.py (foo/0.1.0): Generators folder: /home/user/projects/test/config-version-test/build/Release/generators
conanfile.py (foo/0.1.0): CMakeToolchain generated: conan_toolchain.cmake
conanfile.py (foo/0.1.0): Preset 'conan-release' added to CMakePresets.json. Invoke it manually using 'cmake --preset conan-release' if using CMake>=3.23
conanfile.py (foo/0.1.0): If your CMake version is not compatible with CMakePresets (<3.23) call cmake like: 'cmake <path> -G "Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=/home/user/projects/test/config-version-test/build/Release/generators/conan_toolchain.cmake -DCMAKE_POLICY_DEFAULT_CMP0091=NEW -DCMAKE_BUILD_TYPE=Release'
conanfile.py (foo/0.1.0): CMakeToolchain generated: CMakePresets.json
conanfile.py (foo/0.1.0): CMakeToolchain generated: ../../../CMakeUserPresets.json
conanfile.py (foo/0.1.0): Generating aggregated env files
conanfile.py (foo/0.1.0): Generated aggregated env files: ['conanbuild.sh', 'conanrun.sh']

======== Calling build() ========
conanfile.py (foo/0.1.0): Calling build()
conanfile.py (foo/0.1.0): Running CMake.configure()
conanfile.py (foo/0.1.0): RUN: cmake -G "Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE="/home/user/projects/test/config-version-test/build/Release/generators/conan_toolchain.cmake" -DCMAKE_INSTALL_PREFIX="/home/user/projects/test/config-version-test" -DCMAKE_POLICY_DEFAULT_CMP0091="NEW" -DCMAKE_BUILD_TYPE="Release" "/home/user/projects/test/config-version-test"
-- Using Conan toolchain: /home/user/projects/test/config-version-test/build/Release/generators/conan_toolchain.cmake
-- Conan toolchain: Setting CMAKE_POSITION_INDEPENDENT_CODE=ON (options.fPIC)
-- Conan toolchain: C++ Standard 20 with extensions OFF
-- Conan toolchain: Setting BUILD_SHARED_LIBS = OFF
-- The CXX compiler identification is Clang 16.0.6
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/clang++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
CMake Warning at CMakeLists.txt:4 (find_package):
  Could not find a configuration file for package "armadillo" that is
  compatible with requested version "9.800.0".

  The following configuration files were considered but not accepted:

    /home/user/projects/test/config-version-test/build/Release/generators/armadillo-config.cmake, version: 12.2.0

-- Configuring done (0.3s)
CMake Error at CMakeLists.txt:8 (target_link_libraries):
  Target "foo" links to:

    armadillo::armadillo

  but the target was not found.  Possible reasons include:

    * There is a typo in the target name.
    * A find_package call is missing for an IMPORTED target.
    * An ALIAS target is missing.

-- Generating done (0.0s)
CMake Warning:
  Manually-specified variables were not used by the project:

    CMAKE_POLICY_DEFAULT_CMP0091

CMake Generate step failed.  Build files cannot be regenerated correctly.

*********************************************************
Recipe 'conanfile.py (foo/0.1.0)' cannot build its binary
It is possible that this recipe is not Conan 2.0 ready
If the recipe comes from ConanCenter, report it at https://github.com/conan-io/conan-center-index/issues
If it is your recipe, check if it is updated to 2.0
*********************************************************

ERROR: conanfile.py (foo/0.1.0): Error in build() method, line 48
        cmake.configure()
        ConanException: Error 1 while executing
memsharded commented 1 year ago

Hi @samuel-emrys

Thanks very much for reporting. Indeed, there is a gap here, the cmake_config_version_compat works only when defined in the dependency, not when overriden from the downstream consumer, because the idea is that this doesn't make sense to do it in the consumer, because the one knowing and declaring the compatibility is the package itself, not the consumer. If the consumer is going to change the compatibility, why not directly changing the version that it is requiring to lie inside the original compatibility policy?

Said that, I think there is no reason to be inconsistent and having some properties that cannot be overriden from downstream, no need to discuss the semantics of each property, so lets fix this for next release.

samuel-emrys commented 1 year ago

because the idea is that this doesn't make sense to do it in the consumer, because the one knowing and declaring the compatibility is the package itself, not the consumer. If the consumer is going to change the compatibility, why not directly changing the version that it is requiring to lie inside the original compatibility policy?

Without making comment on whether it should be dictated by the consumer, this issue was prompted when packaging up the ensmallen library. They use the syntax to specify a minimum compatible version (see the commit context): https://github.com/mlpack/ensmallen/commit/1208c62381b6f64f5feccc32a978730a0b9f9fd2#diff-1e7de1ae2d059d21e1dd75d5812d5a34b0222cef273b7c3a2af62eb747f9d20aR60

find_package(Armadillo 9.800.0 REQUIRED)

Having said that, I think that there is a difference between the versioning policy upstream and what the consumer says they are compatible with. Upstream, assuming semver, a major version bump means "the public api has had a breaking change, in some way", which might look like a bump from 1.x to 2.x. On the consumer side, there's every possibility that the nature of the breaking change had no impact on their compatibility with the library because the breaking change was not part of their usage of the library. In that case, they may be comfortable saying, "we think we're compatible with anything newer than 0.1.1", and then revising that as a breaking change that does impact their usage comes out.

This will also mean that when packaging libraries, I won't need to do things like this:

        replace_in_file(
            self,
            os.path.join(self.source_folder, "CMakeLists.txt"),
            "find_package(Armadillo 9.800.0 REQUIRED)",
            "find_package(Armadillo REQUIRED)",
        )

And can instead just

def generate(self):
         deps = CMakeDeps(self)
         deps.set_property("armadillo", "cmake_config_version_compat", "AnyNewerVersion")
         deps.generate()

Anyway, just wanted to provide some additional context. I'm glad it will be in the next release :)