Skip to content

Commit

Permalink
(#5420) Boost: fix error when building without fiber + improve stackt…
Browse files Browse the repository at this point in the history
…race support

* boost: fix building with -o boost:shared=True -o boost:without_fiber=True

* boost: it is safe to use self.options.debug_level unconditionally

* boost: add addr2line_location option + test

* boost: don't create component for modules that can/should have libraries but don't have

* boost: add stacktrace_backtrace support

* boost: fix stacktrace with MSVC

* boost: fix passing of environment flags + remove thread_local for older clang versions

* boost: don't build the tests in parallel to see where c3i chokes

* boost: allow building boost_stacktrace_{addr2line,backtrace} when cross building
  • Loading branch information
madebr authored May 18, 2021
1 parent 0b024eb commit 73b6ce4
Show file tree
Hide file tree
Showing 4 changed files with 245 additions and 31 deletions.
145 changes: 114 additions & 31 deletions recipes/boost/all/conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ class BoostConan(ConanFile):
"extra_b2_flags": "ANY", # custom b2 flags
"i18n_backend": ["iconv", "icu", None],
"visibility": ["global", "protected", "hidden"],
"addr2line_location": "ANY",
"with_stacktrace_backtrace": [True, False],
}
options.update({"without_{}".format(_name): [True, False] for _name in CONFIGURE_OPTIONS})

Expand Down Expand Up @@ -124,6 +126,8 @@ class BoostConan(ConanFile):
"extra_b2_flags": "None",
"i18n_backend": "iconv",
"visibility": "hidden",
"addr2line_location": "/usr/bin/addr2line",
"with_stacktrace_backtrace": True,
}
default_options.update({"without_{}".format(_name): False for _name in CONFIGURE_OPTIONS})
default_options.update({"without_{}".format(_name): True for _name in ("graph_parallel", "mpi", "python")})
Expand Down Expand Up @@ -234,6 +238,10 @@ def config_options(self):
if "without_{}".format(opt_name) not in self.options:
raise ConanException("{} has the configure options {} which is not available in conanfile.py".format(self._dependency_filename, opt_name))

# libbacktrace cannot be built on Visual Studio
if self.settings.compiler == "Visual Studio":
del self.options.with_stacktrace_backtrace

# nowide requires a c++11-able compiler + movable std::fstream: change default to not build on compiler with too old default c++ standard or too low compiler.cppstd
# json requires a c++11-able compiler: change default to not build on compiler with too old default c++ standard or too low compiler.cppstd
if self.settings.compiler.cppstd:
Expand Down Expand Up @@ -297,6 +305,10 @@ def _fPIC(self):
def _shared(self):
return self.options.get_safe("shared", self.default_options["shared"])

@property
def _stacktrace_addr2line_available(self):
return not self.options.header_only and not self.options.without_stacktrace and self.settings.compiler != "Visual Studio"

def configure(self):
if self.options.header_only:
del self.options.shared
Expand All @@ -315,6 +327,15 @@ def configure(self):
self.options.python_version = self._detect_python_version()
self.options.python_executable = self._python_executable

if self._stacktrace_addr2line_available:
if os.path.abspath(str(self.options.addr2line_location)) != str(self.options.addr2line_location):
raise ConanInvalidConfiguration("addr2line_location must be an absolute path to addr2line")
else:
del self.options.addr2line_location

if self.options.get_safe("without_stacktrace", True):
del self.options.with_stacktrace_backtrace

if self.options.layout == "b2-default":
self.options.layout = "versioned" if self.settings.os == "Windows" else "system"

Expand All @@ -334,7 +355,7 @@ def validate(self):
if self.settings.compiler == "Visual Studio" and self._shared:
if "MT" in str(self.settings.compiler.runtime):
raise ConanInvalidConfiguration("Boost can not be built as shared library with MT runtime.")
if self.options.numa:
if self.options.get_safe("numa"):
raise ConanInvalidConfiguration("Cannot build a shared boost with numa support on Visual Studio")

