conan-io / conan

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

[question] Not pulling in sub-dependencies #15993

Closed coreyhartley closed 5 months ago

coreyhartley commented 5 months ago

What is your question?

I'm having a real hard time with sub-dependencies. Essentially, I have 2 projects, A and B. B is a dependency of A. B has several 3rd-party dependencies of it's own. When I go add B as a "requirement" to A in my conanfile.py, A is unable to pull in B's depndencies.

I've tried scouring the documentation, as well as what is available on the internet, even ChatGPT. I can't find a resource that can tell me how to properly do this.

The only way I have found around this is for A to redeclare all sub-dependencies. But then I have had issues doing this from conflicting libraries builds, somehow.

Is it possible to have sub-dependencies without redefining all of them in my conanfile.py and CMakeLists.txt? If so, what am I doing wrong? It looks like Conan automatically attempts to install them... If so, what am I doing wrong?

A - conanfile.py:

import os
from conan import ConanFile
from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout, CMakeDeps

class ARecipe(ConanFile):
    name = "A"
    package_type = "library"

    # Binary configuration
    settings = "os", "compiler", "build_type", "arch"
    options = {"shared": [True, False], "fPIC": [True, False]}
    default_options = {"shared": False, "fPIC": True, "*:cppstd": 17}

    # Sources are located in the same place as this recipe, copy them to the recipe
    exports_sources = "CMakeLists.txt", "src/*"

    def config_options(self):
        if self.settings.os == "Windows":
            self.options.rm_safe("fPIC")

    def configure(self):
        if self.options.shared:
            self.options.rm_safe("fPIC")

    def layout(self):
        cmake_layout(self)

    def generate(self):
        deps = CMakeDeps(self)
        deps.generate()
        tc = CMakeToolchain(self)
        tc.generate()

    def build(self):
        cmake = CMake(self)
        cmake.configure()
        cmake.build()

    def package(self):
        cmake = CMake(self)
        cmake.install()

    def package_info(self):
        self.cpp_info.libs = ["A"]

    def requirements(self):
        self.requires("B/[~v0.1]")

A - CMakeLists.txt:

cmake_minimum_required(VERSION 3.15)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
project(A CXX)

