Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support conan 2 #249

Merged
merged 10 commits into from
Feb 25, 2024
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ jobs:
cmake: ${{ matrix.cmake }}
ninja: true
vcpkg: true
conan: true
conan: 2.1.0
cppcheck: true
clangtidy: true
task: true
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ CMake experience following the best practices.
FetchContent, vcpkg, etc.
- `run_vcpkg`: automatic installation of vcpkg and the project
dependencies
- `ENABLE_CONAN` in `project_options`: automatic installation of Conan
and the project dependencies
- `run_conan`: automatic installation of conan and the project
dependencies
- `dynamic_project_options`: a wrapper around `project_options` to
change the options on the fly dynamically
- `target_link_system_libraries` and
Expand Down Expand Up @@ -79,6 +79,8 @@ run_vcpkg(
VCPKG_URL "https://github.com/microsoft/vcpkg.git"
VCPKG_REV "10e052511428d6b0c7fcc63a139e8024bb146032"
)
# Install conan dependencies: - should be called before defining project()
run_conan()

# Set the project name and language
project(myproject LANGUAGES CXX C)
Expand Down Expand Up @@ -119,7 +121,6 @@ project_options(
${ENABLE_CPPCHECK}
${ENABLE_CLANG_TIDY}
ENABLE_VS_ANALYSIS
# ENABLE_CONAN
# ENABLE_INTERPROCEDURAL_OPTIMIZATION
# ENABLE_NATIVE_OPTIMIZATION
${ENABLE_DOXYGEN}
Expand All @@ -142,7 +143,6 @@ project_options(
# ENABLE_BUILD_WITH_TIME_TRACE
# ENABLE_UNITY
# LINKER "lld"
# CONAN_PROFILE ${profile_path}
)
```

Expand Down
2 changes: 1 addition & 1 deletion docs/src/Readme_top.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ A general-purpose CMake library that provides functions that improve the CMake e
- using custom linkers (e.g. lld)
- `package_project`: automatic packaging/installation of the project for seamless usage via find_package/target_link through CMake's FetchContent, vcpkg, etc.
- `run_vcpkg`: automatic installation of vcpkg and the project dependencies
- `ENABLE_CONAN` in `project_options`: automatic installation of Conan and the project dependencies
- `run_conan`: automatic installation of conan and the project dependencies
- `dynamic_project_options`: a wrapper around `project_options` to change the options on the fly dynamically
- `target_link_system_libraries` and `target_include_system_directories`: linking/including external dependencies/headers without warnings
- `target_link_cuda`: linking Cuda to a target
Expand Down
4 changes: 2 additions & 2 deletions docs/src/project_options_example.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ run_vcpkg(
VCPKG_REV "10e052511428d6b0c7fcc63a139e8024bb146032"
ENABLE_VCPKG_UPDATE
)
# Install conan dependencies: - should be called before defining project()
run_conan()

# Set the project name and language
project(myproject LANGUAGES CXX C)
Expand Down Expand Up @@ -73,7 +75,6 @@ project_options(
${ENABLE_CPPCHECK}
${ENABLE_CLANG_TIDY}
ENABLE_VS_ANALYSIS
# ENABLE_CONAN
# ENABLE_INTERPROCEDURAL_OPTIMIZATION
# ENABLE_NATIVE_OPTIMIZATION
${ENABLE_DOXYGEN}
Expand All @@ -96,7 +97,6 @@ project_options(
# ENABLE_BUILD_WITH_TIME_TRACE
# ENABLE_UNITY
# LINKER "lld"
# CONAN_PROFILE ${profile_path}
)
```

Expand Down
230 changes: 206 additions & 24 deletions src/Conan.cmake
Original file line number Diff line number Diff line change
@@ -1,7 +1,45 @@
include_guard()

# Run Conan for dependency management
macro(run_conan)
function(conan_get_version conan_current_version)
find_program(conan_command "conan" REQUIRED)
execute_process(
COMMAND ${conan_command} --version
OUTPUT_VARIABLE conan_output
RESULT_VARIABLE conan_result
OUTPUT_STRIP_TRAILING_WHITESPACE
)

if(conan_result)
message(FATAL_ERROR "Error when trying to run Conan")
endif()

string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" conan_version ${conan_output})
set(${conan_current_version} ${conan_version} PARENT_SCOPE)
endfunction()

# Run Conan 1 for dependency management
macro(_run_conan1)
set(options
DEPRECATED_CALL # For backward compability
)
set(one_value_args
DEPRECATED_PROFILE # For backward compability
)
set(multi_value_args
HOST_PROFILE
BUILD_PROFILE
INSTALL_ARGS
DEPRECATED_OPTIONS # For backward compability
)
cmake_parse_arguments(_args "${options}" "${one_value_args}" "${multi_value_args}" ${ARGN})

conan_get_version(_conan_current_version)
if(_conan_current_version VERSION_GREATER_EQUAL "2.0.0")
message(FATAL_ERROR
"ENABLE_CONAN in `project_options(...)` only supports conan 1.\n"
" If you're using conan 2, disable ENABLE_CONAN and use `run_conan(...)` before `project(...)`.")
endif()

# Download automatically, you can also just copy the conan.cmake file
if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake")
message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan")
Expand Down Expand Up @@ -61,35 +99,59 @@ macro(run_conan)
set(OUTPUT_QUIET)
endif()

set(_should_detect FALSE)
if(((NOT _args_DEPRECATED_CALL) AND ((NOT _args_HOST_PROFILE) OR ("auto-cmake" IN_LIST _args_HOST_PROFILE)))
OR ((_args_DEPRECATED_CALL) AND (NOT _args_DEPRECATED_PROFILE)))
set(_should_detect TRUE)
list(REMOVE_ITEM _args_HOST_PROFILE "auto-cmake")
endif()

if(NOT _args_DEPRECATED_PROFILE)
set(CONAN_DEFAULT_PROFILE "default")
else()
set(CONAN_DEFAULT_PROFILE ${_args_DEPRECATED_PROFILE})
endif()

if(NOT _args_HOST_PROFILE)
set(CONAN_HOST_PROFILE ${CONAN_DEFAULT_PROFILE})
else()
set(CONAN_HOST_PROFILE ${_args_HOST_PROFILE})
endif()

if(NOT _args_BUILD_PROFILE)
set(CONAN_BUILD_PROFILE ${CONAN_DEFAULT_PROFILE})
else()
set(CONAN_BUILD_PROFILE ${_args_BUILD_PROFILE})
endif()

foreach(_install_args IN LISTS _args_INSTALL_ARGS)
string(REGEX MATCH "--build=.*" _possible_build_arg "${_install_args}")

if(_possible_build_arg)
string(SUBSTRING "${_possible_build_arg}" 8 -1 CONAN_BUILD_ARG)
endif()
endforeach()
if(NOT CONAN_BUILD_ARG)
set(CONAN_BUILD_ARG "missing")
set(CONAN_INSTALL_ARGS "")
else()
list(REMOVE_ITEM _args_INSTALL_ARGS "--build=${CONAN_BUILD_ARG}")
set(CONAN_INSTALL_ARGS ${_args_INSTALL_ARGS})
endif()

foreach(TYPE ${LIST_OF_BUILD_TYPES})
message(STATUS "Running Conan for build type '${TYPE}'")

if("${ProjectOptions_CONAN_PROFILE}" STREQUAL "")
if(_should_detect)
# Detects current build settings to pass into conan
conan_cmake_autodetect(settings BUILD_TYPE ${TYPE})
set(CONAN_SETTINGS SETTINGS ${settings})
set(CONAN_ENV ENV "CC=${CMAKE_C_COMPILER}" "CXX=${CMAKE_CXX_COMPILER}")
else()
elseif(_args_DEPRECATED_CALL)
# Derive all conan settings from a conan profile
set(CONAN_SETTINGS PROFILE ${ProjectOptions_CONAN_PROFILE} SETTINGS "build_type=${TYPE}")
# CONAN_ENV should be redundant, since the profile can set CC & CXX
endif()
set(CONAN_SETTINGS PROFILE ${CONAN_DEFAULT_PROFILE} SETTINGS "build_type=${TYPE}")

if("${ProjectOptions_CONAN_PROFILE}" STREQUAL "")
set(CONAN_DEFAULT_PROFILE "default")
else()
set(CONAN_DEFAULT_PROFILE ${ProjectOptions_CONAN_PROFILE})
endif()
if("${ProjectOptions_CONAN_BUILD_PROFILE}" STREQUAL "")
set(CONAN_BUILD_PROFILE ${CONAN_DEFAULT_PROFILE})
else()
set(CONAN_BUILD_PROFILE ${ProjectOptions_CONAN_BUILD_PROFILE})
endif()

if("${ProjectOptions_CONAN_HOST_PROFILE}" STREQUAL "")
set(CONAN_HOST_PROFILE ${CONAN_DEFAULT_PROFILE})
else()
set(CONAN_HOST_PROFILE ${ProjectOptions_CONAN_HOST_PROFILE})
# CONAN_ENV should be redundant, since the profile can set CC & CXX
endif()

# PATH_OR_REFERENCE ${CMAKE_SOURCE_DIR} is used to tell conan to process
Expand All @@ -99,10 +161,10 @@ macro(run_conan)
PATH_OR_REFERENCE
${CMAKE_SOURCE_DIR}
BUILD
missing
${CONAN_BUILD_ARG}
# Pass compile-time configured options into conan
OPTIONS
${ProjectOptions_CONAN_OPTIONS}
${CONAN_INSTALL_ARGS}
# Pass CMake compilers to Conan
${CONAN_ENV}
PROFILE_HOST
Expand All @@ -117,3 +179,123 @@ macro(run_conan)
endif()

endmacro()

# Run Conan 2 for dependency management
macro(_run_conan2)
set(options)
set(one_value_args)
set(multi_value_args
HOST_PROFILE
BUILD_PROFILE
INSTALL_ARGS
)
cmake_parse_arguments(_args "${options}" "${one_value_args}" "${multi_value_args}" ${ARGN})

if(CMAKE_VERSION VERSION_LESS "3.24.0")
message(FATAL_ERROR
"`run_conan(...)` with conan 2 only supports cmake 3.24+, please update your cmake.\n"
" Or you can downgrade your conan to use conan 1.")
endif()

conan_get_version(_conan_current_version)
if(_conan_current_version VERSION_LESS "2.0.5")
message(FATAL_ERROR
"`run_conan(...)` with conan 2 only supports conan 2.0.5+, please update your conan.\n"
" Or You can downgrade your conan to use conan 1.")
endif()

# Download automatically, you can also just copy the conan.cmake file
if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan_provider.cmake")
message(STATUS "Downloading conan_provider.cmake from https://github.com/conan-io/cmake-conan")
file(
DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/f6464d1e13ef7a47c569f5061f9607ea63339d39/conan_provider.cmake"
"${CMAKE_BINARY_DIR}/conan_provider.cmake"
EXPECTED_HASH SHA256=0a5eb4afbdd94faf06dcbf82d3244331605ef2176de32c09ea9376e768cbb0fc

# TLS_VERIFY ON # fails on some systems
)
endif()

if(NOT _args_HOST_PROFILE)
set(_args_HOST_PROFILE "default;auto-cmake")
endif()

if(NOT _args_BUILD_PROFILE)
set(_args_BUILD_PROFILE "default")
endif()

if(NOT _args_INSTALL_ARGS)
set(_args_INSTALL_ARGS "--build=missing")
endif()

set(CONAN_HOST_PROFILE "${_args_HOST_PROFILE}" CACHE STRING "Conan host profile" FORCE)
set(CONAN_BUILD_PROFILE "${_args_BUILD_PROFILE}" CACHE STRING "Conan build profile" FORCE)
set(CONAN_INSTALL_ARGS "${_args_INSTALL_ARGS}" CACHE STRING "Command line arguments for conan install" FORCE)

# A workaround from https://github.com/conan-io/cmake-conan/issues/595
list(APPEND CMAKE_PROJECT_TOP_LEVEL_INCLUDES "${CMAKE_BINARY_DIR}/conan_provider.cmake")

# Add this to invoke conan even when there's no find_package in CMakeLists.txt.
# This helps users get the third-party package names, which is used in later find_package.
cmake_language(DEFER DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" CALL find_package Git QUIET)
endmacro()

#[[.rst:

``run_conan``
=============

Install conan and conan dependencies:

.. code:: cmake

run_conan()

.. code:: cmake

run_conan(
HOST_PROFILE default auto-cmake
BUILD_PROFILE default
INSTALL_ARGS --build=missing
)

Note that it should be called before defining ``project()``.

Named String:

- Values are semicolon separated, e.g. ``"--build=never;--update;--lockfile-out=''"``.
However, you can make use of the cmake behaviour that automatically concatenates
multiple space separated string into a semicolon seperated list, e.g.
``--build=never --update --lockfile-out=''``.

- ``HOST_PROFILE``: (Defaults to ``"default;auto-cmake"``). This option
sets the host profile used by conan. When ``auto-cmake`` is specified,
cmake-conan will invoke conan's autodetection mechanism which tries to
guess the system defaults. If multiple profiles are specified, a
`compound profile <https://docs.conan.io/2.0/reference/commands/install.html#profiles-settings-options-conf>`_
will be used - compounded from left to right, where right has the highest priority.

- ``BUILD_PROFILE``: (Defaults to ``"default"``). This option
sets the build profile used by conan. If multiple profiles are specified,
a `compound profile <https://docs.conan.io/2.0/reference/commands/install.html#profiles-settings-options-conf>`_
will be used - compounded from left to right, where right has the highest priority.

- ``INSTALL_ARGS``: (Defaults to ``"--build=missing"``). This option
customizes ``conan install`` command invocation. Note that ``--build``
must be specified, otherwise conan will revert to its default behaviour.

- Two arguments are reserved to the dependency provider implementation
and must not be set: the path to a ``conanfile.txt|.py``, and the output
format (``--format``).

]]
macro(run_conan)
conan_get_version(_conan_current_version)

if(_conan_current_version VERSION_LESS "2.0.0")
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY PROJECT_OPTIONS_SHOULD_INVOKE_CONAN1 TRUE)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY PROJECT_OPTIONS_CONAN1_ARGS ${ARGN})
else()
_run_conan2(${ARGN})
endif()
endmacro()
5 changes: 2 additions & 3 deletions src/DynamicProjectOptions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,15 @@ Here is an example of how to use ``dynamic_project_options``:

# install vcpkg dependencies: - should be called before defining project()
# run_vcpkg()
# install conan dependencies: - should be called before defining project()
# run_conan()

# Set the project name and language
project(myproject LANGUAGES CXX C)

# Set PCH to be on by default for all non-Developer Mode Builds
set(ENABLE_PCH_USER_DEFAULT ON)

# enable Conan
set(ENABLE_CONAN_DEFAULT ON)

# Initialize project_options variable related to this project
# This overwrites `project_options` and sets `project_warnings`
# This also accepts the same arguments as `project_options`.
Expand Down
Loading