cmake-conan icon indicating copy to clipboard operation
cmake-conan copied to clipboard

Implement automatic set up of CONAN_IMPORT_PATH to match cmake_multi

Open memsharded opened this issue 7 years ago • 6 comments

So packages implementing:

def imports(self):
    dest = os.getenv("CONAN_IMPORT_PATH", "bin")
    self.copy("*.dll", dst=dest, src="bin")
    self.copy("*.dylib*", dst=dest, src="lib")

memsharded avatar Apr 04 '17 21:04 memsharded

Hello, has there be any further progress on this? When using conan.cmake I'd like to copy all the debug runtime dlls into the debug directory, and the release dlls into the release build directory (note this isn't at the install step, but more for convenient build and run debugging). The first try at this was to use a conanfile.py but I'd like to have dynamic requirements depending on cmake flags, and I didn't want to touch the existing conan.cmake file in case it changes in the future.

I've written a macro which gets a similar effect to what I was after, but it'd be nice to have a formal function for this. Here's the macro - I don't think it handles the settings correctly, but it seems to work in the basic case. It also doesn't handle the case of putting dylibs/so into the rights spot either.

macro(GenerateDebugImports)
   set ( CONANDEBUGIMPORTS
      "bin, *.dll -> ./DebugImports"
      "lib, *.dylib -> ./DebugImports"
      "lib, *.so -> ./DebugImports"
   )
   conan_cmake_setup_conanfile(REQUIRES ${CONANREQUIREMENTS} IMPORTS ${CONANDEBUGIMPORTS})
   conan_cmake_settings(settings)
   conan_cmake_install(SETTINGS ${settings})
endmacro()
macro(GenerateReleaseImports)
   set ( CONANRELEASEIMPORTS
      "bin, *.dll -> ./ReleaseImports"
      "lib, *.dylib -> ./ReleaseImports"
      "lib, *.so -> ./ReleaseImports"
   )
   conan_cmake_setup_conanfile(REQUIRES ${CONANREQUIREMENTS} IMPORTS ${CONANRELEASEIMPORTS})
   conan_cmake_settings(settings)
   conan_cmake_install(SETTINGS ${settings})
endmacro()

if(CMAKE_BUILD_TYPE STREQUAL "Debug")
   GenerateDebugImports()
elseif(CMAKE_BUILD_TYPE)
   GenerateReleaseImports()
else()
   foreach(CMAKE_BUILD_TYPE "Release" "Debug")
      if(CMAKE_BUILD_TYPE STREQUAL "Debug")
         GenerateDebugImports()
      else()
         GenerateReleaseImports()
      endif()
   endforeach()
   set(CMAKE_BUILD_TYPE)
endif()

< Other Library Setup Code goes here, including the conan call. The conan call has to go after the above, because otherwise we don't create a valid conanbuildinfo_multi.cmake file. >
conan_cmake_run(REQUIRES ${CONANREQUIREMENTS}
                BASIC_SETUP CMAKE_TARGETS NO_OUTPUT_DIRS
                BUILD missing)
<other code>

add_custom_command(
   TARGET TestTarget POST_BUILD
   COMMAND ${CMAKE_COMMAND} -E copy_directory "./$<$<CONFIG:Debug>:Debug>$<$<NOT:$<CONFIG:Debug>>:Release>Imports" "$<TARGET_FILE_DIR:TestTarget>"
   WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)

Also there's a bug with the current implementation if you just try and set CMAKE_BUILD_TYPE with the visual studio compiler as an argument to conan_cmake_run - it will do the right thing in conan_cmake_settings i.e.:

    if(ARGUMENTS_BUILD_TYPE)
        set(_SETTINGS ${_SETTINGS} -s build_type=${ARGUMENTS_BUILD_TYPE})
    elseif(CMAKE_BUILD_TYPE)
        set(_SETTINGS ${_SETTINGS} -s build_type=${CMAKE_BUILD_TYPE})

however in the visual studio runtime detection, it fails here because it doesn't check for the argument version and falls back to the direct cmake variable:

function(conan_cmake_detect_vs_runtime result)
    string(TOUPPER ${CMAKE_BUILD_TYPE} build_type)

Which causes it to use the release runtime (MD) when you're building in debug mode and compilation fails.

L-M-V avatar May 21 '18 11:05 L-M-V