# Check, when a boost module is enabled, whether the boost modules it depends on are enabled as well.
Expand Down Expand Up @@ -424,6 +445,10 @@ def _with_icu(self):
def _with_iconv(self):
return not self.options.header_only and self._with_dependency("iconv") and self.options.i18n_backend == "iconv"

@property
def _with_stacktrace_backtrace(self):
return not self.options.header_only and self.options.get_safe("with_stacktrace_backtrace", False)

def requirements(self):
if self._with_zlib:
self.requires("zlib/1.2.11")
Expand All @@ -433,6 +458,9 @@ def requirements(self):
self.requires("xz_utils/5.2.5")
if self._with_zstd:
self.requires("zstd/1.4.9")
if self._with_stacktrace_backtrace:
self.requires("libbacktrace/cci.20210118")
self.requires("libunwind/1.5.0")

if self._with_icu:
self.requires("icu/68.2")
Expand Down Expand Up @@ -672,8 +700,7 @@ def _build_bcp(self):
with tools.vcvars(self.settings) if self._is_msvc else tools.no_op():
with tools.chdir(folder):
command = "%s -j%s --abbreviate-paths toolset=%s" % (self._b2_exe, tools.cpu_count(), self._toolset)
if "debug_level" in self.options:
command += " -d%d" % self.options.debug_level
command += " -d%d" % self.options.debug_level
self.output.warn(command)
self.run(command, run_environment=True)

Expand Down Expand Up @@ -703,6 +730,22 @@ def _run_bcp(self):
self.run(command)

def build(self):
if tools.cross_building(self.settings, skip_x64_x86=True):
# When cross building, do not attempt to run the test-executable (assume they work)
tools.replace_in_file(os.path.join(self.source_folder, self._source_subfolder, "libs", "stacktrace", "build", "Jamfile.v2"),
"$(>) > $(<)",
"echo \"\" > $(<)", strict=False)
# Older clang releases require a thread_local variable to be initialized by a constant value
tools.replace_in_file(os.path.join(self.source_folder, self._source_subfolder, "boost", "stacktrace", "detail", "libbacktrace_impls.hpp"),
"/* thread_local */", "thread_local", strict=False)
tools.replace_in_file(os.path.join(self.source_folder, self._source_subfolder, "boost", "stacktrace", "detail", "libbacktrace_impls.hpp"),
"/* static __thread */", "static __thread", strict=False)
if self.settings.compiler == "clang" and tools.Version(self.settings.compiler.version) < 6:
tools.replace_in_file(os.path.join(self.source_folder, self._source_subfolder, "boost", "stacktrace", "detail", "libbacktrace_impls.hpp"),
"thread_local", "/* thread_local */")
tools.replace_in_file(os.path.join(self.source_folder, self._source_subfolder, "boost", "stacktrace", "detail", "libbacktrace_impls.hpp"),
"static __thread", "/* static __thread */")

if self.options.header_only:
self.output.warn("Header only package, skipping build")
return
Expand Down Expand Up @@ -957,18 +1000,22 @@ def add_defines(library):
link_flags = 'linkflags="%s"' % " ".join(link_flags) if link_flags else ""
flags.append(link_flags)

if self.options.get_safe("addr2line_location"):
cxx_flags.append("-DBOOST_STACKTRACE_ADDR2LINE_LOCATION={}".format(self.options.addr2line_location))

cxx_flags = 'cxxflags="%s"' % " ".join(cxx_flags) if cxx_flags else ""
flags.append(cxx_flags)

if self.options.extra_b2_flags:
flags.extend(shlex.split(str(self.options.extra_b2_flags)))

flags.extend(["install",
"--prefix=%s" % self.package_folder,
"-j%s" % tools.cpu_count(),
"--abbreviate-paths"])
if "debug_level" in self.options:
flags.append("-d%d" % self.options.debug_level)
flags.extend([
"install",
"--prefix=%s" % self.package_folder,
"-j%s" % tools.cpu_count(),
"--abbreviate-paths",
"-d%d" % self.options.debug_level,
])
return flags