file(GLOB HEADERS
    src/*.hpp
)

file(GLOB SOURCES
    src/*.cpp
)

find_package(B REQUIRED)
find_package(C REQUIRED)

add_library(${PROJECT_NAME} ${SOURCES})

set_target_properties(${PROJECT_NAME} PROPERTIES PUBLIC_HEADER "${HEADERS}")

target_include_directories(${PROJECT_NAME} PUBLIC src)

target_link_libraries(${PROJECT_NAME} PUBLIC 
    B::B
    C::C
)

# Moves everything into a services subfolder
install(DIRECTORY src
    DESTINATION include/A/things
    FILES_MATCHING PATTERN "*.hpp"
)

install(TARGETS ${PROJECT_NAME}
    EXPORT ${PROJECT_NAME}Targets
    PUBLIC_HEADER DESTINATION include/A/things
)

B - conanfile.py:

import os
from conan import ConanFile
from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout, CMakeDeps

class BRecipe(ConanFile):

    name = "B"
    package_type = "library"

    # Binary configuration
    settings = "os", "compiler", "build_type", "arch"
    options = {"shared": [True, False], "fPIC": [True, False]}
    default_options = {"shared": False, "fPIC": True, "*:cppstd": 17}

    # Sources are located in the same place as this recipe, copy them to the recipe
    exports_sources = "CMakeLists.txt", "src/**"

    def config_options(self):
        if self.settings.os == "Windows":
            self.options.rm_safe("fPIC")

    def configure(self):
        if self.options.shared:
            self.options.rm_safe("fPIC")

    def layout(self):
        cmake_layout(self)

    def generate(self):
        deps = CMakeDeps(self)
        deps.generate()
        tc = CMakeToolchain(self)
        tc.generate()

    def build(self):
        cmake = CMake(self)
        cmake.configure()
        cmake.build()

    def package(self):
        cmake = CMake(self)
        cmake.install()

    def package_info(self):
        self.cpp_info.libs = ["B"]
        self.cpp_info.includedirs = ["include"]

    def requirements(self):
        self.requires("cppzmq/[~4.10]")
        self.requires("cxxopts/[~3.2]")
        self.requires("nlohmann_json/[~3.11]")

    def build_requirements(self):
        self.tool_requires("cmake/[>=3.15]")

B - CMakeLists.txt:

cmake_minimum_required(VERSION 3.15)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

project(B CXX)

file(GLOB SOURCES
    src/services/*.cpp
)

file(GLOB HEADERS
    src/services/*.hpp
)

find_package(cppzmq REQUIRED)
find_package(cxxopts REQUIRED)
find_package(nlohmann_json REQUIRED)

add_library(${PROJECT_NAME} ${SOURCES})
set_target_properties(${PROJECT_NAME} PROPERTIES PUBLIC_HEADER "${HEADERS}")

target_include_directories(${PROJECT_NAME} PUBLIC 
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
    $<INSTALL_INTERFACE:src>
)

target_link_libraries(${PROJECT_NAME} PUBLIC 
    cppzmq 
    cxxopts::cxxopts
    nlohmann_json::nlohmann_json
)
install(TARGETS ${PROJECT_NAME}
    EXPORT ${PROJECT_NAME}Targets
    PUBLIC_HEADER DESTINATION include/B/things
)

# Moves everything into a services subfolder
install(DIRECTORY src
    DESTINATION include/B/things
    FILES_MATCHING PATTERN "*.hpp"
)

install(EXPORT ${PROJECT_NAME}Targets
    FILE ${PROJECT_NAME}Targets.cmake
    DESTINATION lib/cmake/${PROJECT_NAME}
)

Output:

======== Installing packages ========
cmake/3.28.1: Already installed! (1 of 9)
cmake/3.28.1: Appending PATH environment variable: C:\Users\corey.hartley\.conan2\p\cmake615d6e26ece18\p\bin
libsodium/1.0.19: Already installed! (2 of 9)
zlib/1.3: Already installed! (3 of 9)
zeromq/4.3.5: Already installed! (4 of 9)
openssl/3.2.0: Already installed! (5 of 9)
cppzmq/4.10.0: Already installed! (6 of 9)
capnproto/1.0.1: Already installed! (7 of 9)
B/v0.1.1: Already installed! (8 of 9)
C/v0.2.0: Already installed! (9 of 9)
WARN: deprecated: Usage of deprecated Conan 1.X features that will be removed in Conan 2.X:
WARN: deprecated:     'env_info' used in: openssl/3.2.0, cmake/3.28.1, capnproto/1.0.1
WARN: deprecated:     'cpp_info.names' used in: capnproto/1.0.1, zeromq/4.3.5, zlib/1.3, cppzmq/4.10.0, openssl/3.2.0        
WARN: deprecated:     'cpp_info.build_modules' used in: openssl/3.2.0, zeromq/4.3.5, cppzmq/4.10.0

======== Finalizing install (deploy, generators) ========
conanfile.py (A/v0.1.0): Calling generate()
conanfile.py (A/v0.1.0): Generators folder: C:\git\fulcrum-daq-abstract-cpp\build\generators        
conanfile.py (A/v0.1.0): CMakeDeps necessary find_package() and targets for your CMakeLists.txt
    find_package(B)
    find_package(C)
    target_link_libraries(... B::B C::C)
conanfile.py (At/v0.1.0): CMakeToolchain generated: conan_toolchain.cmake
conanfile.py (A/v0.1.0): Preset 'conan-default' added to CMakePresets.json. Invoke it manually using 'cmake --preset conan-default' if using CMake>=3.23
conanfile.py (A/v0.1.0): If your CMake version is not compatible with CMakePresets (<3.23) call cmake like: 'cmake <path> -G "Visual Studio 17 2022" -DCMAKE_TOOLCHAIN_FILE=C:\git\fulcrum-daq-abstract-cpp\build\generators\conan_toolchain.cmake -DCMAKE_POLICY_DEFAULT_CMP0091=NEW'
conanfile.py (A/v0.1.0): CMakeToolchain generated: CMakePresets.json
conanfile.py (A/v0.1.0): CMakeToolchain generated: ..\..\CMakeUserPresets.json
conanfile.py (A/v0.1.0): Generating aggregated env files
conanfile.py (A/v0.1.0): Generated aggregated env files: ['conanrun.bat', 'conanbuild.bat']
======== Calling build() ========
conanfile.py (A/v0.1.0): Calling build()
conanfile.py (A/v0.1.0): Running CMake.configure()
conanfile.py (A/v0.1.0): RUN: cmake -G "Visual Studio 17 2022" -DCMAKE_TOOLCHAIN_FILE="C:/git/A/build/generators/conan_toolchain.cmake" -DCMAKE_INSTALL_PREFIX="C:/git/A" -DCMAKE_POLICY_DEFAULT_CMP0091="NEW" "C:\git\A"
-- Using Conan toolchain: C:/git/A/build/generators/conan_toolchain.cmake
-- Conan toolchain: CMAKE_GENERATOR_TOOLSET=v143
-- Conan toolchain: C++ Standard 17 with extensions OFF
-- Conan toolchain: Setting BUILD_SHARED_LIBS = OFF
-- Selecting Windows SDK version 10.0.22621.0 to target Windows 10.0.22631.
-- The CXX compiler identification is MSVC 19.37.32825.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.37.32822/bin/Hostx64/x64/cl.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Conan: Target declared 'C::C'
-- Conan: Component target declared 'CapnProto::kj'
-- Conan: Component target declared 'CapnProto::kj-async'
-- Conan: Component target declared 'CapnProto::kj-http'
-- Conan: Component target declared 'CapnProto::kj-test'
-- Conan: Component target declared 'CapnProto::kj-gzip'
-- Conan: Component target declared 'CapnProto::kj-tls'
-- Conan: Component target declared 'CapnProto::capnp'
-- Conan: Component target declared 'CapnProto::capnp-json'
-- Conan: Component target declared 'CapnProto::capnp-rpc'
-- Conan: Component target declared 'CapnProto::capnpc'
-- Conan: Component target declared 'CapnProto::capnp-websocket'
-- Conan: Target declared 'capnproto::capnproto'
-- Conan: Component target declared 'OpenSSL::Crypto'
-- Conan: Component target declared 'OpenSSL::SSL'
-- Conan: Target declared 'openssl::openssl'
-- Conan: Target declared 'ZLIB::ZLIB'
-- Conan: Including build module from 'C:/Users/corey.hartley/.conan2/p/opensd90dd1a179ee9/p/lib/cmake/conan-official-openssl-variables.cmake'
-- Conan: Including build module from 'C:/Users/corey.hartley/.conan2/p/b/capnpe0b8a7f956a1f/p/lib/cmake/CapnProto/CapnProtoMacros.cmake'
-- Conan: Target declared 'B::B'
-- Conan: Target declared 'cppzmq'
-- Conan: Component target declared 'libzmq-static'
-- Conan: Target declared 'libsodium::libsodium'
-- Configuring done (2.7s)
-- Generating done (0.0s)
-- Build files have been written to: C:/git/A/build

conanfile.py (A/v0.1.0): Running CMake.build()
conanfile.py (A/v0.1.0): RUN: cmake --build "C:\git\A\build" --config Release
MSBuild version 17.7.2+d6990bcfa for .NET Framework

  1>Checking Build System
  Building Custom Rule C:/git/A/CMakeLists.txt
  daq.cpp
  message_builder.cpp
C:\git\A\src\message_builder.hpp(4,10): fatal  error C1083: Cannot open include file: 'zmq.hpp': No s
uch file or directory (compiling source file C:\git\A\src\message_builder.cpp) [C:\git\A\build\A]
C:\Users\corey.hartley\.conan2\p\b\Bca4334d876ee2\p\include\B\things\logger.hpp(4,10): fatal  error C1083: Cannot open include file: 'zmq.hpp': No such file or directory (compiling source file C:\git\A\src\daq.cpp 
) [C:\git\A\build\A]

ERROR: conanfile.py (A/v0.1.0): Error in build() method, line 55
        cmake.build()
        ConanException: Error 1 while executing
make: *** [Makefile:35: build] Error 1

Have you read the CONTRIBUTING guide?

memsharded commented 5 months ago

Hi @coreyhartley

Thanks for your question.

I am checking it, but I see some potential issues I'd like to clarify first:

Finally I think the issue that you are seeing that zmq headers are not found, is because by default transitive dependendencies hide the headers. There are 2 cases here:

Please let me know if this helps.

coreyhartley commented 5 months ago

The whole A, B, C nomenclature was just meant to simplify things. But everything else was very helpful! I think that the transitive_headers flag was exactly what I was looking for. So far it looks to be working. Thank you!

memsharded commented 5 months ago

Happy to help, thanks for the feedback! 🙂

Don't hesitate to create new tickets for any further question, thanks!