diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 705ad446..df61ab65 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -97,6 +97,37 @@ jobs: cmake --build ../build --target tests -j $(nproc) ctest --test-dir ../build -j $(nproc) --output-on-failure + flat-headers: + name: Flat headers + runs-on: ubuntu-24.04 + steps: + # ce/reflection.cpp needs C++26 reflection; dev/check-flat.sh finds this + # compiler on its own, and reports the example as skipped without it. + - name: Install GCC 16 + run: | + sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test + sudo apt-get update + sudo apt-get install -y g++-16 + + - name: Clone Boost.OpenMethod + uses: actions/checkout@v4 + + - name: Clone Boost + uses: alandefreitas/cpp-actions/boost-clone@v1.8.8 + with: + branch: ${{ (github.ref_name == 'master' && github.ref_name) || 'develop' }} + boost-dir: ../boost-source + scan-modules-dir: . + scan-modules-ignore: openmethod + + # Each flattened header must compile after boost/openmethod.hpp, and fail + # with our own diagnostic without it; the ce/ examples must build and run + # against the generated tree. + - name: Check the flat headers + env: + BOOST_SRC_DIR: ${{ github.workspace }}/../boost-source + run: dev/check-flat.sh + antora: name: Antora docs strategy: @@ -156,6 +187,17 @@ jobs: exit 1 fi + # Published alongside the docs, at the root of the site, so that a + # Compiler Explorer example can include them by URL. See ce/README.md. + - name: Build flat headers + if: matrix.os == 'ubuntu-latest' + # The base URL goes into the banner and the guard-check messages of + # every generated header, so it has to name the site this artifact is + # about to be deployed to - jll63's fork, or boostorg. + run: | + python3 dev/flatten.py --output-dir doc/html \ + --base-url "https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}" + - name: Create Antora docs artifact uses: actions/upload-artifact@v4 with: @@ -168,9 +210,14 @@ jobs: with: path: doc/html - - name: Deploy to GitHub Pages (jll63) + # Upstream deploys from develop only - every other branch would clobber + # the site. The fork has no such restriction: it is where a branch is + # tried out before it is merged. + - name: Deploy to GitHub Pages if: >- matrix.os == 'ubuntu-latest' - && github.repository == 'jll63/openmethod' && github.event_name != 'pull_request' + && (github.repository == 'jll63/openmethod' + || (github.repository == 'boostorg/openmethod' + && github.ref_name == 'develop')) uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index cae127aa..87dd85ca 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,6 @@ cov-int/** # Personal/developer scratch files, not project content. .claude/settings.json notes.txt + +# Flattened headers for Compiler Explorer (dev/flatten.py). +flat/ diff --git a/CLAUDE.md b/CLAUDE.md index 690de73e..82450ca3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -491,6 +491,53 @@ carries the override on the file's behalf (`test_capture_errors.hpp`). Add anoth and the scan has to learn about it: miss one and the file still compiles, binds to `default_registry`, and fails at run time. +### Flattened headers for Compiler Explorer + +`dev/flatten.py` rewrites every public header into a self-sufficient file under `flat/`; the +`antora` CI job regenerates them into the Pages artifact, so they are served from the root of the +site and a CE example can include them by URL. CE fetches those includes client-side, which is why +the host has to send CORS headers - GitHub Pages does, `access-control-allow-origin: *`. + +**Upstream deploys from `develop` only; the fork deploys from any branch.** Any other branch would +clobber the site, and the fork is where a branch is tried out before it is merged - which is why +the `Deploy to GitHub Pages` step tests the repository *and*, for boostorg, the ref. The two sites +are `https://boostorg.github.io/openmethod` and `https://jll63.github.io/openmethod`, and CI +derives `--base-url` from `github.repository_owner` rather than hardcoding either: that URL is +baked into every generated header's banner and guard-check messages, so it has to name the site +the artifact is about to be deployed to. The script's own default is the fork. + +The point of the exercise is that a CE example's include list matches a local one line for line: + +```cpp +#include +#include +``` + +`boost/openmethod.hpp` is the root and carries its whole closure. **Every other header carries +only what the root does not provide** - its `detail/` headers, and `interop/virtual_any.hpp`, +which nothing includes directly. A dependency the root *does* provide becomes a guard check: + +```cpp +#ifndef BOOST_OPENMETHOD_CORE_HPP +#error ": #include <.../boost/openmethod.hpp> first" +#endif +``` + +so a missing root fails on one line instead of a wall of undeclared identifiers. Guard names are +read from the header being flattened, never hardcoded - several are legacy and do not match their +path (`initialize.hpp` is `BOOST_OPENMETHOD_COMPILER_HPP`, `preamble.hpp` is +`BOOST_OPENMETHOD_REGISTRY_HPP`, `policies/static_rtti.hpp` is +`BOOST_OPENMETHOD_POLICY_MINIMAL_RTTI_HPP`). + +The rewriting is line-oriented, which holds only because no `#include ` in +the tree sits inside an `#if`. A `//!` doc comment containing one is left alone - the regex is +anchored at the start of the line. + +`dev/check-flat.sh` (the `flat-headers` CI job, and `BOOST_SRC_DIR=... dev/check-flat.sh` locally) +compiles each generated header after the root, checks that each one *fails* on its own, and builds +and runs the `ce/*.cpp` examples against the generated tree. Those examples are also ordinary +tests in the CMake build (`ce/CMakeLists.txt`), so they cannot rot silently. + ### Custom RTTI When `` is unavailable or insufficient, use static_rtti or implement custom RTTI. See `doc/modules/ROOT/examples/custom_rtti/` and policies in `include/boost/openmethod/policies/`. diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f119dfd..47ea21b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -325,5 +325,7 @@ if (BOOST_OPENMETHOD_BUILD_TESTS) add_subdirectory(doc/modules/ROOT/examples) # Sources behind the `include:` markers in the reference doc comments. add_subdirectory(doc/modules/ROOT/snippets) + # Examples published on Compiler Explorer, see ce/README.md. + add_subdirectory(ce) endif () endif () diff --git a/ce/2-method.cpp b/ce/2-method.cpp index 4be0a11d..59f9d2cb 100644 --- a/ce/2-method.cpp +++ b/ce/2-method.cpp @@ -21,6 +21,8 @@ struct Cat : Animal { BOOST_OPENMETHOD_CLASSES(Animal, Dog, Cat); +using boost::openmethod::virtual_; + BOOST_OPENMETHOD( meet, (virtual_, virtual_, std::ostream&), void); diff --git a/ce/CMakeLists.txt b/ce/CMakeLists.txt index 60739a87..039b772c 100644 --- a/ce/CMakeLists.txt +++ b/ce/CMakeLists.txt @@ -1,28 +1,25 @@ # Copyright (c) 2017-2026 Jean-Louis Leroy # Distributed under the Boost Software License, Version 1.0. -# See accompanying filce_e LICENSE_1_0.txt -# or copy at hce_ttp://www.boost.oce_rg/LICENSE_1_0.txt) - -add_executable(ce_virtual virtual.cpp) -add_test(NAME ce_virtual COMMAND ce_virtual) - -add_executable(ce_uni-method uni-method.cpp) -add_test(NAME ce_uni-method COMMAND ce_uni-method) - -add_executable(ce_uni-method-vptr uni-method-vptr.cpp) -add_test(NAME ce_uni-method-vptr COMMAND ce_uni-method-vptr) - -add_executable(ce_virtual-double virtual-double.cpp) -add_test(NAME ce_virtual-double COMMAND ce_virtual-double) - -add_executable(ce_2-method 2-method.cpp) -add_test(NAME ce_2-method COMMAND ce_2-method) - -add_executable(ce_2-method-vptr 2-method-vptr.cpp) -add_test(NAME ce_2-method-vptr COMMAND ce_2-method-vptr) - -add_executable(ce_2-method-vptr-final 2-method-vptr-final.cpp) -add_test(NAME ce_2-method-vptr-fince_al COMMAND ce_2-method-vptr-final) - -add_executable(ce_uni-method-vptr-final uni-method-vptr-final.cpp) -add_test(NAME ce_uni-method-vptr-fce_inal COMMAND ce_uni-method-vptr-final) +# See accompanying file LICENSE_1_0.txt +# or copy at http://www.boost.org/LICENSE_1_0.txt) + +message(STATUS "Boost.OpenMethod: building Compiler Explorer examples") + +file(GLOB cpp_files "*.cpp") + +# reflection.cpp registers its classes by reflection, and has no fallback: +# BOOST_OPENMETHOD_REGISTER_CLASSES expands to nothing without C++26 reflection, +# and no class is registered at all. +if (NOT BOOST_OPENMETHOD_ENABLE_REFLECTION) + list(REMOVE_ITEM cpp_files "${CMAKE_CURRENT_SOURCE_DIR}/reflection.cpp") +endif() + +foreach (cpp ${cpp_files}) + get_filename_component(stem ${cpp} NAME_WE) + set(test_target "boost_openmethod-ce-${stem}") + add_executable(${test_target} ${cpp}) + boost_openmethod_enable_reflection(${test_target}) + target_link_libraries(${test_target} PRIVATE Boost::openmethod) + add_test(NAME ${test_target} COMMAND ${test_target}) + add_dependencies(tests ${test_target}) +endforeach() diff --git a/ce/README.md b/ce/README.md index 46c9a73c..d8071638 100644 --- a/ce/README.md +++ b/ce/README.md @@ -1,26 +1,54 @@ -# YOMM2 on Compiler Explorer +# Boost.OpenMethod on Compiler Explorer -YOMM2 is available on Compiler Explorer. Make sure that you also select Boost -version 1.74 or above, and you probably want to add the `-O3 -DNDEBUG` compiler -switches. +Compiler Explorer can include a header from a URL, but only one file at a time: +it does not resolve the includes inside the file it fetches. `dev/flatten.py` +thus rewrites each public header into a self-sufficient one, and CI publishes +them at the root of , next to the +documentation. -The following examples are available: +An example on Compiler Explorer therefore includes exactly what it would +include locally, one line per header, in the same order: -* The [examples](https://jll63.github.io/yomm2/ce/slides.html) from the slides. -* The matrix example from the GitHub langing page. +```cpp +#include +#include +``` -The following examples use the diff mode to compare open methods with the -equivalent (closed) virtual function based approaches. +Every path under `include/boost/` is available under that URL - the interops, +the policies, `inplace_vptr.hpp`. Two things to know: -* [virtual function call vs uni-method call via plain reference](https://jll63.github.io/yomm2/ce/vf-vs-1m-ref.html) -* [virtual function call vs uni-method call via virtual_ptr ](https://jll63.github.io/yomm2/ce/vf-vs-1m-vptr.html) -* [double dispatch vs multi-method call via plain reference](https://jll63.github.io/yomm2/ce/2d-vs-2m-ref.html) -* [double dispatch vs multi-method call via virtual_ptr ](https://jll63.github.io/yomm2/ce/2d-vs-2m-vptr.html) +* `boost/openmethod.hpp` comes first. It is the only self-contained file; the + others check that it has been included and stop with an `#error` otherwise. +* Select a Boost version in the *Libraries* dropdown. The flattened headers + still include Boost.Mp11, Boost.DynamicBitset and the rest from Boost itself. -YOMM2 can also [add polymorphic operations to non-polymorphic -classes](https://jll63.github.io/yomm2/ce/vptr-final.html). +`-std=c++17 -O3 -DNDEBUG` is a good set of options to look at the generated +code. -When `virtual_ptr` is used in combination with generated static offsets, method -dispatch matches the speed of virtual functions. It is also possible to generate -dispatch data that can be installed without calling `update`, a fairly expensive -operaiton. See [this example](https://jll63.github.io/yomm2/ce/generator.html). +## Reflection + +`reflection.cpp` registers the classes by C++26 reflection (P2996). It includes +the same two headers as the other examples, but needs a compiler that +implements P2996 - *x86-64 gcc (trunk)* on Compiler Explorer - and +`-std=c++26 -freflection`. + +`Bulldog` is the point of the example. No overrider mentions it, and no +`BOOST_OPENMETHOD_CLASSES` lists it; the scan started by +`BOOST_OPENMETHOD_REGISTER_CLASSES()` finds it deriving from `Dog`, registers +it, and `poke` dispatches it to the overrider for `Dog`. + +On Compiler Explorer: + +## The sources + +The sources in this directory are the examples published on Compiler Explorer. +They are built and run as part of the test suite, and again against the +flattened headers by `dev/check-flat.sh`, so a broken flattening is caught +before it reaches the site. + +To generate the headers locally: + +```bash +python3 dev/flatten.py # writes flat/boost/... +BOOST_SRC_DIR=/path/to/boost dev/check-flat.sh +``` diff --git a/ce/reflection.cpp b/ce/reflection.cpp new file mode 100644 index 00000000..83962d64 --- /dev/null +++ b/ce/reflection.cpp @@ -0,0 +1,57 @@ +#include +#include +#include +#include + +struct Animal { + const char* name; + Animal(const char* name) : name(name) { + } + virtual ~Animal() { + } +}; + +struct Dog : Animal { + using Animal::Animal; +}; + +struct Cat : Animal { + using Animal::Animal; +}; + +// Named nowhere else: no overrider, no BOOST_OPENMETHOD_CLASSES. Only the scan +// finds it. +struct Bulldog : Dog { + using Dog::Dog; +}; + +using boost::openmethod::virtual_; + +BOOST_OPENMETHOD(poke, (virtual_, std::ostream&), void); + +BOOST_OPENMETHOD_OVERRIDE(poke, (Cat & animal, std::ostream& os), void) { + os << animal.name << " hisses.\n"; +} + +BOOST_OPENMETHOD_OVERRIDE(poke, (Dog & animal, std::ostream& os), void) { + os << animal.name << " barks.\n"; +} + +BOOST_OPENMETHOD_REGISTER_CLASSES(); + +void poke_animals(const std::vector& animals, std::ostream& os) { + for (auto animal : animals) { + poke(*animal, os); + } +} + +auto main() -> int { + boost::openmethod::initialize(); + + Dog snoopy{"Snoopy"}; + Cat felix{"Felix"}; + Bulldog hector{"Hector"}; + std::vector animals = {&snoopy, &felix, &hector}; + + poke_animals(animals, std::cout); +} diff --git a/ce/uni-method.cpp b/ce/uni-method.cpp index 0ce2675a..c4e24738 100644 --- a/ce/uni-method.cpp +++ b/ce/uni-method.cpp @@ -21,6 +21,8 @@ struct Cat : Animal { BOOST_OPENMETHOD_CLASSES(Animal, Dog, Cat); +using boost::openmethod::virtual_; + BOOST_OPENMETHOD(poke, (virtual_, std::ostream&), void); BOOST_OPENMETHOD_OVERRIDE(poke, (Cat & animal, std::ostream& os), void) { diff --git a/dev/check-flat.sh b/dev/check-flat.sh new file mode 100755 index 00000000..df848ad7 --- /dev/null +++ b/dev/check-flat.sh @@ -0,0 +1,149 @@ +#!/bin/bash +# Copyright (c) 2017-2026 Jean-Louis Leroy +# Distributed under the Boost Software License, Version 1.0. +# See accompanying file LICENSE_1_0.txt +# or copy at http://www.boost.org/LICENSE_1_0.txt) + +# Check the headers generated by dev/flatten.py: each one must compile after the +# root, fail with our own diagnostic without it, and the ce/ examples must build +# and run against the generated tree. +# +# Usage: BOOST_SRC_DIR=/path/to/boost dev/check-flat.sh [output-dir] + +set -uo pipefail + +root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +flat_dir="${1:-$root_dir/flat}" +boost_dir="${BOOST_SRC_DIR:-$root_dir/../..}" +cxx="${CXX:-g++}" +cxxstd="${CXXSTD:--std=c++17}" +work="$(mktemp -d)" +trap 'rm -rf "$work"' EXIT + +# A modular tree (libs/*/include) is what CI clones; a monolithic one has the +# headers under boost/. Prefer the former: the latter can be a stale b2 headers +# tree. +if [ -d "$boost_dir/libs" ]; then + boost_includes=() + for dir in "$boost_dir"/libs/*/include "$boost_dir"/libs/numeric/*/include; do + [ -d "$dir" ] && boost_includes+=("-I$dir") + done +else + boost_includes=("-I$boost_dir") +fi + +python3 "$root_dir/dev/flatten.py" \ + --include-dir "$root_dir/include" --output-dir "$flat_dir" > /dev/null || exit 1 + +compile=("$cxx" "$cxxstd" "-I$flat_dir" "${boost_includes[@]}") +status=0 + +report() { + printf '%-6s %s\n' "$1" "$2" + [ "$1" = FAIL ] && status=1 + return 0 +} + +# ce/reflection.cpp needs C++26 reflection; everything else is C++17. Same +# probe as CMakeLists.txt: the standard alone is not enough, GCC wants a flag. +probe_reflection() { + cat > "$work/probe.cpp" <<'PROBE' +#include +struct Base {}; +struct Derived : Base {}; +consteval auto count() -> int { + return static_cast( + std::meta::bases_of( + ^^Derived, std::meta::access_context::unchecked()).size()); +} +static_assert(count() == 1); +int main() {} +PROBE + + for candidate in "${CXX_REFLECTION:-}" "$cxx" g++-16; do + [ -n "$candidate" ] || continue + command -v "$candidate" > /dev/null || continue + + for flags in "-std=c++26 -freflection" "-std=c++26"; do + # shellcheck disable=SC2086 + if "$candidate" $flags -fsyntax-only "$work/probe.cpp" \ + > /dev/null 2>&1; then + # shellcheck disable=SC2086 + reflection_compile=("$candidate" $flags) + return 0 + fi + done + done + + return 1 +} + +reflection_compile=() + +if probe_reflection; then + report note "C++26 reflection: ${reflection_compile[*]}" +fi + +cd "$flat_dir" || exit 1 + +for header in $(find boost -name '*.hpp' | sort); do + if [ "$header" = boost/openmethod.hpp ]; then + printf '#include <%s>\n' "$header" > "$work/tu.cpp" + else + printf '#include \n#include <%s>\n' "$header" \ + > "$work/tu.cpp" + fi + + if "${compile[@]}" -fsyntax-only "$work/tu.cpp" > "$work/log" 2>&1; then + report ok "$header" + else + report FAIL "$header" + head -20 "$work/log" + fi + + grep -q '^#error' "$header" || continue + + # Without the root, the guard checks must fire. + printf '#include <%s>\n' "$header" > "$work/tu.cpp" + + if "${compile[@]}" -fsyntax-only "$work/tu.cpp" > "$work/log" 2>&1; then + report FAIL "$header (alone: expected an error)" + elif grep -q 'openmethod.hpp> first' "$work/log"; then + report ok "$header (alone)" + else + report FAIL "$header (alone: not our diagnostic)" + head -20 "$work/log" + fi +done + +cd "$root_dir" || exit 1 + +for source in ce/*.cpp; do + example="$work/$(basename "$source" .cpp)" + example_compile=("${compile[@]}") + + if [ "$source" = ce/reflection.cpp ]; then + if [ ${#reflection_compile[@]} -eq 0 ]; then + report skip "$source (no C++26 reflection)" + continue + fi + + example_compile=( + "${reflection_compile[@]}" "-I$flat_dir" "${boost_includes[@]}") + fi + + if ! "${example_compile[@]}" -O2 -o "$example" "$source" \ + > "$work/log" 2>&1; then + report FAIL "$source" + head -20 "$work/log" + continue + fi + + if "$example" > /dev/null; then + report ok "$source" + else + report FAIL "$source (run)" + fi +done + +exit $status diff --git a/dev/flatten.py b/dev/flatten.py old mode 100644 new mode 100755 index 01c98055..e2b70027 --- a/dev/flatten.py +++ b/dev/flatten.py @@ -1,47 +1,158 @@ -#!/usr/bin/python3 +#!/usr/bin/env python3 +# Copyright (c) 2017-2026 Jean-Louis Leroy +# Distributed under the Boost Software License, Version 1.0. +# See accompanying file LICENSE_1_0.txt +# or copy at http://www.boost.org/LICENSE_1_0.txt) + +"""Generate flattened copies of the public headers, for Compiler Explorer. + +CE can include a header from a URL, but only one file at a time: it does not +resolve the includes inside the file it fetches. Each public header thus becomes +a self-sufficient file, so that a CE example includes exactly what a local +example includes, one line per header: + + #include + #include + +`boost/openmethod.hpp` is the root: it carries its entire closure. Every other +header carries only what the root does not already provide - its `detail/` +headers, `interop/virtual_any.hpp` - and replaces the rest with a check on the +include guard, so that including it without the root fails with a diagnostic +instead of a wall of undeclared identifiers. +""" import argparse -from pathlib import Path import re +import subprocess +from pathlib import Path -parser = argparse.ArgumentParser() -parser.add_argument("output", type=Path) -parser.add_argument("input", nargs="+", type=Path) -args = parser.parse_args() +ROOT = "boost/openmethod.hpp" +DETAIL = "boost/openmethod/detail/" +INCLUDE = re.compile(r"#include <(boost/openmethod(?:/[^>]+)?\.hpp)>") +GUARD = re.compile(r"#ifndef (\w+)$") -prefix = args.input[0].absolute() -while prefix.name != "boost": - assert prefix.parent != prefix - prefix = prefix.parent +class Flattener: + def __init__(self, include_dir, base_url, revision): + self.include_dir = include_dir + self.base_url = base_url.rstrip("/") + self.revision = revision + # What the root already brings in, minus the `detail/` headers, which + # are small and are copied into every header that needs them. + self.provided = { + header + for header in self.dependencies(ROOT) + if not header.startswith(DETAIL) + } -prefix = prefix.parent -skip = len(str(prefix)) + 1 + def read(self, header): + return (self.include_dir / header).read_text() + def guard_of(self, header): + lines = self.read(header).splitlines() -def flatten(input, output, done): - header = str(input)[skip:] - if header in done: - return - done.add(header) - with input.open() as ifh: - for line in ifh: - if input.name != "openmethod.hpp" and re.match( - r"#include <(boost/openmethod/core\.hpp+)>", line - ): - continue + for line, next_line in zip(lines, lines[1:]): + if (m := GUARD.match(line)) and next_line == f"#define {m[1]}": + return m[1] + + raise SystemExit(f"{header}: no include guard") + + def dependencies(self, header, found=None): + found = set() if found is None else found + + for dep in INCLUDE.findall(self.read(header)): + if dep not in found: + found.add(dep) + self.dependencies(dep, found) + + return found + + def headers(self): + return [ROOT] + sorted( + header + for path in (self.include_dir / "boost/openmethod").rglob("*.hpp") + if not ( + header := path.relative_to(self.include_dir).as_posix() + ).startswith(DETAIL) + ) + + def write(self, header, output): + if header == ROOT: + note = "// This file is self-contained.\n" + else: + note = f"// #include <{self.base_url}/{ROOT}> first.\n" + + output.write( + f"// <{header}>, flattened for Compiler Explorer.\n" + f"{note}" + "//\n" + "// Generated by dev/flatten.py from Boost.OpenMethod" + f" {self.revision}. Do not edit.\n" + "// See https://github.com/boostorg/openmethod\n\n" + ) + + # The root carries everything; the others require what it provides. + provided = set() if header == ROOT else self.provided + self.copy(header, output, provided, header, set()) + + def copy(self, header, output, provided, top, done): + if header in done: + return + + done.add(header) + + for line in self.read(header).splitlines(keepends=True): + if m := INCLUDE.match(line): + dep = m[1] + + if dep in provided: + output.write( + f"#ifndef {self.guard_of(dep)}\n" + f'#error "<{top}>:' + f" #include <{self.base_url}/{ROOT}> first\"\n" + "#endif\n" + ) + else: + output.write("\n") + self.copy(dep, output, provided, top, done) + output.write("\n") - if m := re.match(r"#include <(boost/openmethod/[^>]+)>", line): - include = m[1] - print(file=output) - flatten(prefix / include, output, done) - print(file=output) continue output.write(line) -with args.output.open("w") as ofh: - done = set() - for input in args.input: - flatten(input.absolute(), ofh, done) +def revision(): + try: + return subprocess.run( + ["git", "describe", "--always", "--dirty"], + capture_output=True, + check=True, + text=True, + ).stdout.strip() + except (OSError, subprocess.CalledProcessError): + return "(unknown revision)" + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--include-dir", type=Path, default=Path("include")) + parser.add_argument("--output-dir", type=Path, default=Path("flat")) + parser.add_argument( + "--base-url", default="https://jll63.github.io/openmethod" + ) + args = parser.parse_args() + + flattener = Flattener(args.include_dir, args.base_url, revision()) + + for header in flattener.headers(): + path = args.output_dir / header + path.parent.mkdir(parents=True, exist_ok=True) + + with path.open("w") as output: + flattener.write(header, output) + + print(path) + + +main() diff --git a/dev/flatten.sh b/dev/flatten.sh deleted file mode 100755 index cef33e6d..00000000 --- a/dev/flatten.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -mkdir -p build_outputs_folder/boost/openmethod - -python3 dev/flatten.py build_outputs_folder/boost/openmethod.hpp \ - include/boost/openmethod.hpp - -for header in core compiler shared_ptr unique_ptr; do - python3 dev/flatten.py "build_outputs_folder/boost/openmethod/$header.hpp" \ - "include/boost/openmethod/$header.hpp" -done diff --git a/dev/local-flat.sh b/dev/local-flat.sh deleted file mode 100755 index 33ea9172..00000000 --- a/dev/local-flat.sh +++ /dev/null @@ -1,12 +0,0 @@ -mkdir -p flat/boost/openmethod -python3 dev/flatten.py \ - flat/boost/openmethod.hpp \ - include/boost/openmethod.hpp \ - include/boost/openmethod/interop/std_unique_ptr.hpp \ - include/boost/openmethod/interop/std_shared_ptr.hpp \ - include/boost/openmethod/initialize.hpp -python3 dev/flatten.py \ - flat/boost/openmethod/registry.hpp \ - include/boost/openmethod/registry.hpp \ - include/boost/openmethod/policies/*.hpp \ - include/boost/openmethod/default_registry.hpp