@property
Expand Down Expand Up @@ -1066,7 +1113,7 @@ def create_library_config(deps_name, name):

# Specify here the toolset with the binary if present if don't empty parameter :
contents += '\nusing "%s" : %s : ' % (self._toolset, self._toolset_version)

if self._is_msvc:
contents += ' "%s"' % self._cxx.replace("\\", "/")
else:
Expand All @@ -1083,16 +1130,27 @@ def create_library_config(deps_name, name):
contents += '<archiver>"%s" ' % tools.which(self._ar).replace("\\", "/")
if self._ranlib:
contents += '<ranlib>"%s" ' % tools.which(self._ranlib).replace("\\", "/")
if "CXXFLAGS" in os.environ:
contents += '<cxxflags>"%s" ' % os.environ["CXXFLAGS"]
if "CFLAGS" in os.environ:
contents += '<cflags>"%s" ' % os.environ["CFLAGS"]
if "CPPFLAGS" in os.environ:
contents += '<compileflags>"%s" ' % os.environ["CPPFLAGS"]
if "LDFLAGS" in os.environ:
contents += '<linkflags>"%s" ' % os.environ["LDFLAGS"]
if "ASFLAGS" in os.environ:
contents += '<asmflags>"%s" ' % os.environ["ASFLAGS"]
cxxflags = tools.get_env("CXXFLAGS", "") + " "
cflags = tools.get_env("CFLAGS", "") + " "
cppflags = tools.get_env("CPPFLAGS", "") + " "
ldflags = tools.get_env("LDFLAGS", "") + " "
asflags = tools.get_env("ASFLAGS", "") + " "

if self._with_stacktrace_backtrace:
for l in ("libbacktrace", "libunwind"):
cppflags += " ".join("-I{}".format(p) for p in self.deps_cpp_info[l].include_paths) + " "
ldflags += " ".join("-L{}".format(p) for p in self.deps_cpp_info[l].lib_paths) + " "

if cxxflags.strip():
contents += '<cxxflags>"%s" ' % cxxflags.strip()
if cflags.strip():
contents += '<cflags>"%s" ' % cflags.strip()
if cppflags.strip():
contents += '<compileflags>"%s" ' % cppflags.strip()
if ldflags.strip():
contents += '<linkflags>"%s" ' % ldflags.strip()
if asflags.strip():
contents += '<asmflags>"%s" ' % asflags.strip()

contents += " ;"

Expand Down Expand Up @@ -1349,14 +1407,8 @@ def filter_transform_module_libraries(names):
for name in names:
if name in ("boost_stacktrace_windbg", "boost_stacktrace_windbg_cached") and self.settings.os != "Windows":
continue
if name in ("boost_stacktrace_addr2line", "boost_stacktrace_basic") and self.settings.compiler == "Visual Studio":
if name in ("boost_stacktrace_addr2line", "boost_stacktrace_backtrace", "boost_stacktrace_basic",) and self.settings.compiler == "Visual Studio":
continue
if name == "boost_stacktrace_backtrace":
if "boost_stacktrace_backtrace" not in all_detected_libraries:
continue
# FIXME: Boost.Build sometimes picks up a system libbacktrace library.
# How to avoid this and force using a conan packaged libbacktrace package.
self.output.warn("Picked up a system libbacktrace library")
if not self.options.get_safe("numa") and "_numa" in name:
continue
libs.append(add_libprefix(name.format(**libformatdata)) + libsuffix)
Expand All @@ -1368,6 +1420,11 @@ def filter_transform_module_libraries(names):
continue

module_libraries = filter_transform_module_libraries(self._dependencies["libs"][module])

# Don't create components for modules that should have libraries, but don't have (because of filter)
if self._dependencies["libs"][module] and not module_libraries:
continue

all_expected_libraries = all_expected_libraries.union(module_libraries)
if set(module_libraries).difference(all_detected_libraries):
incomplete_components.append(module)
Expand All @@ -1380,9 +1437,6 @@ def filter_transform_module_libraries(names):
self.cpp_info.components[module].names["pkg_config"] = "boost_{}".format(module)

