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

catch2 (2.x.x): support conan v2 #13794

Merged
merged 19 commits into from
Dec 2, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 54 additions & 54 deletions recipes/catch2/2.x.x/conanfile.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from conans import ConanFile, CMake, tools
from conans.errors import ConanInvalidConfiguration
import functools
from conan import ConanFile
from conan.errors import ConanInvalidConfiguration
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
from conan.tools.files import copy, get, rmdir, replace_in_file
from conan.tools.scm import Version
import os

required_conan_version = ">=1.43.0"
required_conan_version = ">=1.52.0"
paulharris marked this conversation as resolved.
Show resolved Hide resolved


class Catch2Conan(ConanFile):
Expand All @@ -20,7 +22,7 @@ class Catch2Conan(ConanFile):
"with_main": [True, False],
"with_benchmark": [True, False],
"with_prefix": [True, False],
"default_reporter": "ANY",
"default_reporter": [None, "ANY"],
}
default_options = {
"fPIC": True,
Expand All @@ -30,17 +32,6 @@ class Catch2Conan(ConanFile):
"default_reporter": None,
}

exports_sources = "CMakeLists.txt"
generators = "cmake"

@property
def _source_subfolder(self):
return "source_subfolder"

@property
def _build_subfolder(self):
return "build_subfolder"

@property
def _default_reporter_str(self):
return '"{}"'.format(str(self.options.default_reporter).strip('"'))
Expand All @@ -51,79 +42,80 @@ def config_options(self):

def configure(self):
if not self.options.with_main:
del self.options.fPIC
del self.options.with_benchmark
try:
del self.options.fPIC
except Exception:
pass
try:
del self.options.with_benchmark
except Exception:
pass

def package_id(self):
if not self.options.with_main:
self.info.header_only()
paulharris marked this conversation as resolved.
Show resolved Hide resolved

def layout(self):
cmake_layout(self, src_folder="src")

def validate(self):
if tools.Version(self.version) < "2.13.1" and self.settings.arch == "armv8":
if Version(self.version) < "2.13.1" and self.settings.arch == "armv8":
raise ConanInvalidConfiguration("ARMv8 is supported by 2.13.1+ only! give up!")
paulharris marked this conversation as resolved.
Show resolved Hide resolved
if self.options.with_main and tools.Version(self.version) < "2.13.4":
if self.options.with_main and Version(self.version) < "2.13.4":
raise ConanInvalidConfiguration("Option with_main not supported with versions < 2.13.4")

def source(self):
tools.get(**self.conan_data["sources"][self.version], strip_root=True, destination=self._source_subfolder)

@functools.lru_cache(1)
def _configure_cmake(self):
cmake = CMake(self)
cmake.definitions["BUILD_TESTING"] = "OFF"
cmake.definitions["CATCH_INSTALL_DOCS"] = "OFF"
cmake.definitions["CATCH_INSTALL_HELPERS"] = "ON"
cmake.definitions["CATCH_BUILD_STATIC_LIBRARY"] = self.options.with_main
cmake.definitions["enable_benchmark"] = self.options.get_safe("with_benchmark", False)
prince-chrismc marked this conversation as resolved.
Show resolved Hide resolved
cmake.definitions["CATCH_CONFIG_PREFIX_ALL"] = self.options.with_prefix
get(self, **self.conan_data["sources"][self.version], destination=self.source_folder, strip_root=True)

def generate(self):
tc = CMakeToolchain(self)
tc.variables["BUILD_TESTING"] = False
tc.cache_variables["CATCH_INSTALL_DOCS"] = False
tc.cache_variables["CATCH_INSTALL_HELPERS"] = "ON"
tc.cache_variables["CATCH_BUILD_STATIC_LIBRARY"] = self.options.with_main # FIXME cache_variables or just variables?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case of doubt: conan-io/conan#11937 (comment)

Plus:

There are two main cases to use cache_variables:

  • When a definition is configured before project()
  • When an option uses CACHE. Need to check project's CMakeLists.txt

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs currently focus on the difference that CACHE is for "single config" compare to "multiple config",
and says for the majority of cases cache_variables is what you probably want to use ... so this seems to be a complex topic...
I see almost all recipes use variables, but it seems like if you really want the variable to be set, you should use cache_variables and there is less room for surprises?

Copy link
Contributor

@jwillikers jwillikers Oct 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In CMake, cache variables are how CMake and projects expose options to be set by consumers. Cache variables are meant to be able to be preemptible by consumers. If a consumer sets it before a project does, the project won't change the value. I think you're right, @paulharris, that we should prefer setting cache variables instead when possible, as this is how projects should expose the options we are often setting.

There are of course, tons of CMake projects that don't respect cache variables and set normal variables with the same name which causes all sorts of problems.... ugh. And the issue with setting most variables before the first project command is quite problematic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully improvements for this are coming with a future CMake?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#13971 not the best but it's a start

