diff --git a/.ci_support/linux_64_.yaml b/.ci_support/linux_64_.yaml index 1db6794..a1d9aa2 100644 --- a/.ci_support/linux_64_.yaml +++ b/.ci_support/linux_64_.yaml @@ -1,7 +1,3 @@ -NP_DUMMY_VER: -- '1.21' -PY_DUMMY_VER: -- '3.10' bzip2: - '1' cdt_name: diff --git a/.ci_support/linux_aarch64_.yaml b/.ci_support/linux_aarch64_.yaml index 5616694..5c9d6b6 100644 --- a/.ci_support/linux_aarch64_.yaml +++ b/.ci_support/linux_aarch64_.yaml @@ -1,9 +1,5 @@ BUILD: - aarch64-conda_cos7-linux-gnu -NP_DUMMY_VER: -- '1.21' -PY_DUMMY_VER: -- '3.10' bzip2: - '1' cdt_arch: diff --git a/.ci_support/linux_ppc64le_.yaml b/.ci_support/linux_ppc64le_.yaml index e3c3e6e..664d164 100644 --- a/.ci_support/linux_ppc64le_.yaml +++ b/.ci_support/linux_ppc64le_.yaml @@ -1,7 +1,3 @@ -NP_DUMMY_VER: -- '1.21' -PY_DUMMY_VER: -- '3.10' bzip2: - '1' cdt_name: diff --git a/.ci_support/osx_64_.yaml b/.ci_support/osx_64_.yaml index b9759b6..d4c2a34 100644 --- a/.ci_support/osx_64_.yaml +++ b/.ci_support/osx_64_.yaml @@ -1,9 +1,5 @@ MACOSX_DEPLOYMENT_TARGET: -- '10.9' -NP_DUMMY_VER: -- '1.21' -PY_DUMMY_VER: -- '3.10' +- '10.13' bzip2: - '1' channel_sources: diff --git a/.ci_support/osx_arm64_.yaml b/.ci_support/osx_arm64_.yaml index 342b469..9795e2e 100644 --- a/.ci_support/osx_arm64_.yaml +++ b/.ci_support/osx_arm64_.yaml @@ -1,9 +1,5 @@ MACOSX_DEPLOYMENT_TARGET: - '11.0' -NP_DUMMY_VER: -- '1.21' -PY_DUMMY_VER: -- '3.10' bzip2: - '1' channel_sources: diff --git a/.ci_support/win_64_.yaml b/.ci_support/win_64_.yaml index 7fcf728..3e849f3 100644 --- a/.ci_support/win_64_.yaml +++ b/.ci_support/win_64_.yaml @@ -1,7 +1,3 @@ -NP_DUMMY_VER: -- '1.21' -PY_DUMMY_VER: -- '3.10' bzip2: - '1' channel_sources: diff --git a/.scripts/build_steps.sh b/.scripts/build_steps.sh index 3b69f9e..4a057e4 100755 --- a/.scripts/build_steps.sh +++ b/.scripts/build_steps.sh @@ -34,9 +34,9 @@ CONDARC export CONDA_LIBMAMBA_SOLVER_NO_CHANNELS_FROM_INSTALLED=1 mamba install --update-specs --yes --quiet --channel conda-forge --strict-channel-priority \ - pip mamba conda-build boa conda-forge-ci-setup=4 "conda-build<3.28" + pip mamba conda-build conda-forge-ci-setup=4 "conda-build<3.28" mamba update --update-specs --yes --quiet --channel conda-forge --strict-channel-priority \ - pip mamba conda-build boa conda-forge-ci-setup=4 "conda-build<3.28" + pip mamba conda-build conda-forge-ci-setup=4 "conda-build<3.28" # set up the condarc setup_conda_rc "${FEEDSTOCK_ROOT}" "${RECIPE_ROOT}" "${CONFIG_FILE}" @@ -68,7 +68,7 @@ if [[ "${BUILD_WITH_CONDA_DEBUG:-0}" == 1 ]]; then # Drop into an interactive shell /bin/bash else - conda mambabuild "${RECIPE_ROOT}" -m "${CI_SUPPORT}/${CONFIG}.yaml" \ + conda build "${RECIPE_ROOT}" -m "${CI_SUPPORT}/${CONFIG}.yaml" \ --suppress-variables ${EXTRA_CB_OPTIONS:-} \ --clobber-file "${CI_SUPPORT}/clobber_${CONFIG}.yaml" \ --extra-meta flow_run_id="${flow_run_id:-}" remote_url="${remote_url:-}" sha="${sha:-}" diff --git a/.scripts/run_osx_build.sh b/.scripts/run_osx_build.sh index c2f2f2c..01557f7 100755 --- a/.scripts/run_osx_build.sh +++ b/.scripts/run_osx_build.sh @@ -26,9 +26,9 @@ export CONDA_SOLVER="libmamba" export CONDA_LIBMAMBA_SOLVER_NO_CHANNELS_FROM_INSTALLED=1 mamba install --update-specs --quiet --yes --channel conda-forge --strict-channel-priority \ - pip mamba conda-build boa conda-forge-ci-setup=4 "conda-build<3.28" + pip mamba conda-build conda-forge-ci-setup=4 "conda-build<3.28" mamba update --update-specs --yes --quiet --channel conda-forge --strict-channel-priority \ - pip mamba conda-build boa conda-forge-ci-setup=4 "conda-build<3.28" + pip mamba conda-build conda-forge-ci-setup=4 "conda-build<3.28" @@ -81,7 +81,7 @@ else EXTRA_CB_OPTIONS="${EXTRA_CB_OPTIONS:-} --no-test" fi - conda mambabuild ./recipe -m ./.ci_support/${CONFIG}.yaml \ + conda build ./recipe -m ./.ci_support/${CONFIG}.yaml \ --suppress-variables ${EXTRA_CB_OPTIONS:-} \ --clobber-file ./.ci_support/clobber_${CONFIG}.yaml \ --extra-meta flow_run_id="$flow_run_id" remote_url="$remote_url" sha="$sha" diff --git a/.scripts/run_win_build.bat b/.scripts/run_win_build.bat index c186760..94596cc 100755 --- a/.scripts/run_win_build.bat +++ b/.scripts/run_win_build.bat @@ -24,7 +24,7 @@ set "CONDA_LIBMAMBA_SOLVER_NO_CHANNELS_FROM_INSTALLED=1" :: Provision the necessary dependencies to build the recipe later echo Installing dependencies -mamba.exe install "python=3.10" pip mamba conda-build boa conda-forge-ci-setup=4 "conda-build<3.28" -c conda-forge --strict-channel-priority --yes +mamba.exe install "python=3.10" pip mamba conda-build conda-forge-ci-setup=4 "conda-build<3.28" -c conda-forge --strict-channel-priority --yes if !errorlevel! neq 0 exit /b !errorlevel! :: Set basic configuration @@ -55,7 +55,7 @@ call :end_group :: Build the recipe echo Building recipe -conda.exe mambabuild "recipe" -m .ci_support\%CONFIG%.yaml --suppress-variables %EXTRA_CB_OPTIONS% +conda.exe build "recipe" -m .ci_support\%CONFIG%.yaml --suppress-variables %EXTRA_CB_OPTIONS% if !errorlevel! neq 0 exit /b !errorlevel! :: Prepare some environment variables for the upload step diff --git a/build-locally.py b/build-locally.py index 3f4b7a7..e0d408d 100755 --- a/build-locally.py +++ b/build-locally.py @@ -64,8 +64,9 @@ def verify_config(ns): elif ns.config.startswith("osx"): if "OSX_SDK_DIR" not in os.environ: raise RuntimeError( - "Need OSX_SDK_DIR env variable set. Run 'export OSX_SDK_DIR=SDKs' " - "to download the SDK automatically to 'SDKs/MacOSX.sdk'. " + "Need OSX_SDK_DIR env variable set. Run 'export OSX_SDK_DIR=$PWD/SDKs' " + "to download the SDK automatically to '$PWD/SDKs/MacOSX.sdk'. " + "Note: OSX_SDK_DIR must be set to an absolute path. " "Setting this variable implies agreement to the licensing terms of the SDK by Apple." ) diff --git a/recipe/bld.bat b/recipe/bld.bat index 5b4c255..e61dbb0 100755 --- a/recipe/bld.bat +++ b/recipe/bld.bat @@ -23,6 +23,7 @@ mkdir temp_prefix variant=release ^ threading=multi ^ link=shared ^ + cxxstd=20 ^ -s NO_COMPRESSION=0 ^ -s NO_ZLIB=0 ^ -s NO_BZIP2=0 ^ diff --git a/recipe/build.sh b/recipe/build.sh index 26c5b74..94b2535 100755 --- a/recipe/build.sh +++ b/recipe/build.sh @@ -17,6 +17,8 @@ CXXFLAGS="${CXXFLAGS} -fPIC" if [[ "${target_platform}" == osx* ]]; then TOOLSET=clang + # see https://conda-forge.org/docs/maintainer/knowledge_base/#newer-c-features-with-old-sdk + CXXFLAGS="${CXXFLAGS} -D_LIBCPP_DISABLE_AVAILABILITY" elif [[ "${target_platform}" == linux* ]]; then TOOLSET=gcc fi @@ -75,6 +77,7 @@ mkdir temp_prefix include="${INCLUDE_PATH}" \ cxxflags="${CXXFLAGS}" \ linkflags="${LINKFLAGS}" \ + cxxstd=20 \ --layout=system \ -j"${CPU_COUNT}" \ install diff --git a/recipe/conda_build_config.yaml b/recipe/conda_build_config.yaml index 3518139..0654d29 100644 --- a/recipe/conda_build_config.yaml +++ b/recipe/conda_build_config.yaml @@ -1,6 +1,3 @@ -# we only need _a_ python version to build the headers for libboost-headers, -# putting it in CBC means it's accessible also in the build scripts -PY_DUMMY_VER: - - "3.10" -NP_DUMMY_VER: - - "1.21" +# Bump MacOs target to 10.13 for Boost.Cobalt / C++20 support +MACOSX_DEPLOYMENT_TARGET: + - '10.13' # [osx and x86_64] diff --git a/recipe/meta.yaml b/recipe/meta.yaml index ddfd99e..5ae9170 100644 --- a/recipe/meta.yaml +++ b/recipe/meta.yaml @@ -1,7 +1,7 @@ {% set version = "1.84.0" %} {% set boost_libs = [ - "atomic", "chrono", "container", "context", "contract", "coroutine", + "atomic", "chrono", "cobalt", "container", "context", "contract", "coroutine", "date_time", "filesystem", "graph", "iostreams", "locale", "log", "log_setup", "math_c99", "math_c99f", "math_tr1", "math_tr1f", "prg_exec_monitor", "program_options", "random", "regex", "serialization", "system", "thread", @@ -11,6 +11,11 @@ {% set boost_libs_static_only = ["exception", "test_exec_monitor"] %} {% set boost_libs_py = ["python%s" % py, "numpy%s" % py] %} +# we only need _a_ python version to build the headers for libboost-headers, +# make it accessible in the build scripts via build/script_env below. +{% set PY_DUMMY_VER = "3.10" %} +{% set NP_DUMMY_VER = "1.21" %} + package: name: boost-split version: {{ version }} @@ -21,16 +26,20 @@ source: patches: # ensure our compiler flags get used during bootstrapping - patches/0001-Add-default-value-for-cxx-and-cxxflags-options-for-t.patch + - patches/0002-Reimplement-string_set-as-any_string.patch build: - number: 1 + number: 2 + script_env: + - PY_DUMMY_VER={{ PY_DUMMY_VER }} + - NP_DUMMY_VER={{ NP_DUMMY_VER }} requirements: build: - {{ compiler('cxx') }} - cross-python_{{ target_platform }} # [build_platform != target_platform] - - python {{ PY_DUMMY_VER }} *cpython # [build_platform != target_platform] - - numpy {{ NP_DUMMY_VER }} # [build_platform != target_platform] + - python {{ PY_DUMMY_VER }}.* *_cpython # [build_platform != target_platform] + - numpy {{ NP_DUMMY_VER }}.* # [build_platform != target_platform] host: - icu # [unix] - xz # [unix] @@ -38,8 +47,8 @@ requirements: - zlib - zstd # see conda_build_config - - python {{ PY_DUMMY_VER }} *cpython - - numpy {{ NP_DUMMY_VER }} + - python {{ PY_DUMMY_VER }}.* *_cpython + - numpy {{ NP_DUMMY_VER }}.* outputs: - name: libboost-headers @@ -97,6 +106,8 @@ outputs: - xz - zlib - zstd + run: + - __osx >={{ MACOSX_DEPLOYMENT_TARGET|default("10.9") }} # [osx and x86_64] run_constrained: # Anaconda's output is also called libboost; therefore we automatically # avoid issues of co-installability, because a package with the same name diff --git a/recipe/patches/0002-Reimplement-string_set-as-any_string.patch b/recipe/patches/0002-Reimplement-string_set-as-any_string.patch new file mode 100644 index 0000000..43a0be3 --- /dev/null +++ b/recipe/patches/0002-Reimplement-string_set-as-any_string.patch @@ -0,0 +1,335 @@ +From c5e8f02c903696a213fc4b710f6740ccd1f07f4e Mon Sep 17 00:00:00 2001 +From: Alexander Grund +Date: Sun, 3 Dec 2023 20:06:27 +0100 +Subject: [PATCH] Reimplement `string_set` as `any_string` + +Use a better name for a type-erased string implemented using +`dynamic_cast` instead of storing&comparing the `typeid` of the char +type used. +This is a workaround for missing typeinfo for `char8_t` on e.g. libc++ +on FreeBSD 13.1. +It also simplifies memory management size calc/copy implementation. +--- + boost/locale/detail/any_string.hpp | 71 ++++++++++++++++++++++ + boost/locale/formatting.hpp | 51 ++-------------- + libs/locale/src/boost/locale/shared/formatting.cpp | 45 -------------- + libs/locale/test/test_ios_info.cpp | 64 +++++++++++++++---- + 4 files changed, 127 insertions(+), 104 deletions(-) + create mode 100644 boost/locale/detail/any_string.hpp + +diff --git a/boost/locale/detail/any_string.hpp b/boost/locale/detail/any_string.hpp +new file mode 100644 +index 00000000..c0cc7ffb +--- /dev/null ++++ b/boost/locale/detail/any_string.hpp +@@ -0,0 +1,71 @@ ++// ++// Copyright (c) 2023 Alexander Grund ++// ++// Distributed under the Boost Software License, Version 1.0. ++// https://www.boost.org/LICENSE_1_0.txt ++ ++#ifndef BOOST_LOCALE_DETAIL_ANY_STRING_HPP_INCLUDED ++#define BOOST_LOCALE_DETAIL_ANY_STRING_HPP_INCLUDED ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/// \cond INTERNAL ++namespace boost { namespace locale { namespace detail { ++ /// Type-erased std::basic_string ++ class any_string { ++ struct BOOST_SYMBOL_VISIBLE base { ++ virtual ~base() = default; ++ virtual base* clone() const = 0; ++ ++ protected: ++ base() = default; ++ base(const base&) = default; ++ base(base&&) = delete; ++ base& operator=(const base&) = default; ++ base& operator=(base&&) = delete; ++ }; ++ template ++ struct BOOST_SYMBOL_VISIBLE impl : base { ++ explicit impl(const boost::basic_string_view value) : s(value) {} ++ impl* clone() const override { return new impl(*this); } ++ std::basic_string s; ++ }; ++ ++ std::unique_ptr s_; ++ ++ public: ++ any_string() = default; ++ any_string(const any_string& other) : s_(other.s_ ? other.s_->clone() : nullptr) {} ++ any_string(any_string&&) = default; ++ any_string& operator=(any_string other) // Covers the copy and move assignment ++ { ++ s_.swap(other.s_); ++ return *this; ++ } ++ ++ template ++ void set(const boost::basic_string_view s) ++ { ++ BOOST_ASSERT(!s.empty()); ++ s_.reset(new impl(s)); ++ } ++ ++ template ++ std::basic_string get() const ++ { ++ if(!s_) ++ throw std::bad_cast(); ++ return dynamic_cast&>(*s_).s; ++ } ++ }; ++ ++}}} // namespace boost::locale::detail ++ ++/// \endcond ++ ++#endif +diff --git a/boost/locale/formatting.hpp b/boost/locale/formatting.hpp +index d14e6f69..e3c8619e 100644 +--- a/boost/locale/formatting.hpp ++++ b/boost/locale/formatting.hpp +@@ -8,15 +8,13 @@ + #ifndef BOOST_LOCALE_FORMATTING_HPP_INCLUDED + #define BOOST_LOCALE_FORMATTING_HPP_INCLUDED + ++#include + #include +-#include +-#include + #include + #include + #include + #include + #include +-#include + + #ifdef BOOST_MSVC + # pragma warning(push) +@@ -130,13 +128,13 @@ namespace boost { namespace locale { + template + void date_time_pattern(const std::basic_string& str) + { +- date_time_pattern_set().set(str); ++ datetime_.set(str); + } + /// Get date/time pattern (strftime like) + template + std::basic_string date_time_pattern() const + { +- return date_time_pattern_set().get(); ++ return datetime_.get(); + } + + /// \cond INTERNAL +@@ -144,51 +142,10 @@ namespace boost { namespace locale { + /// \endcond + + private: +- class string_set; +- +- const string_set& date_time_pattern_set() const; +- string_set& date_time_pattern_set(); +- +- class BOOST_LOCALE_DECL string_set { +- public: +- string_set(); +- ~string_set(); +- string_set(const string_set& other); +- string_set& operator=(string_set other); +- void swap(string_set& other); +- +- template +- void set(const boost::basic_string_view s) +- { +- BOOST_ASSERT(!s.empty()); +- delete[] ptr; +- ptr = nullptr; +- type = &typeid(Char); +- size = sizeof(Char) * s.size(); +- ptr = size ? new char[size] : nullptr; +- memcpy(ptr, s.data(), size); +- } +- +- template +- std::basic_string get() const +- { +- if(type == nullptr || *type != typeid(Char)) +- throw std::bad_cast(); +- std::basic_string result(size / sizeof(Char), Char(0)); +- memcpy(&result.front(), ptr, size); +- return result; +- } +- +- private: +- const std::type_info* type; +- size_t size; +- char* ptr; +- }; +- + uint64_t flags_; + int domain_id_; + std::string time_zone_; +- string_set datetime_; ++ detail::any_string datetime_; + }; + + /// \brief This namespace includes all manipulators that can be used on IO streams +diff --git a/libs/locale/src/boost/locale/shared/formatting.cpp b/libs/locale/src/boost/locale/shared/formatting.cpp +index 489d1fd5..457ba782 100644 +--- a/libs/locale/src/boost/locale/shared/formatting.cpp ++++ b/libs/locale/src/boost/locale/shared/formatting.cpp +@@ -7,43 +7,8 @@ + #include + #include + #include "boost/locale/shared/ios_prop.hpp" +-#include +-#include + + namespace boost { namespace locale { +- +- ios_info::string_set::string_set() : type(nullptr), size(0), ptr(nullptr) {} +- ios_info::string_set::~string_set() +- { +- delete[] ptr; +- } +- ios_info::string_set::string_set(const string_set& other) +- { +- if(other.ptr != nullptr) { +- ptr = new char[other.size]; +- size = other.size; +- type = other.type; +- memcpy(ptr, other.ptr, size); +- } else { +- ptr = nullptr; +- size = 0; +- type = nullptr; +- } +- } +- +- void ios_info::string_set::swap(string_set& other) +- { +- std::swap(type, other.type); +- std::swap(size, other.size); +- std::swap(ptr, other.ptr); +- } +- +- ios_info::string_set& ios_info::string_set::operator=(string_set other) +- { +- swap(other); +- return *this; +- } +- + ios_info::ios_info() : flags_(0), domain_id_(0), time_zone_(time_zone::global()) {} + + ios_info::~ios_info() = default; +@@ -105,16 +70,6 @@ namespace boost { namespace locale { + return time_zone_; + } + +- const ios_info::string_set& ios_info::date_time_pattern_set() const +- { +- return datetime_; +- } +- +- ios_info::string_set& ios_info::date_time_pattern_set() +- { +- return datetime_; +- } +- + ios_info& ios_info::get(std::ios_base& ios) + { + return impl::ios_prop::get(ios); +diff --git a/libs/locale/test/test_ios_info.cpp b/libs/locale/test/test_ios_info.cpp +index 9b63aaed..79179a8f 100644 +--- a/libs/locale/test/test_ios_info.cpp ++++ b/libs/locale/test/test_ios_info.cpp +@@ -105,18 +105,6 @@ void test_member_methods() + + info.date_time_pattern(std::string("Pattern")); + TEST_EQ(info.date_time_pattern(), "Pattern"); +- +- info.date_time_pattern(ascii_to("WChar Pattern")); +- TEST_EQ(info.date_time_pattern(), ascii_to("WChar Pattern")); +- TEST_THROWS(info.date_time_pattern(), std::bad_cast); +- +- info.date_time_pattern(ascii_to("Char16 Pattern")); +- TEST_THROWS(info.date_time_pattern(), std::bad_cast); +- TEST_EQ(info.date_time_pattern(), ascii_to("Char16 Pattern")); +- +- info.date_time_pattern(ascii_to("Char32 Pattern")); +- TEST_THROWS(info.date_time_pattern(), std::bad_cast); +- TEST_EQ(info.date_time_pattern(), ascii_to("Char32 Pattern")); + } + } + +@@ -212,8 +200,60 @@ void test_manipulators() + TEST_EQ(info2.date_time_pattern(), L"My TZ"); + } + ++void test_any_string() ++{ ++ boost::locale::detail::any_string s; ++ TEST_THROWS(s.get(), std::bad_cast); ++ TEST_THROWS(s.get(), std::bad_cast); ++ ++ s.set("Char Pattern"); ++ TEST_EQ(s.get(), "Char Pattern"); ++ TEST_THROWS(s.get(), std::bad_cast); ++ ++ s.set(ascii_to("WChar Pattern")); ++ TEST_EQ(s.get(), ascii_to("WChar Pattern")); ++ TEST_THROWS(s.get(), std::bad_cast); ++ ++ s.set(ascii_to("Char16 Pattern")); ++ TEST_EQ(s.get(), ascii_to("Char16 Pattern")); ++ TEST_THROWS(s.get(), std::bad_cast); ++ ++ s.set(ascii_to("Char32 Pattern")); ++ TEST_EQ(s.get(), ascii_to("Char32 Pattern")); ++ TEST_THROWS(s.get(), std::bad_cast); ++ ++#ifndef BOOST_LOCALE_NO_CXX20_STRING8 ++ s.set(ascii_to("Char8 Pattern")); ++ TEST_EQ(s.get(), ascii_to("Char8 Pattern")); ++ TEST_THROWS(s.get(), std::bad_cast); ++#endif ++ ++ boost::locale::detail::any_string s1, s2, empty; ++ s1.set("Char"); ++ s2.set(ascii_to("WChar")); ++ // Copy ctor ++ boost::locale::detail::any_string s3(s1); ++ TEST_EQ(s3.get(), "Char"); ++ TEST_EQ(s1.get(), "Char"); ++ // Ensure deep copy ++ s3.set("Foo"); ++ TEST_EQ(s3.get(), "Foo"); ++ TEST_EQ(s1.get(), "Char"); ++ // Copy assign ++ s3 = s2; ++ TEST_EQ(s3.get(), ascii_to("WChar")); ++ TEST_EQ(s2.get(), ascii_to("WChar")); ++ // Move assign ++ s3 = std::move(s1); ++ TEST_EQ(s3.get(), "Char"); ++ // From empty ++ s3 = empty; ++ TEST_THROWS(s3.get(), std::bad_cast); ++} ++ + void test_main(int /*argc*/, char** /*argv*/) + { ++ test_any_string(); + test_member_methods(); + test_manipulators(); + }