for requirement in self._dependencies.get("requirements", {}).get(module, []):
if requirement == "backtrace":
# FIXME: backtrace not (yet) available in cci
continue
if self.options.get_safe(requirement, None) == False:
continue
conan_requirement = self._option_to_conan_requirement(requirement)
Expand All @@ -1392,8 +1446,9 @@ def filter_transform_module_libraries(names):
if requirement != self.options.i18n_backend:
continue
self.cpp_info.components[module].requires.append("{0}::{0}".format(conan_requirement))

for incomplete_component in incomplete_components:
self.output.warn("Boost component '{0}' is missing libraries. Try building boost with '-o boost:without_{0}'.".format(incomplete_component))
self.output.warn("Boost component '{0}' is missing libraries. Try building boost with '-o boost:without_{0}'. (Option is not guaranteed to exist)".format(incomplete_component))

non_used = all_detected_libraries.difference(all_expected_libraries)
if non_used:
Expand All @@ -1403,6 +1458,33 @@ def filter_transform_module_libraries(names):
if non_built:
raise ConanException("These libraries were expected to be built, but were not built: {}".format(non_built))

if not self.options.without_stacktrace:
if self.settings.os in ("Linux", "FreeBSD"):
self.cpp_info.components["stacktrace_basic"].system_libs.append("dl")
self.cpp_info.components["stacktrace_addr2line"].system_libs.append("dl")
if self._with_stacktrace_backtrace:
self.cpp_info.components["stacktrace_backtrace"].system_libs.append("dl")

if self._stacktrace_addr2line_available:
self.cpp_info.components["stacktrace_addr2line"].defines.extend([
"BOOST_STACKTRACE_ADDR2LINE_LOCATION=\"{}\"".format(self.options.addr2line_location),
"BOOST_STACKTRACE_USE_ADDR2LINE",
])

if self._with_stacktrace_backtrace:
self.cpp_info.components["stacktrace_backtrace"].defines.append("BOOST_STACKTRACE_USE_BACKTRACE")
self.cpp_info.components["stacktrace_backtrace"].system_libs.append("dl")
self.cpp_info.components["stacktrace_backtrace"].requires.extend([
"libunwind::libunwind",
"libbacktrace::libbacktrace",
])

self.cpp_info.components["stacktrace_noop"].defines.append("BOOST_STACKTRACE_USE_NOOP")

if self.settings.os == "Windows":
self.cpp_info.components["stacktrace_windb"].defines.append("BOOST_STACKTRACE_USE_WINDBG")
self.cpp_info.components["stacktrace_windb_cached"].defines.append("BOOST_STACKTRACE_USE_WINDBG_CACHED")

if not self.options.without_python:
pyversion = tools.Version(self._python_version)
self.cpp_info.components["python{}{}".format(pyversion.major, pyversion.minor)].requires = ["python"]
Expand Down Expand Up @@ -1438,3 +1520,4 @@ def filter_transform_module_libraries(names):
self.cpp_info.components["headers"].defines.extend(["BOOST_AC_USE_PTHREADS", "BOOST_SP_USE_PTHREADS"])
else:
self.cpp_info.components["headers"].defines.extend(["BOOST_AC_DISABLE_THREADS", "BOOST_SP_DISABLE_THREADS"])
self.user_info.stacktrace_addr2line_available = self._stacktrace_addr2line_available
37 changes: 37 additions & 0 deletions recipes/boost/all/test_package/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,43 @@ if(NOT HEADER_ONLY)
target_link_libraries(locale_exe PRIVATE Boost::locale)
endif()

if(WITH_STACKTRACE_ADDR2LINE)
find_package(Boost COMPONENTS stacktrace REQUIRED)
add_executable(stacktrace_addr2line_exe stacktrace.cpp)
target_compile_definitions(stacktrace_addr2line_exe PRIVATE TEST_STACKTRACE_IMPL=1)
target_link_libraries(stacktrace_addr2line_exe PRIVATE Boost::stacktrace_addr2line)
endif()