tc.cache_variables["enable_benchmark"] = self.options.get_safe("with_benchmark", False) # FIXME cache_variables or just variables?
tc.variables["CATCH_CONFIG_PREFIX_ALL"] = self.options.with_prefix
if self.options.default_reporter:
cmake.definitions["CATCH_CONFIG_DEFAULT_REPORTER"] = self._default_reporter_str

cmake.configure(build_folder=self._build_subfolder)
return cmake
tc.variables["CATCH_CONFIG_DEFAULT_REPORTER"] = self._default_reporter_str
tc.generate()

def build(self):
# Catch2 does skip install if included as subproject:
# https://github.com/catchorg/Catch2/blob/79a5cd795c387e2da58c13e9dcbfd9ea7a2cfb30/CMakeLists.txt#L100-L102
paulharris marked this conversation as resolved.
Show resolved Hide resolved
main_cml = os.path.join(self._source_subfolder, "CMakeLists.txt")
tools.replace_in_file(main_cml, "if (NOT_SUBPROJECT)", "if (TRUE)")
main_cml = os.path.join(self.source_folder, "CMakeLists.txt")
replace_in_file(self, main_cml, "if (NOT_SUBPROJECT)", "if (TRUE)")
paulharris marked this conversation as resolved.
Show resolved Hide resolved
cmake = CMake(self)
cmake.configure()
if self.options.with_main:
cmake = self._configure_cmake()
cmake.build()