I have added the definition of the CONAN_IMPORT_PATH as environment variable from the conan.cmake in https://github.com/conan-io/cmake-conan/commit/36f25ff678cc32dd95caafe0acde9de34c0f1591.

It is in develop branch (the main branch of https://github.com/conan-io/cmake-conan), so if you could please check from there that it behaves like you want, it would be useful, to be sure before it is released. Thanks!

memsharded avatar May 21 '18 16:05 memsharded

So my particular use case is the following: We have a set of dependencies for our main application as follows:

set ( REQUIREDDEPS
    dep1/master@user/stable
    dep2/master@user/stable
)

However we also have some dependencies which we enable by cmake configuration flags: Something like:

option(USE_TBB "Uses the Intel TBB malloc library for fast memory allocation" ON)

if (USE_TBB)
   set (TBBCONANREQUIRE "TBB/2018_U1@user/stable")
   set (TBBEXTERNAL CONAN_PKG::TBB)
endif (USE_TBB)

We then combine these into a REQUIRES for the conan cmake call:

set ( CONANREQUIREMENTS
   ${REQUIREDDEPS}
   ${TBBCONANREQUIRE}
)

Now the issue is to bring the dlls into the cmake build directory:

set ( CONANIMPORTS
   "bin, *.dll -> ./bin"
   "lib, *.dylib -> ./lib"
   "lib, *.so -> ./lib"
)

conan_cmake_run(REQUIRES ${CONANREQUIREMENTS}
   BASIC_SETUP CMAKE_TARGETS NO_OUTPUT_DIRS
   IMPORTS ${CONANIMPORTS}
   BUILD missing)

Unfortunately in a multiconfiguration generator such as visual studio, IMPORTS ${CONANIMPORTS} copies both the debug and release dlls to the same directory because conan_cmake_install() is called subsequently in debug and release mode.

As conan.cmake creates a conanfile.txt, and not a conanfile.py, I can't do something like:

set (CONANIMPORTS
    if (settings.build_type == "Debug") {
       self.copy("*.dll", src="debug/bin")
    } else {
       self.copy("*.dll", src="release/bin")
    }
)
or:
    dest = os.getenv("CONAN_IMPORT_PATH", "bin")
    self.copy("*.dll", dst=dest, src="bin")
    self.copy("*.dylib*", dst=dest, src="lib")

so unless the .txt format supports the direct substitution of CONAN_IMPORT_PATH (I'm not sure that it does - correct me if I'm wrong but I couldn't find it in the documentation), this will involve creating a premade conanfile.py and passing it in with CONANFILE, however then I lose the ability to dynamically create the REQUIRES depending on the cmake options.

L-M-V avatar May 21 '18 16:05 L-M-V

I see. I'd recommend to provide your own conanfile.py, and call it from the cmake providing the CONANFILE parameter. It will probably be cleaner and easier to extend for other cases that the conanfile.txt might be limited. Is there any reason you can't use a conanfile.py?

memsharded avatar May 21 '18 17:05 memsharded

I'm facing this same issue too. I take it there has been no further progress on this? In my opinion, for a multi-configuration CMake generator (in my case VS) the import instructions need to be translated to an "install" expression for CMake as part of the target definition. The alternative is perhaps that conan performs the import step for every possible configuration (we tend to use exclusively Debug & RelWithDebInfo configurations, but rarely Release). That would be wasteful, but the only way that import can run at CMake configure time?

macdew avatar Apr 19 '19 15:04 macdew

I don't want to import in the build dir on ELF systems, since the RPATHs are correctly set right into .conan dirs. We want it to be done on windows, though.

However, in all cases, I want to launch the imports after cmake's install target. Since the user may have chosen anything as CMAKE_INSTALL_LIBDIR (and BINDIR on windows), it doesn't make sense to have the imports function in conanfile.py use a hardcoded destination dir.

Somehow, I think I don't understand what the imports function goal is, or there's really a missing piece in the puzzle.

I'm balanced between two options:

  • investigating the IMPORTED_LOCATION properties of all libraries, since at some point the generated conanbuildinfo.cmake sets them to the actual abs path of the library. Then, do install(PROGRAMS) calls to put them in CMAKE_INSTALL_LIBDIR (or BINDIR for windows)
  • hacking conanfile.py so imports gets its destination from an env var. I'd call conan imports at the end of cmake_install.cmake, with that env var set to CMAKE_INSTALL_LIBDIR (or BINDIR for windows).

johan-boule avatar Nov 13 '21 18:11 johan-boule