From 3c722646bf161051104f203fc1a620814d056d22 Mon Sep 17 00:00:00 2001 From: Martin Valgur Date: Sun, 30 Jun 2024 17:02:37 +0300 Subject: [PATCH 1/7] Add get_latest_conan_version() --- conan_provider.cmake | 9 +++++++++ .../download/get_latest_version/CMakeLists.txt | 5 +++++ .../download/get_latest_version/conanfile.txt | 2 ++ tests/test_smoke.py | 10 ++++++++++ 4 files changed, 26 insertions(+) create mode 100644 tests/resources/download/get_latest_version/CMakeLists.txt create mode 100644 tests/resources/download/get_latest_version/conanfile.txt diff --git a/conan_provider.cmake b/conan_provider.cmake index 9931247c..d160421b 100644 --- a/conan_provider.cmake +++ b/conan_provider.cmake @@ -535,6 +535,15 @@ function(conan_version_check) endfunction() +function(get_latest_conan_version VERSION_VARIABLE) + set(json_file "${CMAKE_BINARY_DIR}/conan_latest_release.json") + file(DOWNLOAD "https://api.github.com/repos/conan-io/conan/releases/latest" "${json_file}") + file(READ "${json_file}" json) + string(REGEX MATCH "\"tag_name\": \"([^\"]+)\"" _ "${json}") + set(${VERSION_VARIABLE} "${CMAKE_MATCH_1}" PARENT_SCOPE) +endfunction() + + macro(construct_profile_argument argument_variable profile_list) set(${argument_variable} "") if("${profile_list}" STREQUAL "CONAN_HOST_PROFILE") diff --git a/tests/resources/download/get_latest_version/CMakeLists.txt b/tests/resources/download/get_latest_version/CMakeLists.txt new file mode 100644 index 00000000..1a024f8b --- /dev/null +++ b/tests/resources/download/get_latest_version/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.24) +project(MyApp CXX) + +get_latest_conan_version(LATEST_CONAN_VERSION) +message(STATUS "Latest Conan version: ${LATEST_CONAN_VERSION}") diff --git a/tests/resources/download/get_latest_version/conanfile.txt b/tests/resources/download/get_latest_version/conanfile.txt new file mode 100644 index 00000000..674c4559 --- /dev/null +++ b/tests/resources/download/get_latest_version/conanfile.txt @@ -0,0 +1,2 @@ +[requires] +hello/0.1 \ No newline at end of file diff --git a/tests/test_smoke.py b/tests/test_smoke.py index 91553d72..1b5e0b6d 100644 --- a/tests/test_smoke.py +++ b/tests/test_smoke.py @@ -755,3 +755,13 @@ def test_try_compile(self, capfd, basic_cmake_project): run(f'cmake -S {source_dir} -B {binary_dir} -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES={conan_provider}') out, _ = capfd.readouterr() assert 'Performing Test HELLO_WORLD_CAN_COMPILE - Success' in out + + +class TestDownload: + def test_get_latest_version(self, capfd, basic_cmake_project): + source_dir, binary_dir = basic_cmake_project + shutil.copytree(src_dir / 'tests' / 'resources' / 'download' / 'get_latest_version', source_dir, dirs_exist_ok=True) + run(f'cmake -S {source_dir} -B {binary_dir} -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES={conan_provider}') + out, _ = capfd.readouterr() + assert re.search(r'Latest Conan version: \d+\.\d+\.\d+', out) + From 9c9539475d5c6969e1496a50a2c938b6b0b1e96d Mon Sep 17 00:00:00 2001 From: Martin Valgur Date: Sun, 30 Jun 2024 17:25:48 +0300 Subject: [PATCH 2/7] Add download_conan() --- conan_provider.cmake | 53 +++++++++++++++++++ .../download/download_function/CMakeLists.txt | 6 +++ .../download/download_function/conanfile.txt | 2 + tests/test_smoke.py | 8 +++ 4 files changed, 69 insertions(+) create mode 100644 tests/resources/download/download_function/CMakeLists.txt create mode 100644 tests/resources/download/download_function/conanfile.txt diff --git a/conan_provider.cmake b/conan_provider.cmake index d160421b..56bbeede 100644 --- a/conan_provider.cmake +++ b/conan_provider.cmake @@ -544,6 +544,59 @@ function(get_latest_conan_version VERSION_VARIABLE) endfunction() +function(download_conan) + set(options "") + set(oneValueArgs VERSION DESTINATION) + set(multiValueArgs "") + include(CMakeParseArguments) + cmake_parse_arguments(PARSE_ARGV 0 ARG "${options}" "${oneValueArgs}" "${multiValueArgs}") + + if(NOT ARG_VERSION) + message(FATAL_ERROR "CMake-Conan: Required parameter VERSION not set!") + endif() + if(NOT ARG_DESTINATION) + message(FATAL_ERROR "CMake-Conan: Required parameter DESTINATION not set!") + endif() + + if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "AMD64|amd64|x86_64|x64") + set(HOST_ARCH "x86_64") + elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "X86|i686|i386") + set(HOST_ARCH "i686") + elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|ARM64") + set(HOST_ARCH "arm64") + else() + message(FATAL_ERROR "CMake-Conan: Pre-packaged Conan is not available for ${CMAKE_HOST_SYSTEM_PROCESSOR}") + endif() + + set(FILE_EXT "tgz") + if(WIN32 AND HOST_ARCH MATCHES "x86_64|i686") + set(HOST_OS "windows") + set(FILE_EXT "zip") + elseif(APPLE AND HOST_ARCH MATCHES "x86_64|arm64") + set(HOST_OS "macos") + elseif(LINUX AND HOST_ARCH STREQUAL "x86_64") + set(HOST_OS "linux") + else() + message(FATAL_ERROR "CMake-Conan: Pre-packaged Conan is not available for ${CMAKE_SYSTEM_NAME} ${CMAKE_HOST_SYSTEM_PROCESSOR}") + endif() + + set(CONAN_VERSION ${ARG_VERSION}) + set(CONAN_FILE "conan-${CONAN_VERSION}-${HOST_OS}-${HOST_ARCH}.${FILE_EXT}") + set(CONAN_URL "https://github.com/conan-io/conan/releases/download/${CONAN_VERSION}/${CONAN_FILE}") + + message(STATUS "Downloading Conan ${CONAN_VERSION} from ${CONAN_URL}") + include(FetchContent) + FetchContent_Declare( + Conan + URL "${CONAN_URL}" + DOWNLOAD_DIR ${CMAKE_BINARY_DIR} + SOURCE_DIR "${ARG_DESTINATION}" + DOWNLOAD_EXTRACT_TIMESTAMP 1 + ) + FetchContent_MakeAvailable(Conan) +endfunction() + + macro(construct_profile_argument argument_variable profile_list) set(${argument_variable} "") if("${profile_list}" STREQUAL "CONAN_HOST_PROFILE") diff --git a/tests/resources/download/download_function/CMakeLists.txt b/tests/resources/download/download_function/CMakeLists.txt new file mode 100644 index 00000000..21b940c1 --- /dev/null +++ b/tests/resources/download/download_function/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.24) +project(MyApp CXX) + +get_latest_conan_version(LATEST_CONAN_VERSION) +message(STATUS "Latest Conan version: ${LATEST_CONAN_VERSION}") +download_conan(VERSION ${LATEST_CONAN_VERSION} DESTINATION ${CMAKE_BINARY_DIR}/conan) diff --git a/tests/resources/download/download_function/conanfile.txt b/tests/resources/download/download_function/conanfile.txt new file mode 100644 index 00000000..674c4559 --- /dev/null +++ b/tests/resources/download/download_function/conanfile.txt @@ -0,0 +1,2 @@ +[requires] +hello/0.1 \ No newline at end of file diff --git a/tests/test_smoke.py b/tests/test_smoke.py index 1b5e0b6d..c1d8c6fa 100644 --- a/tests/test_smoke.py +++ b/tests/test_smoke.py @@ -765,3 +765,11 @@ def test_get_latest_version(self, capfd, basic_cmake_project): out, _ = capfd.readouterr() assert re.search(r'Latest Conan version: \d+\.\d+\.\d+', out) + def test_download_function(self, capfd, basic_cmake_project): + source_dir, binary_dir = basic_cmake_project + shutil.copytree(src_dir / 'tests' / 'resources' / 'download' / 'download_function', source_dir, dirs_exist_ok=True) + run(f'cmake -S {source_dir} -B {binary_dir} -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES={conan_provider}') + out, _ = capfd.readouterr() + assert 'Downloading Conan ' in out + assert (os.path.exists(os.path.join(binary_dir, 'conan', 'conan')) or + os.path.exists(os.path.join(binary_dir, 'conan', 'conan.exe'))) \ No newline at end of file From 2ab87944fb61206b1c4c90b2cf05f19d83622544 Mon Sep 17 00:00:00 2001 From: Martin Valgur Date: Sun, 30 Jun 2024 18:38:08 +0300 Subject: [PATCH 3/7] Nicer conan_get_version() error message --- conan_provider.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conan_provider.cmake b/conan_provider.cmake index 56bbeede..632096fa 100644 --- a/conan_provider.cmake +++ b/conan_provider.cmake @@ -507,7 +507,7 @@ function(conan_get_version conan_command conan_current_version) OUTPUT_STRIP_TRAILING_WHITESPACE ) if(conan_result) - message(FATAL_ERROR "CMake-Conan: Error when trying to run Conan") + message(FATAL_ERROR "CMake-Conan: Error when trying to run '${conan_command} --version'") endif() string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" conan_version ${conan_output}) From 12ca608e3de2bc52742d8e4f0550b01ee107794f Mon Sep 17 00:00:00 2001 From: Martin Valgur Date: Sun, 30 Jun 2024 20:10:04 +0300 Subject: [PATCH 4/7] Add CONAN_DOWNLOAD and CONAN_ISOLATE_HOME options --- conan_provider.cmake | 68 ++++++++++++---- .../download/auto_download/CMakeLists.txt | 6 ++ .../download/auto_download/conanfile.txt | 5 ++ .../download/download_function/conanfile.txt | 1 - .../download/get_latest_version/conanfile.txt | 1 - tests/test_smoke.py | 77 ++++++++++++++++++- 6 files changed, 142 insertions(+), 16 deletions(-) create mode 100644 tests/resources/download/auto_download/CMakeLists.txt create mode 100644 tests/resources/download/auto_download/conanfile.txt diff --git a/conan_provider.cmake b/conan_provider.cmake index 632096fa..d0065561 100644 --- a/conan_provider.cmake +++ b/conan_provider.cmake @@ -20,7 +20,14 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -set(CONAN_MINIMUM_VERSION 2.0.5) +# Configurable variables +set(CONAN_MINIMUM_VERSION "2.0.5" CACHE STRING "Minimum required Conan version") +set(CONAN_HOST_PROFILE "default;auto-cmake" CACHE STRING "Conan host profile") +set(CONAN_BUILD_PROFILE "default" CACHE STRING "Conan build profile") +set(CONAN_INSTALL_ARGS "--build=missing" CACHE STRING "Command line arguments for conan install") +set(CONAN_DOWNLOAD "if-missing" CACHE STRING "Download the Conan client (always, if-missing or never)") +set(CONAN_DOWNLOAD_VERSION "latest" CACHE STRING "Download a specific Conan version") +set(CONAN_ISOLATE_HOME "if-downloaded" CACHE STRING "Set $CONAN_HOME to \${CMAKE_BINARY_DIR}/conan_home (always, if-downloaded or never)") # Create a new policy scope and set the minimum required cmake version so the # features behind a policy setting like if(... IN_LIST ...) behaves as expected @@ -456,7 +463,7 @@ function(conan_install) set(CONAN_OUTPUT_FOLDER ${CMAKE_BINARY_DIR}/conan) # Invoke "conan install" with the provided arguments set(CONAN_ARGS ${CONAN_ARGS} -of=${CONAN_OUTPUT_FOLDER}) - message(STATUS "CMake-Conan: conan install ${CMAKE_SOURCE_DIR} ${CONAN_ARGS} ${ARGN}") + message(STATUS "CMake-Conan: ${CONAN_COMMAND} install ${CMAKE_SOURCE_DIR} ${CONAN_ARGS} ${ARGN}") # In case there was not a valid cmake executable in the PATH, we inject the @@ -517,7 +524,7 @@ endfunction() function(conan_version_check) set(options ) - set(oneValueArgs MINIMUM CURRENT) + set(oneValueArgs MINIMUM CURRENT RESULT) set(multiValueArgs ) cmake_parse_arguments(CONAN_VERSION_CHECK "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) @@ -530,7 +537,19 @@ function(conan_version_check) endif() if(CONAN_VERSION_CHECK_CURRENT VERSION_LESS CONAN_VERSION_CHECK_MINIMUM) - message(FATAL_ERROR "CMake-Conan: Conan version must be ${CONAN_VERSION_CHECK_MINIMUM} or later") + if(CONAN_VERSION_CHECK_RESULT) + set(${CONAN_VERSION_CHECK_RESULT} FALSE PARENT_SCOPE) + endif() + if(CONAN_DOWNLOAD STREQUAL "if-missing") + message(STATUS "CMake-Conan: Found Conan but its version (${CONAN_VERSION_CHECK_CURRENT}) is older than " + "required (${CONAN_VERSION_CHECK_MINIMUM}). Will download the latest version instead.") + else() + message(FATAL_ERROR "CMake-Conan: Conan version must be ${CONAN_VERSION_CHECK_MINIMUM} or later") + endif() + else() + if(CONAN_VERSION_CHECK_RESULT) + set(${CONAN_VERSION_CHECK_RESULT} TRUE PARENT_SCOPE) + endif() endif() endfunction() @@ -584,7 +603,7 @@ function(download_conan) set(CONAN_FILE "conan-${CONAN_VERSION}-${HOST_OS}-${HOST_ARCH}.${FILE_EXT}") set(CONAN_URL "https://github.com/conan-io/conan/releases/download/${CONAN_VERSION}/${CONAN_FILE}") - message(STATUS "Downloading Conan ${CONAN_VERSION} from ${CONAN_URL}") + message(STATUS "CMake-Conan: Downloading Conan ${CONAN_VERSION} from ${CONAN_URL}") include(FetchContent) FetchContent_Declare( Conan @@ -619,9 +638,37 @@ macro(conan_provide_dependency method package_name) set_property(GLOBAL PROPERTY CONAN_PROVIDE_DEPENDENCY_INVOKED TRUE) get_property(_conan_install_success GLOBAL PROPERTY CONAN_INSTALL_SUCCESS) if(NOT _conan_install_success) - find_program(CONAN_COMMAND "conan" REQUIRED) - conan_get_version(${CONAN_COMMAND} CONAN_CURRENT_VERSION) - conan_version_check(MINIMUM ${CONAN_MINIMUM_VERSION} CURRENT ${CONAN_CURRENT_VERSION}) + if(NOT CONAN_DOWNLOAD STREQUAL "always") + find_program(CONAN_COMMAND "conan" QUIET) + if(CONAN_COMMAND) + conan_get_version(${CONAN_COMMAND} CONAN_CURRENT_VERSION) + conan_version_check(MINIMUM ${CONAN_MINIMUM_VERSION} CURRENT ${CONAN_CURRENT_VERSION} + RESULT _conan_version_check_result) + if(NOT _conan_version_check_result) + set(CONAN_COMMAND "-NOTFOUND") + endif() + endif() + endif() + if(NOT CONAN_COMMAND) + if(CONAN_DOWNLOAD STREQUAL "never") + message(FATAL_ERROR "CMake-Conan: Conan executable not found. " + "Please install Conan, set CONAN_COMMAND or enable CONAN_DOWNLOAD") + endif() + if(CONAN_DOWNLOAD_VERSION STREQUAL "latest") + get_latest_conan_version(_download_version) + else() + set(_download_version ${CONAN_DOWNLOAD_VERSION}) + endif() + download_conan(VERSION ${_download_version} DESTINATION "${CMAKE_BINARY_DIR}/conan_client") + set(CONAN_COMMAND "${CMAKE_BINARY_DIR}/conan_client/conan") + set(_conan_downloaded TRUE) + endif() + + if(CONAN_ISOLATE_HOME STREQUAL "always" OR (CONAN_ISOLATE_HOME STREQUAL "if-downloaded" AND _conan_downloaded)) + message(STATUS "CMake-Conan: Setting CONAN_HOME to '${CMAKE_BINARY_DIR}/conan_home'") + set(ENV{CONAN_HOME} "${CMAKE_BINARY_DIR}/conan_home") + endif() + message(STATUS "CMake-Conan: first find_package() found. Installing dependencies with Conan") if("default" IN_LIST CONAN_HOST_PROFILE OR "default" IN_LIST CONAN_BUILD_PROFILE) conan_profile_detect_default() @@ -722,11 +769,6 @@ endmacro() # to check if the dependency provider was invoked at all. cmake_language(DEFER DIRECTORY "${CMAKE_SOURCE_DIR}" CALL conan_provide_dependency_check) -# Configurable variables for Conan profiles -set(CONAN_HOST_PROFILE "default;auto-cmake" CACHE STRING "Conan host profile") -set(CONAN_BUILD_PROFILE "default" CACHE STRING "Conan build profile") -set(CONAN_INSTALL_ARGS "--build=missing" CACHE STRING "Command line arguments for conan install") - find_program(_cmake_program NAMES cmake NO_PACKAGE_ROOT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH NO_CMAKE_FIND_ROOT_PATH) if(NOT _cmake_program) get_filename_component(PATH_TO_CMAKE_BIN "${CMAKE_COMMAND}" DIRECTORY) diff --git a/tests/resources/download/auto_download/CMakeLists.txt b/tests/resources/download/auto_download/CMakeLists.txt new file mode 100644 index 00000000..0e88d63b --- /dev/null +++ b/tests/resources/download/auto_download/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.24) +project(MyApp CXX) + +find_package(hello REQUIRED) +add_executable(app main.cpp) +target_link_libraries(app hello::hello) diff --git a/tests/resources/download/auto_download/conanfile.txt b/tests/resources/download/auto_download/conanfile.txt new file mode 100644 index 00000000..bc7c6208 --- /dev/null +++ b/tests/resources/download/auto_download/conanfile.txt @@ -0,0 +1,5 @@ +[requires] +hello/0.1 + +[generators] +CMakeDeps diff --git a/tests/resources/download/download_function/conanfile.txt b/tests/resources/download/download_function/conanfile.txt index 674c4559..ce01d1ef 100644 --- a/tests/resources/download/download_function/conanfile.txt +++ b/tests/resources/download/download_function/conanfile.txt @@ -1,2 +1 @@ [requires] -hello/0.1 \ No newline at end of file diff --git a/tests/resources/download/get_latest_version/conanfile.txt b/tests/resources/download/get_latest_version/conanfile.txt index 674c4559..ce01d1ef 100644 --- a/tests/resources/download/get_latest_version/conanfile.txt +++ b/tests/resources/download/get_latest_version/conanfile.txt @@ -1,2 +1 @@ [requires] -hello/0.1 \ No newline at end of file diff --git a/tests/test_smoke.py b/tests/test_smoke.py index c1d8c6fa..b25d9af4 100644 --- a/tests/test_smoke.py +++ b/tests/test_smoke.py @@ -772,4 +772,79 @@ def test_download_function(self, capfd, basic_cmake_project): out, _ = capfd.readouterr() assert 'Downloading Conan ' in out assert (os.path.exists(os.path.join(binary_dir, 'conan', 'conan')) or - os.path.exists(os.path.join(binary_dir, 'conan', 'conan.exe'))) \ No newline at end of file + os.path.exists(os.path.join(binary_dir, 'conan', 'conan.exe'))) + + @staticmethod + def _prepare_isolated_home(binary_dir): + # Copy everything except settings.yml from the real CONAN_HOME to the expected isolated CONAN_HOME. + # The settings.yml should be re-created after CMake configure. + conan_home = os.getenv("CONAN_HOME") + isolated_home = os.path.join(binary_dir, "conan_home") + shutil.copytree(conan_home, isolated_home) + isolated_settings_yml = os.path.join(isolated_home, "settings.yml") + os.unlink(isolated_settings_yml) + return isolated_settings_yml + + @pytest.mark.parametrize("isolate", ["always", "never"]) + def test_isolate_home(self, capfd, basic_cmake_project, isolate): + "Test that CONAN_ISOLATE_HOME=always/never results in {build_dir}/conan_home being used / not used" + source_dir, binary_dir = basic_cmake_project + isolated_settings_yml = self._prepare_isolated_home(binary_dir) + assert not os.path.exists(isolated_settings_yml) + shutil.copytree(src_dir / 'tests' / 'resources' / 'download' / 'auto_download', source_dir, dirs_exist_ok=True) + run(f'cmake -S {source_dir} -B {binary_dir} -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES={conan_provider} -DCMAKE_BUILD_TYPE=Release' + f' -DCONAN_ISOLATE_HOME={isolate}') + out, _ = capfd.readouterr() + if isolate == "always": + assert 'Setting CONAN_HOME to ' in out + assert os.path.exists(isolated_settings_yml) + else: + assert 'Setting CONAN_HOME to ' not in out + assert not os.path.exists(isolated_settings_yml) + + @pytest.mark.parametrize("conan_missing", [True, False]) + def test_download_never(self, capfd, basic_cmake_project, conan_missing): + source_dir, binary_dir = basic_cmake_project + shutil.copytree(src_dir / 'tests' / 'resources' / 'download' / 'auto_download', source_dir, dirs_exist_ok=True) + run(f'cmake -S {source_dir} -B {binary_dir} -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES={conan_provider} -DCMAKE_BUILD_TYPE=Release' + ' -DCONAN_DOWNLOAD=never' + (' -DCONAN_MINIMUM_VERSION=100.0.0' if conan_missing else ''), check=False) + out, err = capfd.readouterr() + assert 'Downloading Conan ' not in out + if conan_missing: + assert 'CMake-Conan: Conan version must be 100.0.0 or later' in err + else: + assert 'CMake-Conan: Conan version must be 100.0.0 or later' not in err + + def test_download_always(self, capfd, basic_cmake_project): + source_dir, binary_dir = basic_cmake_project + isolated_settings_yml = self._prepare_isolated_home(binary_dir) + assert not os.path.exists(isolated_settings_yml) + shutil.copytree(src_dir / 'tests' / 'resources' / 'download' / 'auto_download', source_dir, dirs_exist_ok=True) + run(f'cmake -S {source_dir} -B {binary_dir} -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES={conan_provider} -DCMAKE_BUILD_TYPE=Release' + ' -DCONAN_DOWNLOAD=always -DCONAN_ISOLATE_HOME=if-downloaded -DCONAN_MINIMUM_VERSION=100.0.0', check=False) + out, _ = capfd.readouterr() + assert 'Downloading Conan ' in out + assert 'Setting CONAN_HOME to ' in out + assert os.path.exists(isolated_settings_yml) + assert (os.path.exists(os.path.join(binary_dir, 'conan_client', 'conan')) or + os.path.exists(os.path.join(binary_dir, 'conan_client', 'conan.exe'))) + + @pytest.mark.parametrize("conan_missing", [True, False]) + def test_download_if_missing(self, capfd, basic_cmake_project, conan_missing): + source_dir, binary_dir = basic_cmake_project + print(binary_dir) + shutil.copytree(src_dir / 'tests' / 'resources' / 'download' / 'auto_download', source_dir, dirs_exist_ok=True) + run(f'cmake -S {source_dir} -B {binary_dir} -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES={conan_provider} -DCMAKE_BUILD_TYPE=Release' + ' -DCONAN_DOWNLOAD=if-missing -DCONAN_ISOLATE_HOME=never' + (' -DCONAN_MINIMUM_VERSION=100.0.0' if conan_missing else '')) + out, err = capfd.readouterr() + assert 'CMake-Conan: Conan version must be 100.0.0 or later' not in err + assert not os.path.exists(os.path.join(binary_dir, 'conan_home')) + if conan_missing: + assert 'Will download the latest version instead.' in out + assert 'Downloading Conan ' in out + assert re.search(r"conan_client/conan(\.exe)? install ", out) + assert (os.path.exists(os.path.join(binary_dir, 'conan_client', 'conan')) or + os.path.exists(os.path.join(binary_dir, 'conan_client', 'conan.exe'))) + else: + assert 'Will download the latest version instead.' not in out + assert 'Downloading Conan ' not in out From 4119e541dd9e0c287cd35a5739dc346874a40b30 Mon Sep 17 00:00:00 2001 From: Martin Valgur Date: Sun, 30 Jun 2024 21:08:31 +0300 Subject: [PATCH 5/7] Improve error handling in get_latest_conan_version() --- conan_provider.cmake | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/conan_provider.cmake b/conan_provider.cmake index d0065561..79f16e16 100644 --- a/conan_provider.cmake +++ b/conan_provider.cmake @@ -556,9 +556,20 @@ endfunction() function(get_latest_conan_version VERSION_VARIABLE) set(json_file "${CMAKE_BINARY_DIR}/conan_latest_release.json") - file(DOWNLOAD "https://api.github.com/repos/conan-io/conan/releases/latest" "${json_file}") - file(READ "${json_file}" json) + file(DOWNLOAD "https://api.github.com/repos/conan-io/conan/releases/latest" + "${json_file}" + INACTIVITY_TIMEOUT 5 + STATUS status) + list(GET status 0 status_code) + if(NOT status_code EQUAL 0) + list(GET status 1 message) + message(FATAL_ERROR "CMake-Conan: Failed to get the latest Conan version info: ${message} (${status_code})") + endif() + file(READ "${json_file}" json ENCODING UTF-8) string(REGEX MATCH "\"tag_name\": \"([^\"]+)\"" _ "${json}") + if(NOT _) + message(FATAL_ERROR "CMake-Conan: Failed to parse the latest Conan version info from: '${json_file}'") + endif() set(${VERSION_VARIABLE} "${CMAKE_MATCH_1}" PARENT_SCOPE) endfunction() From 6e6a0708364a3c2dd105d55ad3d66aac43331961 Mon Sep 17 00:00:00 2001 From: Martin Valgur Date: Tue, 16 Jul 2024 12:43:57 +0300 Subject: [PATCH 6/7] Fix indentation --- conan_provider.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conan_provider.cmake b/conan_provider.cmake index 79f16e16..202f629d 100644 --- a/conan_provider.cmake +++ b/conan_provider.cmake @@ -532,7 +532,7 @@ function(conan_version_check) if(NOT CONAN_VERSION_CHECK_MINIMUM) message(FATAL_ERROR "CMake-Conan: Required parameter MINIMUM not set!") endif() - if(NOT CONAN_VERSION_CHECK_CURRENT) + if(NOT CONAN_VERSION_CHECK_CURRENT) message(FATAL_ERROR "CMake-Conan: Required parameter CURRENT not set!") endif() From 7672df47f779f7be3e46ecc2aab62d993d42699d Mon Sep 17 00:00:00 2001 From: Martin Valgur Date: Tue, 16 Jul 2024 12:42:59 +0300 Subject: [PATCH 7/7] get_latest_conan_version(): retry up to 2 times GitHub fails to respond surprisingly often. --- conan_provider.cmake | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/conan_provider.cmake b/conan_provider.cmake index 202f629d..31426f05 100644 --- a/conan_provider.cmake +++ b/conan_provider.cmake @@ -556,15 +556,19 @@ endfunction() function(get_latest_conan_version VERSION_VARIABLE) set(json_file "${CMAKE_BINARY_DIR}/conan_latest_release.json") - file(DOWNLOAD "https://api.github.com/repos/conan-io/conan/releases/latest" - "${json_file}" - INACTIVITY_TIMEOUT 5 - STATUS status) - list(GET status 0 status_code) - if(NOT status_code EQUAL 0) - list(GET status 1 message) - message(FATAL_ERROR "CMake-Conan: Failed to get the latest Conan version info: ${message} (${status_code})") - endif() + foreach(_retry_counter RANGE 3) + file(DOWNLOAD "https://api.github.com/repos/conan-io/conan/releases/latest" + "${json_file}" + INACTIVITY_TIMEOUT 15 + STATUS status) + list(GET status 0 status_code) + if(NOT status_code EQUAL 0) + list(GET status 1 message) + message(WARNING "CMake-Conan: Failed to get the latest Conan version info: ${message} (${status_code})") + continue() + endif() + break() + endforeach() file(READ "${json_file}" json ENCODING UTF-8) string(REGEX MATCH "\"tag_name\": \"([^\"]+)\"" _ "${json}") if(NOT _)