def package(self):
self.copy(pattern="LICENSE.txt", dst="licenses", src=self._source_subfolder)
cmake = self._configure_cmake()
copy(self, pattern="LICENSE.txt", dst=os.path.join(self.package_folder, "licenses"), src=self.source_folder)
cmake = CMake(self)
cmake.install()
tools.rmdir(os.path.join(self.package_folder, "lib", "cmake"))
tools.rmdir(os.path.join(self.package_folder, "share"))
rmdir(self, os.path.join(self.package_folder, "lib", "cmake"))
rmdir(self, os.path.join(self.package_folder, "share"))
for cmake_file in ["ParseAndAddCatchTests.cmake", "Catch.cmake", "CatchAddTests.cmake"]:
self.copy(
copy(self,
cmake_file,
src=os.path.join(self._source_subfolder, "contrib"),
dst=os.path.join("lib", "cmake", "Catch2"),
src=os.path.join(self.source_folder, "contrib"),
dst=os.path.join(self.package_folder, "lib", "cmake", "Catch2"),
)

def package_info(self):
self.cpp_info.set_property("cmake_file_name", "Catch2")
self.cpp_info.set_property("cmake_target_name", "Catch2::Catch2{}".format("WithMain" if self.options.with_main else ""))
self.cpp_info.set_property("pkg_config_name", "catch2".format("-with-main" if self.options.with_main else ""))
self.cpp_info.names["cmake_find_package"] = "Catch2"
self.cpp_info.names["cmake_find_package_multi"] = "Catch2"
self.cpp_info.set_property("pkg_config_name", "catch2{}".format("-with-main" if self.options.with_main else ""))

if self.options.with_main:
self.cpp_info.components["_catch2"].set_property("cmake_target_name", "Catch2::Catch2")
self.cpp_info.components["_catch2"].set_property("pkg_config_name", "catch2")
self.cpp_info.components["_catch2"].names["cmake_find_package"] = "Catch2"
self.cpp_info.components["_catch2"].names["cmake_find_package_multi"] = "Catch2"

self.cpp_info.components["catch2_with_main"].builddirs = [os.path.join("lib", "cmake", "Catch2")]
self.cpp_info.components["catch2_with_main"].builddirs.append(os.path.join("lib", "cmake", "Catch2"))
self.cpp_info.components["catch2_with_main"].libs = ["Catch2WithMain"]
self.cpp_info.components["catch2_with_main"].system_libs = ["log"] if self.settings.os == "Android" else []
self.cpp_info.components["catch2_with_main"].set_property("cmake_target_name", "Catch2::Catch2WithMain")
self.cpp_info.components["catch2_with_main"].set_property("pkg_config_name", "catch2-with-main")
self.cpp_info.components["catch2_with_main"].names["cmake_find_package"] = "Catch2WithMain"
self.cpp_info.components["catch2_with_main"].names["cmake_find_package_multi"] = "Catch2WithMain"
defines = self.cpp_info.components["catch2_with_main"].defines
else:
self.cpp_info.builddirs = [os.path.join("lib", "cmake", "Catch2")]
Expand All @@ -135,4 +127,12 @@ def package_info(self):
if self.options.with_prefix:
defines.append("CATCH_CONFIG_PREFIX_ALL")
if self.options.default_reporter:
defines.append("CATCH_CONFIG_DEFAULT_REPORTER={}".format(self._default_reporter_str))
defines.append(f"CATCH_CONFIG_DEFAULT_REPORTER={self._default_reporter_str}")

# TODO: to remove in conan v2 once legacy generators removed
self.cpp_info.names["cmake_find_package"] = "Catch2"
self.cpp_info.names["cmake_find_package_multi"] = "Catch2"
self.cpp_info.components["_catch2"].names["cmake_find_package"] = "Catch2"
self.cpp_info.components["_catch2"].names["cmake_find_package_multi"] = "Catch2"
self.cpp_info.components["catch2_with_main"].names["cmake_find_package"] = "Catch2WithMain"
self.cpp_info.components["catch2_with_main"].names["cmake_find_package_multi"] = "Catch2WithMain"
17 changes: 8 additions & 9 deletions recipes/catch2/2.x.x/test_package/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,32 +1,31 @@
cmake_minimum_required(VERSION 3.5)
project(test_package)
cmake_minimum_required(VERSION 3.8)
project(test_package LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()

find_package(Catch2 REQUIRED)
find_package(Catch2 REQUIRED CONFIG)

if(NOT WITH_PREFIX)
add_executable(test_package 000-CatchMain.cpp 100-Fix-Section.cpp)
target_link_libraries(test_package PRIVATE Catch2::Catch2)
target_compile_features(test_package PRIVATE cxx_std_11)

if(WITH_MAIN)
add_executable(standalone 200-standalone.cpp)
target_link_libraries(standalone PRIVATE Catch2::Catch2WithMain)
target_compile_features(standalone PRIVATE cxx_std_11)
if(WITH_BENCHMARK)
add_executable(benchmark 300-benchmark.cpp)
target_link_libraries(benchmark PRIVATE Catch2::Catch2WithMain)
target_compile_features(benchmark PRIVATE cxx_std_11)
endif()
endif()
else()
add_executable(test_package 000-CatchMain.cpp 400-with-prefix.cpp)
target_link_libraries(test_package PRIVATE Catch2::Catch2)
target_compile_features(test_package PRIVATE cxx_std_11)

if(WITH_MAIN)
add_executable(standalone 400-with-prefix.cpp)
target_link_libraries(standalone PRIVATE Catch2::Catch2WithMain)
target_compile_features(standalone PRIVATE cxx_std_11)
endif()
endif()
44 changes: 31 additions & 13 deletions recipes/catch2/2.x.x/test_package/conanfile.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,42 @@
from conans import ConanFile, CMake, tools
from conans.tools import Version
from conan import ConanFile
from conan.tools.cmake import CMake, CMakeToolchain
from conan.tools.build import can_run
from conan.tools.cmake import cmake_layout
import os


class TestPackageConan(ConanFile):
settings = "os", "compiler", "build_type", "arch"
generators = "cmake", "cmake_find_package"
generators = "CMakeDeps", "VirtualRunEnv"
test_type = "explicit"

_tests_todo = []

def requirements(self):
self.requires(self.tested_reference_str)

def generate(self):
tc = CMakeToolchain(self)
tc.variables["WITH_PREFIX"] = self.dependencies[self.tested_reference_str].options.with_prefix
tc.variables["WITH_MAIN"] = self.dependencies[self.tested_reference_str].options.with_main
tc.variables["WITH_BENCHMARK"] = self.dependencies[self.tested_reference_str].options.with_main and self.dependencies[self.tested_reference_str].options.with_benchmark
tc.generate()

# note: this is required as self.dependencies is not available in test()
self._tests_todo.append("test_package")
if self.dependencies[self.tested_reference_str].options.with_main:
self._tests_todo.append("standalone")
if self.dependencies[self.tested_reference_str].options.with_benchmark:
self._tests_todo.append("benchmark")

def layout(self):
cmake_layout(self)

def build(self):
cmake = CMake(self)
cmake.definitions["WITH_MAIN"] = self.options["catch2"].with_main
cmake.definitions["WITH_BENCHMARK"] = self.options["catch2"].with_main and self.options["catch2"].with_benchmark
cmake.definitions["WITH_PREFIX"] = self.options["catch2"].with_prefix
cmake.configure()
cmake.build()

def test(self):
if not tools.cross_building(self.settings):
self.run(os.path.join("bin", "test_package"), run_environment=True)
if self.options["catch2"].with_main:
self.run(os.path.join("bin", "standalone"), run_environment=True)
if self.options["catch2"].with_benchmark:
self.run(os.path.join("bin", "benchmark"), run_environment=True)
if can_run(self):
for test_name in self._tests_todo:
self.run(os.path.join(self.cpp.build.bindirs[0], test_name), env="conanrun")