if(WITH_STACKTRACE_BACKTRACE)
add_executable(stacktrace_backtrace_exe stacktrace.cpp)
target_compile_definitions(stacktrace_backtrace_exe PRIVATE TEST_STACKTRACE_IMPL=2)
target_link_libraries(stacktrace_backtrace_exe PRIVATE Boost::stacktrace_backtrace)
endif()

if(WITH_STACKTRACE)
find_package(Boost COMPONENTS stacktrace REQUIRED)

if(NOT MSVC)
add_executable(stacktrace_basic_exe stacktrace.cpp)
target_compile_definitions(stacktrace_basic_exe PRIVATE TEST_STACKTRACE_IMPL=3)
target_link_libraries(stacktrace_basic_exe PRIVATE Boost::stacktrace_basic)
endif()

add_executable(stacktrace_noop_exe stacktrace.cpp)
target_compile_definitions(stacktrace_noop_exe PRIVATE TEST_STACKTRACE_IMPL=4)
target_link_libraries(stacktrace_noop_exe PRIVATE Boost::stacktrace_noop)

if(WIN32)
add_executable(stacktrace_windbg_exe stacktrace.cpp)
target_compile_definitions(stacktrace_windbg_exe PRIVATE TEST_STACKTRACE_IMPL=5)
target_link_libraries(stacktrace_windbg_exe PRIVATE Boost::stacktrace_windbg)

add_executable(stacktrace_windbg_cached_exe stacktrace.cpp)
target_compile_definitions(stacktrace_windbg_cached_exe PRIVATE TEST_STACKTRACE_IMPL=6)
target_link_libraries(stacktrace_windbg_cached_exe PRIVATE Boost::stacktrace_windbg_cached)
endif()
endif()

if(WITH_PYTHON)
find_package(Boost COMPONENTS python REQUIRED)
add_library(hello_ext MODULE python.cpp)
Expand Down
16 changes: 16 additions & 0 deletions recipes/boost/all/test_package/conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ def build(self):
cmake.definitions["WITH_LOCALE"] = not self.options["boost"].without_locale
cmake.definitions["WITH_NOWIDE"] = not self._boost_option("without_nowide", True)
cmake.definitions["WITH_JSON"] = not self._boost_option("without_json", True)
cmake.definitions["WITH_STACKTRACE"] = not self.options["boost"].without_stacktrace
cmake.definitions["WITH_STACKTRACE_ADDR2LINE"] = self.deps_user_info["boost"].stacktrace_addr2line_available
cmake.definitions["WITH_STACKTRACE_BACKTRACE"] = self._boost_option("with_stacktrace_backtrace", False)
cmake.configure()
# Disable parallel builds because c3i (=conan-center's test/build infrastructure) seems to choke here
cmake.parallel = False
cmake.build()

def test(self):
Expand Down Expand Up @@ -65,3 +70,14 @@ def test(self):
with tools.environment_append({"PYTHONPATH": "{}:{}".format("bin", "lib")}):
self.run("{} {}".format(self.options["boost"].python_executable, os.path.join(self.source_folder, "python.py")), run_environment=True)
self.run(os.path.join("bin", "numpy_exe"), run_environment=True)
if not self.options["boost"].without_stacktrace:
if self.settings.compiler != "Visual Studio":
self.run(os.path.join("bin", "stacktrace_basic_exe"), run_environment=True)
self.run(os.path.join("bin", "stacktrace_noop_exe"), run_environment=True)
if str(self.deps_user_info["boost"].stacktrace_addr2line_available) == "True":
self.run(os.path.join("bin", "stacktrace_addr2line_exe"), run_environment=True)
if self.settings.os == "Windows":
self.run(os.path.join("bin", "stacktrace_windbg_exe"), run_environment=True)
self.run(os.path.join("bin", "stacktrace_windbg_cached_exe"), run_environment=True)
if self._boost_option("with_stacktrace_backtrace", False):
self.run(os.path.join("bin", "stacktrace_backtrace_exe"), run_environment=True)
Loading

0 comments on commit 73b6ce4

Please sign in to comment.