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

abseil: cross-compatibility conan v1/v2 #12056

Merged
merged 8 commits into from
Aug 26, 2022
Merged
Show file tree
Hide file tree
Changes from 5 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
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,29 +1,8 @@
cmake_minimum_required(VERSION 3.8)
project(cmake_wrapper)

include(conanbuildinfo.cmake)
conan_basic_setup()

# Note: This must appear before adding the Abseil source subdirectory because
# Abseil's minimum CMake version is 3.5 so it may cause the definition of
# check_cxx_source_compiles() to have CMP0067 unset
include(CheckCXXSourceCompiles)

if(MSVC)
add_definitions("-D_HAS_DEPRECATED_RESULT_OF")
endif()

if(NOT CMAKE_SYSTEM_PROCESSOR)
set(CMAKE_SYSTEM_PROCESSOR ${CONAN_ABSEIL_SYSTEM_PROCESSOR})
endif()

add_subdirectory(source_subfolder)

get_target_property(ABSL_INCLUDES absl::config INTERFACE_INCLUDE_DIRECTORIES)
set(CMAKE_REQUIRED_INCLUDES ${ABSL_INCLUDES})

check_cxx_source_compiles("
#include\"absl/base/config.h\"
#include \"absl/base/config.h\"
#if defined(ABSL_HAVE_STD_STRING_VIEW) && ABSL_HAVE_STD_STRING_VIEW == 1
int main() {}
#else
Expand All @@ -33,7 +12,7 @@ int main() {}
USE_STD_STRING_VIEW)

check_cxx_source_compiles("
#include\"absl/base/config.h\"
#include \"absl/base/config.h\"
#if defined(ABSL_HAVE_STD_ANY) && ABSL_HAVE_STD_ANY == 1
int main() {}
#else
Expand All @@ -43,7 +22,7 @@ int main() {}
USE_STD_ANY)

check_cxx_source_compiles("
#include\"absl/base/config.h\"
#include \"absl/base/config.h\"
#if defined(ABSL_HAVE_STD_OPTIONAL) && ABSL_HAVE_STD_OPTIONAL == 1
int main() {}
#else
Expand All @@ -53,7 +32,7 @@ int main() {}
USE_STD_OPTIONAL)

check_cxx_source_compiles("
#include\"absl/base/config.h\"
#include \"absl/base/config.h\"
#if defined(ABSL_HAVE_STD_VARIANT) && ABSL_HAVE_STD_VARIANT == 1
int main() {}
#else
Expand All @@ -62,4 +41,4 @@ int main() {}
"
USE_STD_VARIANT)

configure_file(${CMAKE_CURRENT_SOURCE_DIR}/abi.h.in ${CMAKE_CURRENT_SOURCE_DIR}/abi.h)
configure_file(${CMAKE_CURRENT_LIST_DIR}/abi.h.in ${PROJECT_BINARY_DIR}/abi.h)
9 changes: 0 additions & 9 deletions recipes/abseil/all/conandata.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,14 @@ sources:
patches:
"20211102.0":
- patch_file: "patches/0003-absl-string-libm.patch"
base_path: "source_subfolder"
"20210324.2":
- patch_file: "patches/0003-absl-string-libm.patch"
base_path: "source_subfolder"
- patch_file: "patches/0004-cpp-standard-20210324.patch"
base_path: "source_subfolder"
"20200923.3":
- patch_file: "patches/0003-absl-string-libm.patch"
base_path: "source_subfolder"
- patch_file: "patches/0004-cpp-standard-20200923.patch"
base_path: "source_subfolder"
"20200225.3":
- patch_file: "patches/0001-cmake-install.patch"
base_path: "source_subfolder"
- patch_file: "patches/0002-missing-numeric_limits.h.patch"
base_path: "source_subfolder"
- patch_file: "patches/0003-absl-string-libm.patch"
base_path: "source_subfolder"
- patch_file: "patches/0004-cpp-standard-20200225.patch"
base_path: "source_subfolder"
186 changes: 105 additions & 81 deletions recipes/abseil/all/conanfile.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
from conans import ConanFile, CMake, tools
from conans.errors import ConanInvalidConfiguration
from conan import ConanFile
from conan.errors import ConanInvalidConfiguration
from conan.tools.build import check_min_cppstd, cross_building
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
from conan.tools.files import apply_conandata_patches, copy, get, load, replace_in_file, rmdir, save
from conan.tools.microsoft import is_msvc
from conans import tools as tools_legacy
import json
import os
import re
import textwrap

