diff --git a/.github/workflows/bvt-appleclang.yml b/.github/workflows/bvt-appleclang.yml index 32f84f1c..ace58802 100644 --- a/.github/workflows/bvt-appleclang.yml +++ b/.github/workflows/bvt-appleclang.yml @@ -7,26 +7,33 @@ jobs: steps: - uses: actions/checkout@v4 + - name: install meson + run: pipx install meson + - name: check compiler versions run: | - clang --version + cc --version xcodebuild -version - - name: build and run test with AppleClang + - name: build and run test with AppleClang on cmake run: | - cmake -B build -GNinja -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE - cmake --build build -j - ctest --test-dir build -j - mkdir build/drop - chmod +x tools/dump_build_env.sh - ./tools/dump_build_env.sh clang++ build/drop/env-info.json + cmake -B build-cmake -GNinja -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE + cmake --build build-cmake -j + ctest --test-dir build-cmake -j + mkdir build-cmake/drop + bash ./tools/dump_build_env.sh c++ build-cmake/drop/env-info.json - - name: run benchmarks + - name: build and run test with AppleClang on meson run: | - build/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build/drop/benchmarking-results.json + meson setup build-meson --buildtype=release -Dtests=enabled -Dbenchmarks=enabled --force-fallback-for=fmt + meson test -C build-meson + meson test -C build-meson --benchmark --test-args=--benchmark_list_tests=true + + - name: run benchmarks + run: build-cmake/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build-cmake/drop/benchmarking-results.json - name: archive benchmarking results uses: actions/upload-artifact@v4 with: name: drop-appleclang - path: build/drop/ + path: build-cmake/drop/ diff --git a/.github/workflows/bvt-clang.yml b/.github/workflows/bvt-clang.yml index 4c62cb41..1706930a 100644 --- a/.github/workflows/bvt-clang.yml +++ b/.github/workflows/bvt-clang.yml @@ -7,35 +7,45 @@ jobs: steps: - uses: actions/checkout@v4 + - name: install meson + run: pipx install meson + - name: install clang run: | - wget https://apt.llvm.org/llvm.sh - chmod +x llvm.sh - sudo ./llvm.sh 21 - sudo apt install -y libc++-21-dev clang-format-21 + curl https://apt.llvm.org/llvm.sh | sudo bash -s -- 21 + sudo apt install -y clang-21 libc++-21-dev clang-format-21 + cat <<'EOF' >> "$GITHUB_ENV" + CC=clang-21 + CXX=clang++-21 + CXXFLAGS=-stdlib=libc++ + EOF - name: check compiler version run: | - clang++-21 --version + "$CXX" --version - - name: build and run test with clang 21 + - name: build and run test with clang 21 on cmake run: | - cmake -B build -GNinja -DCMAKE_C_COMPILER=clang-21 -DCMAKE_CXX_COMPILER=clang++-21 -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=TRUE - mapfile -t FILES < <(find include tests benchmarks build/examples_from_docs tools -type f \( -name '*.h' -o -name '*.ixx' -o -name '*.cpp' \)) + cmake -B build-cmake -GNinja -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=TRUE + mapfile -t FILES < <(find include tests benchmarks build-cmake/examples_from_docs tools -type f \( -name '*.h' -o -name '*.ixx' -o -name '*.cpp' \)) echo "Running clang-format on ${#FILES[@]} files: ${FILES[*]}" clang-format-21 --dry-run --Werror "${FILES[@]}" - cmake --build build -j - ctest --test-dir build -j - mkdir build/drop - chmod +x tools/dump_build_env.sh - ./tools/dump_build_env.sh clang++-21 build/drop/env-info.json + cmake --build build-cmake -j + ctest --test-dir build-cmake -j + mkdir build-cmake/drop + bash ./tools/dump_build_env.sh "$CXX" build-cmake/drop/env-info.json - - name: run benchmarks + - name: build and run test with clang 21 on meson run: | - build/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build/drop/benchmarking-results.json + meson setup build-meson --buildtype=release -Dtests=enabled -Dbenchmarks=enabled --force-fallback-for=fmt + meson test -C build-meson + meson test -C build-meson --benchmark --test-args=--benchmark_list_tests=true + + - name: run benchmarks + run: build-cmake/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build-cmake/drop/benchmarking-results.json - name: archive benchmarking results uses: actions/upload-artifact@v4 with: name: drop-clang - path: build/drop/ + path: build-cmake/drop/ diff --git a/.github/workflows/bvt-compatibility.yml b/.github/workflows/bvt-compatibility.yml index 83f41dfe..6dda7740 100644 --- a/.github/workflows/bvt-compatibility.yml +++ b/.github/workflows/bvt-compatibility.yml @@ -4,63 +4,46 @@ on: jobs: bvt-compatibility: runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + compiler: + - {family: gcc, version: 13, modules: false} + - {family: gcc, version: 14, modules: true} + - {family: clang, version: 16, modules: false} + - {family: clang, version: 17, modules: false} + - {family: clang, version: 18, modules: false} + - {family: clang, version: 19, modules: false} + - {family: clang, version: 20, modules: true} + steps: - uses: actions/checkout@v4 - - name: install compilers - run: | - wget https://apt.llvm.org/llvm.sh - chmod +x llvm.sh - sudo ./llvm.sh 20 - sudo apt install -y gcc-13 g++-13 gcc-14 g++-14 clang-16 clang-17 clang-18 clang-19 libc++-17-dev - - - name: check compiler versions - run: | - g++-13 --version - g++-14 --version - clang++-16 --version - clang++-17 --version - clang++-18 --version - clang++-19 --version - clang++-20 --version - - - name: build and run test with gcc 14 - run: | - cmake -B build-gcc-14 -GNinja -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=TRUE - cmake --build build-gcc-14 -j - ctest --test-dir build-gcc-14 -j - - - name: build and run test with gcc 13 - run: | - cmake -B build-gcc-13 -DCMAKE_C_COMPILER=gcc-13 -DCMAKE_CXX_COMPILER=g++-13 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE - cmake --build build-gcc-13 -j - ctest --test-dir build-gcc-13 -j - - - name: build and run test with clang 20 - run: | - cmake -B build-clang-20 -GNinja -DCMAKE_C_COMPILER=clang-20 -DCMAKE_CXX_COMPILER=clang++-20 -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=TRUE - cmake --build build-clang-20 -j - ctest --test-dir build-clang-20 -j - - name: build and run test with clang 19 + - name: install gcc + if: ${{ matrix.compiler.family == 'gcc' }} run: | - cmake -B build-clang-19 -DCMAKE_C_COMPILER=clang-19 -DCMAKE_CXX_COMPILER=clang++-19 -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE - cmake --build build-clang-19 -j - ctest --test-dir build-clang-19 -j + sudo apt install -y 'g++-${{ matrix.compiler.version }}' + cat <<'EOF' >> "$GITHUB_ENV" + CC=gcc-${{ matrix.compiler.version }} + CXX=g++-${{ matrix.compiler.version }} + EOF - - name: build and run test with clang 18 + - name: install clang + if: ${{ matrix.compiler.family == 'clang' }} run: | - cmake -B build-clang-18 -DCMAKE_C_COMPILER=clang-18 -DCMAKE_CXX_COMPILER=clang++-18 -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE - cmake --build build-clang-18 -j - ctest --test-dir build-clang-18 -j + sudo apt install -y 'clang-${{ matrix.compiler.version }}' 'clang-tools-${{ matrix.compiler.version }}' 'libc++-${{ matrix.compiler.version }}-dev' 'libc++abi-${{ matrix.compiler.version }}-dev' + cat <<'EOF' >> "$GITHUB_ENV" + CC=clang-${{ matrix.compiler.version }} + CXX=clang++-${{ matrix.compiler.version }} + CXXFLAGS=-stdlib=libc++ + EOF - - name: build and run test with clang 17 + - name: check compiler version run: | - cmake -B build-clang-17 -DCMAKE_C_COMPILER=clang-17 -DCMAKE_CXX_COMPILER=clang++-17 -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE - cmake --build build-clang-17 -j - ctest --test-dir build-clang-17 -j + "$CXX" --version - - name: build and run test with clang 16 + - name: build and run test with cmake run: | - cmake -B build-clang-16 -DCMAKE_C_COMPILER=clang-16 -DCMAKE_CXX_COMPILER=clang++-16 -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE - cmake --build build-clang-16 -j - ctest --test-dir build-clang-16 -j + cmake -B build -GNinja -DCMAKE_BUILD_TYPE=Release '-DPROXY_BUILD_MODULES=${{ matrix.compiler.modules }}' + cmake --build build -j + ctest --test-dir build -j diff --git a/.github/workflows/bvt-gcc.yml b/.github/workflows/bvt-gcc.yml index b0909e21..4a5e6138 100644 --- a/.github/workflows/bvt-gcc.yml +++ b/.github/workflows/bvt-gcc.yml @@ -8,30 +8,35 @@ jobs: steps: - uses: actions/checkout@v4 - - name: install dependencies + - name: install cmake and meson run: | apt-get update - apt-get install -y cmake ninja-build + apt-get install -y cmake meson ninja-build - name: check compiler version run: | g++ --version - - name: build and run test with gcc 15 + - name: build and run test with gcc 15 on cmake run: | - cmake -B build -GNinja -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=TRUE - cmake --build build -j - ctest --test-dir build -j - mkdir build/drop + cmake -B build-cmake -GNinja -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=TRUE + cmake --build build-cmake -j + ctest --test-dir build-cmake -j + mkdir build-cmake/drop chmod +x tools/dump_build_env.sh - ./tools/dump_build_env.sh g++ build/drop/env-info.json + ./tools/dump_build_env.sh g++ build-cmake/drop/env-info.json - - name: run benchmarks + - name: build and run test with gcc 15 on meson run: | - build/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build/drop/benchmarking-results.json + meson setup build-meson --buildtype=release -Dtests=enabled -Dbenchmarks=enabled --force-fallback-for=fmt + meson test -C build-meson + meson test -C build-meson --benchmark --test-args=--benchmark_list_tests=true + + - name: run benchmarks + run: build-cmake/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build-cmake/drop/benchmarking-results.json - name: archive benchmarking results uses: actions/upload-artifact@v4 with: name: drop-gcc - path: build/drop/ + path: build-cmake/drop/ diff --git a/.github/workflows/bvt-msvc.yml b/.github/workflows/bvt-msvc.yml index bf38ae2f..fe06cca7 100644 --- a/.github/workflows/bvt-msvc.yml +++ b/.github/workflows/bvt-msvc.yml @@ -7,23 +7,31 @@ jobs: steps: - uses: actions/checkout@v4 + - name: install meson + run: pipx install meson + - name: add cl.exe to PATH uses: ilammy/msvc-dev-cmd@v1 - - name: build and run test with MSVC + - name: build and run test with MSVC on cmake run: | - cmake -B build -DCMAKE_CXX_STANDARD=23 -DPROXY_BUILD_MODULES=TRUE ` - && cmake --build build --config Release -j ` - && ctest --test-dir build -j ` - && mkdir build\drop > $null ` - && .\tools\dump_build_env_msvc.ps1 -OutputPath build\drop\env-info.json + cmake -B build-cmake -DCMAKE_CXX_STANDARD=23 -DPROXY_BUILD_MODULES=TRUE ` + && cmake --build build-cmake --config Release -j ` + && ctest --test-dir build-cmake -j ` + && mkdir build-cmake\drop > $null ` + && .\tools\dump_build_env_msvc.ps1 -OutputPath build-cmake\drop\env-info.json - - name: run benchmarks + - name: build and run test with MSVC on meson run: | - build\benchmarks\Release\msft_proxy_benchmarks.exe --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build\drop\benchmarking-results.json + meson setup build-meson --buildtype=release -Dtests=enabled -Dbenchmarks=enabled --force-fallback-for=fmt --vsenv + meson test -C build-meson + meson test -C build-meson --benchmark --test-args=--benchmark_list_tests=true + + - name: run benchmarks + run: build-cmake\benchmarks\Release\msft_proxy_benchmarks.exe --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build-cmake\drop\benchmarking-results.json - name: archive benchmarking results uses: actions/upload-artifact@v4 with: name: drop-msvc - path: build/drop/ + path: build-cmake/drop/ diff --git a/.github/workflows/bvt-nvhpc.yml b/.github/workflows/bvt-nvhpc.yml index 9a333311..e9c1eea3 100644 --- a/.github/workflows/bvt-nvhpc.yml +++ b/.github/workflows/bvt-nvhpc.yml @@ -10,29 +10,41 @@ jobs: - name: free disk space uses: jlumbroso/free-disk-space@v1.3.1 + # - name: install meson + # # FIXME: install from upstream once https://github.com/mesonbuild/meson/pull/15353 is released + # run: pipx install git+https://github.com/mesonbuild/meson@02b85a846629090a0c7f18e860bab0a10ea4349b + - name: install NVHPC 25.11 run: | curl https://developer.download.nvidia.com/hpc-sdk/ubuntu/DEB-GPG-KEY-NVIDIA-HPC-SDK | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-hpcsdk-archive-keyring.gpg echo 'deb [signed-by=/usr/share/keyrings/nvidia-hpcsdk-archive-keyring.gpg] https://developer.download.nvidia.com/hpc-sdk/ubuntu/amd64 /' | sudo tee /etc/apt/sources.list.d/nvhpc.list sudo apt-get update -y sudo apt-get install -y nvhpc-25-11 + cat<<'EOF' >> "$GITHUB_ENV" + CC=/opt/nvidia/hpc_sdk/Linux_x86_64/25.11/compilers/bin/nvc + CXX=/opt/nvidia/hpc_sdk/Linux_x86_64/25.11/compilers/bin/nvc++ + EOF - - name: build and run test with NVHPC 25.11 + - name: build and run test with NVHPC 25.11 on cmake run: | - PATH=/opt/nvidia/hpc_sdk/Linux_x86_64/25.11/compilers/bin:$PATH; export PATH - cmake -B build -GNinja -DCMAKE_C_COMPILER=nvc -DCMAKE_CXX_COMPILER=nvc++ -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE - cmake --build build -j - ctest --test-dir build -j - mkdir build/drop + cmake -B build-cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE + cmake --build build-cmake -j + ctest --test-dir build-cmake -j + mkdir build-cmake/drop chmod +x tools/dump_build_env.sh - ./tools/dump_build_env.sh nvc++ build/drop/env-info.json + ./tools/dump_build_env.sh "$CXX" build-cmake/drop/env-info.json + + # - name: build and run test with NVHPC 25.11 on meson + # run: | + # meson setup build-meson --buildtype=release -Dtests=enabled -Dbenchmarks=enabled --force-fallback-for=fmt + # meson test -C build-meson + # meson test -C build-meson --benchmark --test-args=--benchmark_list_tests=true - name: run benchmarks - run: | - build/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build/drop/benchmarking-results.json + run: build-cmake/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build-cmake/drop/benchmarking-results.json - name: archive benchmarking results uses: actions/upload-artifact@v4 with: name: drop-nvhpc - path: build/drop/ + path: build-cmake/drop/ diff --git a/.github/workflows/bvt-oneapi.yml b/.github/workflows/bvt-oneapi.yml index 3bc1d43e..86f19141 100644 --- a/.github/workflows/bvt-oneapi.yml +++ b/.github/workflows/bvt-oneapi.yml @@ -7,9 +7,15 @@ jobs: steps: - uses: actions/checkout@v4 + - name: install meson + run: pipx install meson + - name: install libc++ run: | sudo apt-get install -y libc++-19-dev libc++abi-19-dev + cat <<'EOF' >> "$GITHUB_ENV" + CXXFLAGS=-stdlib=libc++ + EOF - name: install intel oneAPI run: | @@ -18,28 +24,36 @@ jobs: sudo apt update sudo apt install -y intel-oneapi-compiler-dpcpp-cpp source /opt/intel/oneapi/setvars.sh - echo "PATH=$PATH" >> $GITHUB_ENV - echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH" >> $GITHUB_ENV + cat<<-EOF >> "$GITHUB_ENV" + PATH=$PATH + LD_LIBRARY_PATH=$LD_LIBRARY_PATH + CC=$(which icx) + CXX=$(which icpx) + EOF - name: check compiler version run: | - icpx --version + "$CXX" --version - - name: build and run test with oneapi + - name: build and run test with oneapi on cmake run: | - cmake -B build -GNinja -DCMAKE_CXX_COMPILER=icpx -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-stdlib=libc++" - cmake --build build -j - ctest --test-dir build -j - mkdir build/drop - chmod +x tools/dump_build_env.sh - ./tools/dump_build_env.sh icpx build/drop/env-info.json + cmake -B build-cmake -GNinja -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release + cmake --build build-cmake -j + ctest --test-dir build-cmake -j + mkdir build-cmake/drop + bash ./tools/dump_build_env.sh "$CXX" build-cmake/drop/env-info.json - - name: run benchmarks + - name: build and run test with oneapi on meson run: | - build/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build/drop/benchmarking-results.json + meson setup build-meson --buildtype=release -Dtests=enabled -Dbenchmarks=enabled --force-fallback-for=fmt + meson test -C build-meson + meson test -C build-meson --benchmark --test-args=--benchmark_list_tests=true + + - name: run benchmarks + run: build-cmake/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build-cmake/drop/benchmarking-results.json - name: archive benchmarking results uses: actions/upload-artifact@v4 with: name: drop-oneapi - path: build/drop/ + path: build-cmake/drop/ diff --git a/.github/workflows/pipeline-release.yml b/.github/workflows/pipeline-release.yml index 67778741..77a9d2c8 100644 --- a/.github/workflows/pipeline-release.yml +++ b/.github/workflows/pipeline-release.yml @@ -17,8 +17,8 @@ jobs: version=$(grep -oP 'msft_proxy\d+\s+VERSION\s+\K[0-9]+\.[0-9]+\.[0-9]+' CMakeLists.txt) git tag "$version" git push origin "$version" - tar -czf "proxy-$version.tgz" $(git ls-files 'include/**.h' 'include/**.ixx') - echo "PRO_VER=$version" >> $GITHUB_OUTPUT + git ls-files 'include/**.h' 'include/**.ixx' | xargs tar -czf "proxy-$version.tgz" + echo "PRO_VER=$version" >> "$GITHUB_OUTPUT" shell: bash - name: create release draft diff --git a/.gitignore b/.gitignore index 631c2447..d2e81b70 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,10 @@ # Ignore build directories build/ Testing/ + +# Python bytecode cache +__pycache__/ + +# Meson subprojects +/subprojects/* +!/subprojects/*.wrap diff --git a/benchmarks/meson.build b/benchmarks/meson.build new file mode 100644 index 00000000..f878c364 --- /dev/null +++ b/benchmarks/meson.build @@ -0,0 +1,13 @@ +benchmark( + 'ProxyBenchmarks', + executable( + 'msft_proxy_benchmarks', + files( + 'proxy_creation_benchmark.cpp', + 'proxy_operation_benchmark.cpp', + 'proxy_operation_benchmark_context.cpp', + ), + implicit_include_directories: false, + dependencies: [msft_proxy4_dep, benchmark_dep], + ), +) diff --git a/docs/meson.build b/docs/meson.build new file mode 100644 index 00000000..1b18b777 --- /dev/null +++ b/docs/meson.build @@ -0,0 +1,39 @@ +extract_doctest_exe = find_program('../tools/meson_extract_doctest.py') + +docs = run_command( + extract_doctest_exe, + meson.current_source_dir(), + capture: true, + check: true, +).stdout().strip('\0').split('\0') + +examples = [] + +foreach doc : docs + deps = [msft_proxy4_dep] + if doc.contains('fmt') + deps += fmt_dep + endif + + name = doc.replace('/', '_').substring(0, -3) + example_exe = executable( + f'example_@name@', + custom_target( + command: [extract_doctest_exe, '@INPUT@', '@OUTPUT@'], + input: doc, + output: f'@name@.cpp', + ), + extra_files: doc, + implicit_include_directories: false, + dependencies: deps, + build_by_default: false, + ) + examples += example_exe + test( + name, + example_exe, + suite: 'ProxyExamples', + ) +endforeach + +alias_target('examples', examples) diff --git a/docs/spec/proxy_view.md b/docs/spec/proxy_view.md index 3fb1c5a0..2f81c5d3 100644 --- a/docs/spec/proxy_view.md +++ b/docs/spec/proxy_view.md @@ -37,6 +37,7 @@ using proxy_view = proxy>; ## Example ```cpp +#include #include #include diff --git a/include/proxy/v4/proxy.h b/include/proxy/v4/proxy.h index 923e41c5..dcebcd3e 100644 --- a/include/proxy/v4/proxy.h +++ b/include/proxy/v4/proxy.h @@ -23,6 +23,10 @@ #if __has_include() #include #endif // __has_include() +#if __cpp_lib_format >= 201907L || \ + (defined(_LIBCPP_VERSION) && _LIBCPP_VERSION >= 170000) +#define PRO4D_HAS_FORMAT +#endif // __cpp_lib_format || _LIBCPP_VERSION >= 170000 #endif // __STDC_HOSTED__ #if __cpp_rtti >= 199711L @@ -2125,7 +2129,7 @@ struct weak_conversion_dispatch : cast_dispatch_base { template using weak_conversion_overload = weak_proxy() const noexcept; -#if __STDC_HOSTED__ && __has_include() +#ifdef PRO4D_HAS_FORMAT template struct format_overload_traits; template <> @@ -2166,7 +2170,7 @@ struct format_dispatch { return impl.format(self, fc); } }; -#endif // __STDC_HOSTED__ && __has_include() +#endif // PRO4D_HAS_FORMAT #if __cpp_rtti >= 199711L struct proxy_cast_context { @@ -2273,7 +2277,7 @@ struct proxy_typeid_reflector { namespace skills { -#if __STDC_HOSTED__ && __has_include() +#ifdef PRO4D_HAS_FORMAT template using format = typename FB::template add_convention using wformat = typename FB::template add_convention>; -#endif // __STDC_HOSTED__ && __has_include() +#endif // PRO4D_HAS_FORMAT #if __cpp_rtti >= 199711L template @@ -2604,7 +2608,7 @@ struct weak_dispatch : D { // == Adapters (std::formatter) == // ============================================================================= -#if __STDC_HOSTED__ && __has_include() +#ifdef PRO4D_HAS_FORMAT namespace std { template @@ -2635,7 +2639,7 @@ struct formatter, CharT> { }; } // namespace std -#endif // __STDC_HOSTED__ && __has_include() +#endif // PRO4D_HAS_FORMAT #undef PROD_UNREACHABLE #undef PROD_NO_UNIQUE_ADDRESS_ATTRIBUTE diff --git a/include/proxy/v4/proxy.ixx b/include/proxy/v4/proxy.ixx index f1ad5985..37fb9811 100644 --- a/include/proxy/v4/proxy.ixx +++ b/include/proxy/v4/proxy.ixx @@ -41,10 +41,10 @@ using v4::weak_proxy; namespace skills { -#if __STDC_HOSTED__ && __has_include() +#ifdef PRO4D_HAS_FORMAT using skills::format; using skills::wformat; -#endif // __STDC_HOSTED__ && __has_include() +#endif // PRO4D_HAS_FORMAT #if __cpp_rtti >= 199711L using skills::direct_rtti; @@ -60,10 +60,10 @@ using skills::slim; } // namespace pro::inline v4 -#if __STDC_HOSTED__ && __has_include() +#ifdef PRO4D_HAS_FORMAT export namespace std { using std::formatter; } // namespace std -#endif // __STDC_HOSTED__ && __has_include() +#endif // PRO4D_HAS_FORMAT diff --git a/meson.build b/meson.build new file mode 100644 index 00000000..24d5a84d --- /dev/null +++ b/meson.build @@ -0,0 +1,118 @@ +project( + 'msft_proxy4', + 'cpp', + version: '4.0.1', + license: 'MIT', + license_files: 'LICENSE', + meson_version: '>=1.3', + default_options: { + 'cpp_std': ['vc++latest', 'c++26', 'c++23', 'c++20'], + 'warning_level': '3', + }, +) + +cxx = meson.get_compiler('cpp') +if cxx.compute_int('__has_cpp_attribute(msvc::no_unique_address)') != 0 and not cxx.compiles( + ''' + struct A {}; + + struct B { + [[msvc::no_unique_address]] A val; + }; + + struct C : B { + char x; + }; + + static_assert(sizeof(C) == 1); + ''', + name: 'msvc::no_unique_address behaves correctly', +) + error( + 'unsupported compiler:', + 'https://github.com/llvm/llvm-project/issues/143245', + ) +endif +if cxx.get_argument_syntax() == 'msvc' + add_project_arguments( + '/bigobj', + language: 'cpp', + ) +endif + +inc = include_directories('include') + +hdrs = files( + 'include/proxy/proxy.h', + 'include/proxy/proxy_fmt.h', + 'include/proxy/proxy_macros.h', +) +hdrs_v4 = files( + 'include/proxy/v4/proxy.h', + 'include/proxy/v4/proxy_fmt.h', + 'include/proxy/v4/proxy_macros.h', +) +hdrs_mod = files('include/proxy/v4/proxy.ixx') + +msft_proxy4_dep = declare_dependency( + sources: hdrs + hdrs_v4, + include_directories: inc, +) + +install_headers( + hdrs, + subdir: 'proxy', +) +install_headers( + hdrs_v4, + subdir: 'proxy/v4', +) +install_headers( + hdrs_mod, + subdir: 'proxy/v4', +) +pkgconfig = import( + 'pkgconfig', + required: false, +) +if pkgconfig.found() + pkgconfig.generate( + name: meson.project_name(), + description: 'Next Generation Polymorphism in C++', + url: 'https://microsoft.github.io/proxy/', + ) +endif + +meson.override_dependency(meson.project_name(), msft_proxy4_dep) + +tests = get_option('tests').disable_auto_if(meson.is_subproject()) +gtest_dep = dependency( + 'gtest_main', + main: true, + required: tests, +) +fmt_dep = dependency( + 'fmt', + version: '>=6.1.0', + required: false, + disabler: true, +) +subdir( + 'tests', + if_found: gtest_dep, +) + +benchmarks = get_option('benchmarks').disable_auto_if(meson.is_subproject()) +benchmark_dep = dependency( + 'benchmark_main', + required: benchmarks, +) +subdir( + 'benchmarks', + if_found: benchmark_dep, +) + +examples = get_option('examples').disable_auto_if(meson.is_subproject()) +if examples.allowed() + subdir('docs') +endif diff --git a/meson.format b/meson.format new file mode 100644 index 00000000..66e2f726 --- /dev/null +++ b/meson.format @@ -0,0 +1,4 @@ +indent_by=' ' +end_of_line=lf +insert_final_newline=true +kwargs_force_multiline=true diff --git a/meson.options b/meson.options new file mode 100644 index 00000000..584693ac --- /dev/null +++ b/meson.options @@ -0,0 +1,18 @@ +option( + 'tests', + type: 'feature', + value: 'auto', + description: 'Build tests', +) +option( + 'benchmarks', + type: 'feature', + value: 'auto', + description: 'Build benchmarks', +) +option( + 'examples', + type: 'feature', + value: 'auto', + description: 'Extract and build examples from docs', +) diff --git a/subprojects/fmt.wrap b/subprojects/fmt.wrap new file mode 100644 index 00000000..65448572 --- /dev/null +++ b/subprojects/fmt.wrap @@ -0,0 +1,13 @@ +[wrap-file] +directory = fmt-12.1.0 +source_url = https://github.com/fmtlib/fmt/archive/12.1.0.tar.gz +source_filename = fmt-12.1.0.tar.gz +source_hash = ea7de4299689e12b6dddd392f9896f08fb0777ac7168897a244a6d6085043fea +source_fallback_url = https://github.com/wrapdb/fmt/releases/download/12.1.0-4/fmt-12.1.0.tar.gz +patch_filename = fmt_12.1.0-4_patch.zip +patch_url = https://github.com/wrapdb/fmt/releases/download/12.1.0-4/fmt_12.1.0-4_patch.zip +patch_hash = 65b7fe3c29f25528011bc295e83e4f6f10028c922407e003b7856bb79789f345 +3rdparty_wrapdb_version = 12.1.0-4 + +[provide] +dependency_names = fmt diff --git a/subprojects/google-benchmark.wrap b/subprojects/google-benchmark.wrap new file mode 100644 index 00000000..7e2e6e73 --- /dev/null +++ b/subprojects/google-benchmark.wrap @@ -0,0 +1,13 @@ +[wrap-file] +directory = benchmark-1.8.4 +source_url = https://github.com/google/benchmark/archive/refs/tags/v1.8.4.tar.gz +source_filename = benchmark-1.8.4.tar.gz +source_hash = 3e7059b6b11fb1bbe28e33e02519398ca94c1818874ebed18e504dc6f709be45 +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/google-benchmark_1.8.4-5/benchmark-1.8.4.tar.gz +patch_filename = google-benchmark_1.8.4-5_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/google-benchmark_1.8.4-5/get_patch +patch_hash = 671ffed65f1e95e8c20edb7a06eb54476797e58169160b255f52dc71f4b83957 +wrapdb_version = 1.8.4-5 + +[provide] +dependency_names = benchmark, benchmark_main diff --git a/subprojects/gtest.wrap b/subprojects/gtest.wrap new file mode 100644 index 00000000..9902a4f7 --- /dev/null +++ b/subprojects/gtest.wrap @@ -0,0 +1,16 @@ +[wrap-file] +directory = googletest-1.17.0 +source_url = https://github.com/google/googletest/archive/refs/tags/v1.17.0.tar.gz +source_filename = googletest-1.17.0.tar.gz +source_hash = 65fab701d9829d38cb77c14acdc431d2108bfdbf8979e40eb8ae567edf10b27c +patch_filename = gtest_1.17.0-4_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/gtest_1.17.0-4/get_patch +patch_hash = 3abf7662d09db706453a5b064a1e914678c74b9d9b0b19382747ca561d0d8750 +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/gtest_1.17.0-4/googletest-1.17.0.tar.gz +wrapdb_version = 1.17.0-4 + +[provide] +gtest = gtest_dep +gtest_main = gtest_main_dep +gmock = gmock_dep +gmock_main = gmock_main_dep diff --git a/tests/meson.build b/tests/meson.build new file mode 100644 index 00000000..fbf36f2c --- /dev/null +++ b/tests/meson.build @@ -0,0 +1,67 @@ +test_srcs = files( + 'proxy_creation_tests.cpp', + 'proxy_dispatch_tests.cpp', + 'proxy_format_tests.cpp', + 'proxy_integration_tests.cpp', + 'proxy_invocation_tests.cpp', + 'proxy_lifetime_tests.cpp', + 'proxy_reflection_tests.cpp', + 'proxy_regression_tests.cpp', + 'proxy_rtti_tests.cpp', + 'proxy_traits_tests.cpp', + 'proxy_view_tests.cpp', +) + +test_deps = [msft_proxy4_dep, gtest_dep] + +if fmt_dep.found() + test_srcs += files( + 'proxy_fmt_format_tests.cpp', + ) + test_deps += fmt_dep +endif + +test( + 'ProxyTests', + executable( + 'msft_proxy_tests', + test_srcs, + implicit_include_directories: false, + dependencies: test_deps, + build_by_default: false, + ), + protocol: 'gtest', +) + +freestanding_cflags = ['-ffreestanding'] +freestanding_ldflags = ['-nodefaultlibs'] + +if cxx.has_multi_arguments( + freestanding_cflags, + freestanding_ldflags, + required: false, +) + libc_dep = cxx.find_library( + 'c', + required: false, + ) + + if libc_dep.found() + test( + 'ProxyFreestandingSupportTests', + executable( + 'msft_proxy_freestanding_tests', + files('freestanding/proxy_freestanding_tests.cpp'), + implicit_include_directories: false, + dependencies: [msft_proxy4_dep, libc_dep], + cpp_args: freestanding_cflags + freestanding_ldflags, + link_args: get_option('b_sanitize') == 'none' ? freestanding_ldflags : [], + override_options: { + 'cpp_eh': 'none', + 'cpp_rtti': false, + }, + build_by_default: false, + ), + ) + endif +endif diff --git a/tests/proxy_dispatch_tests.cpp b/tests/proxy_dispatch_tests.cpp index e2a71aa8..ca7fc85b 100644 --- a/tests/proxy_dispatch_tests.cpp +++ b/tests/proxy_dispatch_tests.cpp @@ -829,6 +829,7 @@ TEST(ProxyDispatchTests, TestFreeAsMemDispatch) { } TEST(ProxyDispatchTests, TestSubstitutionDispatch) { +#ifdef PRO4D_HAS_FORMAT struct Base : pro::facade_builder // ::add_skill // ::build {}; @@ -844,4 +845,7 @@ TEST(ProxyDispatchTests, TestSubstitutionDispatch) { pro::proxy p3 = std::move(p1); ASSERT_FALSE(p1.has_value()); ASSERT_EQ(std::format("{}", *p3), "123"); +#else + GTEST_SKIP() << "std::format not available"; +#endif // PRO4D_HAS_FORMAT } diff --git a/tests/proxy_format_tests.cpp b/tests/proxy_format_tests.cpp index 7b0d2248..89479733 100644 --- a/tests/proxy_format_tests.cpp +++ b/tests/proxy_format_tests.cpp @@ -4,6 +4,7 @@ #include #include +#ifdef PRO4D_HAS_FORMAT namespace proxy_format_tests_details { struct NonFormattable : pro::facade_builder::build {}; @@ -29,17 +30,26 @@ static_assert( } // namespace proxy_format_tests_details namespace details = proxy_format_tests_details; +#endif // PRO4D_HAS_FORMAT TEST(ProxyFormatTests, TestFormat) { +#ifdef PRO4D_HAS_FORMAT int v = 123; pro::proxy p = &v; ASSERT_EQ(std::format("{}", *p), "123"); ASSERT_EQ(std::format("{:*<6}", *p), "123***"); +#else + GTEST_SKIP() << "std::format not available"; +#endif // PRO4D_HAS_FORMAT } TEST(ProxyFormatTests, TestWformat) { +#ifdef PRO4D_HAS_FORMAT int v = 123; pro::proxy p = &v; ASSERT_EQ(std::format(L"{}", *p), L"123"); ASSERT_EQ(std::format(L"{:*<6}", *p), L"123***"); +#else + GTEST_SKIP() << "std::format not available"; +#endif // PRO4D_HAS_FORMAT } diff --git a/tests/proxy_reflection_tests.cpp b/tests/proxy_reflection_tests.cpp index 2041e9a2..5cc90b40 100644 --- a/tests/proxy_reflection_tests.cpp +++ b/tests/proxy_reflection_tests.cpp @@ -17,7 +17,8 @@ struct TraitsReflector { is_copy_constructible_(std::is_copy_constructible_v), is_nothrow_move_constructible_(std::is_nothrow_move_constructible_v), is_nothrow_destructible_(std::is_nothrow_destructible_v), - is_trivial_(std::is_trivial_v) {} + is_trivial_(std::is_trivially_default_constructible_v && + std::is_trivially_copyable_v) {} template struct accessor { diff --git a/tools/extract_example_code_from_docs.py b/tools/extract_example_code_from_docs.py index 405c1dcf..becbb947 100644 --- a/tools/extract_example_code_from_docs.py +++ b/tools/extract_example_code_from_docs.py @@ -1,42 +1,79 @@ -import os +#!/usr/bin/env python3 +# pyright: strict + import re -import sys +from typing import Optional +from pathlib import Path + +EXAMPLE_PATTERN = re.compile(r"## Example\r?\n\r?\n```cpp\r?\n(.*?)\r?\n```", re.DOTALL) + -def extract_cpp_code(md_path, cpp_path): - with open(md_path, 'r', encoding='utf-8') as f: +def extract_cpp_code(md_path: Path) -> Optional[str]: + with open(md_path, "r", encoding="utf-8") as f: content = f.read() - pattern = r'## Example\r?\n\r?\n```cpp\r?\n(.*?)\r?\n```' - code_blocks = re.findall(pattern, content, re.DOTALL) + code_blocks: list[str] = re.findall(EXAMPLE_PATTERN, content) if len(code_blocks) == 0: return # No match, skip elif len(code_blocks) > 1: - raise ValueError(f"File '{md_path}' contains more than one '## Example' C++ code block.") + msg = f"File '{md_path}' contains more than one '## Example' C++ code block." + raise ValueError(msg) cpp_code = code_blocks[0] - header = f"// This file was auto-generated from:\n// {md_path}\n\n" + header = f""" +// This file was auto-generated from: +// {md_path} + +""".lstrip() + + if "pro::skills::format" in cpp_code: + cpp_code = f""" +#include +#ifdef PRO4D_HAS_FORMAT +{cpp_code} +#else +int main() {{ + // std::format not available + return 77; +}} +#endif +""".strip() - with open(cpp_path, 'w', encoding='utf-8') as out: - out.write(header) - out.write(cpp_code) + return header + cpp_code -def main(): - if len(sys.argv) != 3: - print("Usage: python extract_example_code_from_docs.py ") - sys.exit(1) - input_dir = sys.argv[1] - output_dir = sys.argv[2] +def main() -> None: + import argparse + import os + from dataclasses import dataclass + + @dataclass(frozen=True) + class Args: + input_dir: Path + output_dir: Path + + parser = argparse.ArgumentParser() + _ = parser.add_argument("input_dir", type=Path, help="Path to Markdown documents") + _ = parser.add_argument("output_dir", type=Path, help="Source code output path") + args = parser.parse_args(namespace=Args) + + input_dir = args.input_dir + output_dir = args.output_dir for root, _, files in os.walk(input_dir): for file in files: - if file.endswith('.md'): - md_path = os.path.join(root, file) - rel_path = os.path.relpath(md_path, input_dir) - rel_base = os.path.splitext(rel_path)[0].replace(os.sep, '_') - cpp_path = os.path.join(output_dir, f"example_{rel_base}.cpp") - extract_cpp_code(md_path, cpp_path) - -if __name__ == '__main__': + if file.endswith(".md"): + md_path = Path(root) / file + rel_path = md_path.relative_to(input_dir) + rel_base = "_".join([*rel_path.parent.parts, rel_path.stem]) + cpp_path = output_dir / f"example_{rel_base}.cpp" + cpp_code = extract_cpp_code(md_path) + if cpp_code is None: + continue + with open(cpp_path, "w", encoding="utf-8") as f: + _ = f.write(cpp_code) + + +if __name__ == "__main__": main() diff --git a/tools/meson_extract_doctest.py b/tools/meson_extract_doctest.py new file mode 100644 index 00000000..63bfc46e --- /dev/null +++ b/tools/meson_extract_doctest.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# pyright: strict + +import sys +from pathlib import Path + +from extract_example_code_from_docs import extract_cpp_code + +infile = Path(sys.argv[1]) +if infile.is_dir(): + for path in infile.glob("**/*.md"): + if extract_cpp_code(path) is None: + continue + print(str(path.relative_to(infile).as_posix()), end="\0") + exit() + +outfile = Path(sys.argv[2]) + +cpp_code = extract_cpp_code(infile) + +if not cpp_code: + raise ValueError + +with open(outfile, "w") as f: + _ = f.write(cpp_code)