required_conan_version = ">=1.43.0"
required_conan_version = ">=1.50.0"


class AbseilConan(ConanFile):
Expand All @@ -26,22 +32,11 @@ class AbseilConan(ConanFile):
}

short_paths = True
generators = "cmake"
_cmake = None

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

@property
def _is_msvc(self):
return str(self.settings.compiler) in ["Visual Studio", "msvc"]

def export_sources(self):
self.copy("CMakeLists.txt")
self.copy("abi.h.in")
for patch in self.conan_data.get("patches", {}).get(self.version, []):
self.copy(patch["patch_file"])
copy(self, "abi_trick/*", self.recipe_folder, self.export_sources_folder)
for p in self.conan_data.get("patches", {}).get(self.version, []):
copy(self, p["patch_file"], self.recipe_folder, self.export_sources_folder)

def config_options(self):
if self.settings.os == "Windows":
Expand All @@ -52,69 +47,83 @@ def configure(self):
del self.options.fPIC

def validate(self):
if self.settings.compiler.get_safe("cppstd"):
tools.check_min_cppstd(self, 11)
if self.options.shared and self._is_msvc:
if self.info.settings.compiler.cppstd:
check_min_cppstd(self, 11)
if self.info.options.shared and is_msvc(self):
# upstream tries its best to export symbols, but it's broken for the moment
raise ConanInvalidConfiguration("abseil shared not availabe for Visual Studio (yet)")

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

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

def _configure_cmake(self):
if self._cmake:
return self._cmake
self._cmake = CMake(self)
self._cmake.definitions["ABSL_ENABLE_INSTALL"] = True
self._cmake.definitions["ABSL_PROPAGATE_CXX_STD"] = True
self._cmake.definitions["BUILD_TESTING"] = False
if tools.cross_building(self):
self._cmake.definitions["CONAN_ABSEIL_SYSTEM_PROCESSOR"] = str(self.settings.arch)
self._cmake.configure()
return self._cmake
get(self, **self.conan_data["sources"][self.version],
destination=self.source_folder, strip_root=True)

def generate(self):
tc = CMakeToolchain(self)
tc.variables["ABSL_ENABLE_INSTALL"] = True
tc.variables["ABSL_PROPAGATE_CXX_STD"] = True
tc.variables["BUILD_TESTING"] = False
# We force CMP0067 policy to NEW for our abi trick in _patch_sources()
tc.cache_variables["CMAKE_POLICY_DEFAULT_CMP0067"] = "NEW"
if is_msvc(self):
# see https://github.com/abseil/abseil-cpp/issues/649
tc.preprocessor_definitions["_HAS_DEPRECATED_RESULT_OF"] = 1
tc.generate()

def _patch_sources(self):
apply_conandata_patches(self)

# In case of cross-build, set CMAKE_SYSTEM_PROCESSOR if not set by toolchain or user
if cross_building(self):
toolchain_file = os.path.join(self.generators_folder, "conan_toolchain.cmake")
cmake_system_processor_block = textwrap.dedent("""\
if(NOT CMAKE_SYSTEM_PROCESSOR)
set(CMAKE_SYSTEM_PROCESSOR {})
endif()
""".format(str(self.settings.arch)))
save(self, toolchain_file, cmake_system_processor_block, append=True)

# Trick to capture ABI
cmakelists = os.path.join(self.source_folder, "CMakeLists.txt")
abi_trick_block = textwrap.dedent("""\
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/../abi_trick")
include(conan_abi_test)
""")
save(self, cmakelists, abi_trick_block, append=True)

def build(self):
for patch in self.conan_data.get("patches", {}).get(self.version, []):
tools.patch(**patch)
cmake = self._configure_cmake()
abi_file = _ABIFile("abi.h")
abi_file.replace_in_options_file(os.path.join(self._source_subfolder, "absl", "base", "options.h"))
self._patch_sources()
cmake = CMake(self)
cmake.configure()
abi_file = _ABIFile(self, os.path.join(self.build_folder, "abi.h"))
abi_file.replace_in_options_file(os.path.join(self.source_folder, "absl", "base", "options.h"))
cmake.build()

@property
def _module_path(self):
return os.path.join("lib", "cmake", "conan_trick")

@property
def _cxx_std_build_module(self):
return "cxx_std.cmake"

def package(self):
self.copy("LICENSE", dst="licenses", src=self._source_subfolder)
cmake = self._configure_cmake()
copy(self, "LICENSE", src=self.source_folder, dst=os.path.join(self.package_folder, "licenses"))
cmake = CMake(self)
cmake.install()
tools.rmdir(os.path.join(self.package_folder, "lib", "pkgconfig"))
rmdir(self, os.path.join(self.package_folder, "lib", "pkgconfig"))

# Load components hierarchy before removing CMake files generated by abseil installation
cmake_folder = os.path.join(self.package_folder, "lib", "cmake")
components = self._create_components_file_from_cmake_target_file(os.path.join(cmake_folder, "absl", "abslTargets.cmake"))
tools.rmdir(cmake_folder)
absl_targets_file = os.path.join(cmake_folder, "absl", "abslTargets.cmake")
components = self._load_components_from_cmake_target_file(absl_targets_file)
rmdir(self, cmake_folder)

# Create a json helper file in order to populate package_info() at consume time
self._create_components_file(self._components_helper_filepath, components)

# Create a build-module that will propagate the required cxx_std to consumers of this recipe's targets
# TODO: Revisit with feedback from https://github.com/conan-io/conan/issues/10281
os.makedirs(os.path.join(self.package_folder, self._module_path))
with open(os.path.join(self.package_folder, self._module_path, self._cxx_std_build_module), 'w', encoding='utf-8') as f:
f.write("cmake_minimum_required(VERSION 3.1)\n\n")
cxx_std_required = _ABIFile("abi.h").cxx_std()
for _, values in components.items():
cmake_target = values["cmake_target"]
f.write(f"target_compile_features(absl::{cmake_target} INTERFACE cxx_std_{cxx_std_required})\n")

def _create_components_file_from_cmake_target_file(self, absl_target_file_path):
self._create_cxx_std_module_file(self._cxx_std_module_filepath, components)

def _load_components_from_cmake_target_file(self, absl_target_file_path):
components = {}

abs_target_file = open(absl_target_file_path, "r", encoding="utf-8")
abs_target_content = abs_target_file.read()
abs_target_file.close()
abs_target_content = load(self, absl_target_file_path)

cmake_functions = re.findall(r"(?P<func>add_library|set_target_properties)[\n|\s]*\([\n|\s]*(?P<args>[^)]*)\)", abs_target_content)
for (cmake_function_name, cmake_function_args) in cmake_functions:
Expand Down Expand Up @@ -151,7 +160,7 @@ def _create_components_file_from_cmake_target_file(self, absl_target_file_path):
for system_lib in ["bcrypt", "advapi32", "dbghelp"]:
if system_lib in dependency:
components[potential_lib_name].setdefault("system_libs", []).append(system_lib)
elif tools.is_apple_os(self.settings.os):
elif tools_legacy.is_apple_os(self.settings.os):
for framework in ["CoreFoundation"]:
if framework in dependency:
components[potential_lib_name].setdefault("frameworks", []).append(framework)
Expand All @@ -160,20 +169,32 @@ def _create_components_file_from_cmake_target_file(self, absl_target_file_path):
for definition in values_list:
components[potential_lib_name].setdefault("defines", []).append(definition)

# Save components informations in json file
with open(self._components_helper_filepath, "w", encoding="utf-8") as json_file:
json.dump(components, json_file, indent=4)

return components

def _create_components_file(self, output_file, components):
content = json.dumps(components, indent=4)
save(self, output_file, content)

@property
def _components_helper_filepath(self):
return os.path.join(self.package_folder, "lib", "components.json")

def _create_cxx_std_module_file(self, output_file, components):
content = ""
cxx_std_required = _ABIFile(self, os.path.join(self.build_folder, "abi.h")).cxx_std()
for _, values in components.items():
cmake_target = values["cmake_target"]
content += f"target_compile_features(absl::{cmake_target} INTERFACE cxx_std_{cxx_std_required})\n"
save(self, output_file, content)

@property
def _cxx_std_module_filepath(self):
return os.path.join(self.package_folder, "lib", "cmake", "conan_trick", "cxx_std.cmake")

def package_info(self):
self.cpp_info.set_property("cmake_file_name", "absl")

components_json_file = tools.load(self._components_helper_filepath)
components_json_file = load(self, self._components_helper_filepath)
abseil_components = json.loads(components_json_file)
for pkgconfig_name, values in abseil_components.items():
cmake_target = values["cmake_target"]
Expand All @@ -184,26 +205,29 @@ def package_info(self):
self.cpp_info.components[pkgconfig_name].system_libs = values.get("system_libs", [])
self.cpp_info.components[pkgconfig_name].frameworks = values.get("frameworks", [])
self.cpp_info.components[pkgconfig_name].requires = values.get("requires", [])
if self._is_msvc and self.settings.compiler.get_safe("cppstd") == "20":
self.cpp_info.components[pkgconfig_name].defines.extend(["_HAS_DEPRECATED_RESULT_OF", "_SILENCE_CXX17_RESULT_OF_DEPRECATION_WARNING"])

if is_msvc(self) and self.settings.compiler.get_safe("cppstd") == "20":
self.cpp_info.components[pkgconfig_name].defines.extend([
"_HAS_DEPRECATED_RESULT_OF",
"_SILENCE_CXX17_RESULT_OF_DEPRECATION_WARNING",
])

self.cpp_info.components[pkgconfig_name].names["cmake_find_package"] = cmake_target
self.cpp_info.components[pkgconfig_name].names["cmake_find_package_multi"] = cmake_target

self.cpp_info.names["cmake_find_package"] = "absl"
self.cpp_info.names["cmake_find_package_multi"] = "absl"

cxx_std_build_module = os.path.join(self.package_folder, self._module_path, self._cxx_std_build_module)
self.cpp_info.set_property("cmake_build_modules", [cxx_std_build_module, ])
self.cpp_info.components["absl_config"].build_modules["cmake_find_package"] = [cxx_std_build_module, ]
self.cpp_info.components["absl_config"].build_modules["cmake_find_package_multi"] = [cxx_std_build_module, ]
self.cpp_info.set_property("cmake_build_modules", [self._cxx_std_module_filepath])
self.cpp_info.components["absl_config"].build_modules["cmake_find_package"] = [self._cxx_std_module_filepath]
self.cpp_info.components["absl_config"].build_modules["cmake_find_package_multi"] = [self._cxx_std_module_filepath]


class _ABIFile:
abi = {}

def __init__(self, filepath):
abi_h = tools.load(filepath)
def __init__(self, conanfile, filepath):
self.conanfile = conanfile
abi_h = load(self.conanfile, filepath)
for line in abi_h.splitlines():
if line.startswith("#define"):
tokens = line.split()
Expand All @@ -212,9 +236,9 @@ def __init__(self, filepath):

def replace_in_options_file(self, options_filepath):
for name, value in self.abi.items():
tools.replace_in_file(options_filepath,
replace_in_file(self.conanfile, options_filepath,
"#define ABSL_OPTION_{} 2".format(name),
"#define ABSL_OPTION_{} {}".format(name, value))

def cxx_std(self):
return 17 if any([v == "1" for k, v in self.abi.items()]) else 11
Loading