diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8a6c241 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Preserve upstream document bytes and original whitespace on every platform. +upstream/** -text -whitespace diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e5721ea --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,88 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + mcpp: + name: mcpp (${{ matrix.os }}, ${{ matrix.toolchain }}${{ matrix.target && format(', {0}', matrix.target) || '' }}) + strategy: + fail-fast: false + matrix: + include: + - { os: ubuntu-24.04, toolchain: 'gcc@16.1.0' } + - { os: ubuntu-24.04, toolchain: 'gcc@16.1.0', target: x86_64-linux-musl } + - { os: ubuntu-24.04, toolchain: 'llvm@23.1.3' } + - { os: macos-15, toolchain: 'llvm@23.1.3' } + - { os: windows-2025, toolchain: 'llvm@23.1.3' } + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + defaults: + run: + shell: bash + env: + XLINGS_NON_INTERACTIVE: 1 + TARGET: ${{ matrix.target && format('--target {0}', matrix.target) || '' }} + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Mwrap identity and bilingual README contract + run: python3 tools/check_mwrap_release.py + + - name: Install Xlings (Linux/macOS) + if: runner.os != 'Windows' + run: | + curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/refs/heads/main/tools/other/quick_install.sh | bash + echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH" + + - name: Install Xlings (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + irm https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.ps1 | iex + "$env:USERPROFILE\.xlings\subos\current\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + + - name: Install project tools (.xlings.json) + run: | + for attempt in 1 2 3; do + xlings update > /dev/null 2>&1 || true + if xlings install; then break; fi + [ "$attempt" = 3 ] && exit 1 + sleep 30 + done + mcpp --version + + - name: Select the toolchain + run: | + name="${{ matrix.toolchain }}" + mcpp toolchain install "${name%@*}" "${name#*@}" + mcpp toolchain default "$name" + + - name: Build the modules + run: mcpp build $TARGET + + - name: Tests (buffers, streams, filter chains, .lzma, checks, errors, the constants, every function linked, header mixing) + run: mcpp test $TARGET + + - name: Export audit (the headers as clang reads them on this host) + if: startsWith(matrix.toolchain, 'llvm') + run: python3 tools/generate.py --check + + - name: Upstream's test files decoded by the same library + if: ${{ !matrix.target }} + run: python3 tools/run_conformance.py --toolchain "${{ matrix.toolchain }}" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..f0c6447 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,49 @@ +name: Release +on: + push: + tags: ["mwrap-*"] +permissions: + contents: read +jobs: + publish: + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: write + actions: read + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Check tag, upstream version and bilingual entry pages + run: python3 tools/check_mwrap_release.py --tag "$GITHUB_REF_NAME" + - name: Require successful source CI at this exact commit + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail + result=$(gh run list --repo "$GITHUB_REPOSITORY" --workflow ci.yml --commit "$GITHUB_SHA" --limit 1 --json status,conclusion --jq '.[0] | .status == "completed" and .conclusion == "success"') + test "$result" = true || { echo 'No successful source CI at the tag commit'; exit 1; } + - name: Build immutable source asset and publish + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail + version="${GITHUB_REF_NAME#mwrap-}" + repo="${GITHUB_REPOSITORY#*/}" + asset="${repo}-${version}.tar.gz" + git archive --format=tar.gz --prefix="${repo}-${version}/" "$GITHUB_SHA" > "$RUNNER_TEMP/$asset" + (cd "$RUNNER_TEMP" && sha256sum "$asset" > "$asset.sha256") + if gh release view "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then + echo 'Release already exists; refusing to change immutable assets'; exit 1 + fi + extra=() + if [[ "$version" == *-rc* ]]; then extra+=(--prerelease); fi + gh release create "$GITHUB_REF_NAME" "$RUNNER_TEMP/$asset" "$RUNNER_TEMP/$asset.sha256" \ + --repo "$GITHUB_REPOSITORY" --verify-tag --title "$repo $version" \ + --notes "Module wrapper for upstream $version. New mwrap package identities become installable after mcpp-index publication; see the bilingual README for the tested support scope." "${extra[@]}" diff --git a/.github/workflows/templates.yml b/.github/workflows/templates.yml new file mode 100644 index 0000000..4bcfe74 --- /dev/null +++ b/.github/workflows/templates.yml @@ -0,0 +1,28 @@ +name: Actual project templates +on: + push: + branches: [main] + pull_request: + workflow_dispatch: +permissions: + contents: read +concurrency: + group: templates-${{ github.ref }} + cancel-in-progress: true +env: + MCPP_VERSION: "2026.11.5.1" +jobs: + templates: + runs-on: ubuntu-24.04 + timeout-minutes: 60 + steps: + - uses: actions/checkout@v4 + - name: Install the pinned mcpp + shell: bash + run: | + archive="mcpp-${MCPP_VERSION}-linux-x86_64.tar.gz" + curl -L -fsS -o "$archive" "https://github.com/mcpp-community/mcpp/releases/download/v${MCPP_VERSION}/$archive" + tar -xzf "$archive" + echo "$PWD/mcpp-${MCPP_VERSION}-linux-x86_64/bin" >> "$GITHUB_PATH" + - name: Actual scaffold, strict build and run of the default template + run: python3 tools/test_template.py --toolchain gcc@16.1.0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7c7b405 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +target/ +.mcpp/ +mcpp.lock +compile_commands.json +__pycache__/ +.xlings/ diff --git a/.xlings.json b/.xlings.json new file mode 100644 index 0000000..0ee91fd --- /dev/null +++ b/.xlings.json @@ -0,0 +1,5 @@ +{ + "workspace": { + "xim:mcpp": "2026.11.5.1" + } +} diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..ae4bdef --- /dev/null +++ b/NOTICE @@ -0,0 +1,14 @@ +xz-m: C++ module wrapper +Copyright (c) 2026 mcpp-community contributors + +The wrapper contributions are licensed under the Apache License, Version 2.0. +Upstream code, copied interfaces and dependencies retain their original +licenses and copyright notices. liblzma of XZ Utils is distributed under the +BSD Zero Clause License (0BSD): + + Copyright (C) The XZ Utils authors and contributors + +Its original README and licenses are retained in upstream/xz/. The library +itself is provided by compat.xz from the same release, and the macros of +include/mwrap/xz/macros.hpp are upstream's definitions. Provenance and +checksums are recorded in upstream/documents.json. diff --git a/README.zh.md b/README.zh.md new file mode 100644 index 0000000..7a040ff --- /dev/null +++ b/README.zh.md @@ -0,0 +1,80 @@ +# xz + +> XZ Utils 的 liblzma——.xz 与 .lzma 压缩库,以 C++23 模块提供。 + +[English](README.md) · **简体中文** · [上游](https://github.com/tukaani-project/xz) · [发布](https://github.com/mcpplibs/xz-m/releases) · [mcpp](https://github.com/mcpp-community/mcpp) · [索引](https://github.com/mcpplibs/mcpp-index) + +[![C++23](https://img.shields.io/badge/C%2B%2B-23-blue.svg)](https://en.cppreference.com/w/cpp/23) +[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE) +[![mcpp-community](https://img.shields.io/badge/mcpp--community-mwrap-blue)](https://github.com/mcpp-community) +[![CI](https://github.com/mcpplibs/xz-m/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/mcpplibs/xz-m/actions/workflows/ci.yml) + +上游 API 不变:`lzma_easy_encoder`、`lzma_stream_decoder`、`lzma_code`、`lzma_stream`、`lzma_filter`、`lzma_ret` 及其枚举值 +都是 `` 中的上游实体,位于上游声明它们的全局命名空间。包名沿用唯一实现 `compat.xz` 5.8.3 的身份;常量宏映射为 +C++ 常量。每次 CI 都用同一份库解码上游自己的测试文件。 + +## 模块 + +- `import mcpplibs.mwrap.xz;`:`` 及其 `lzma/*.h` 的函数、类型与枚举值,并重新导出宏模块 +- `import mcpplibs.mwrap.xz.macros;`:`LZMA_PRESET_DEFAULT`、`LZMA_PRESET_EXTREME`、`LZMA_CONCATENATED`、`LZMA_FILTER_LZMA2`、`LZMA_VLI_UNKNOWN`、`LZMA_VERSION` 等,位于 `mcpplibs::mwrap::xz::macros` +- `#include `:`LZMA_STREAM_INIT`、`lzma_vli_is_valid`、`lzma_set_ext_size`、`lzma_block_header_size_decode`、`LZMA_VLI_C` + +> 更多模块 API -> [用法与 API](docs/usage.md) + +## 快速开始 + +```sh +mcpp new pack --template mcpplibs.mwrap.xz@5.8.3 +cd pack +mcpp run +``` + +默认模板把一段文本打包为带 CRC-64 的 .xz 流,再解包,打印大小与校验和。 + +```toml +[dependencies] +"mcpplibs.mwrap.xz" = "5.8.3" +``` + +```cpp +import std; +import mcpplibs.mwrap.xz; + +namespace x = mcpplibs::mwrap::xz::macros; + +int main() { + std::string text(10000, 'x'); + std::vector out(lzma_stream_buffer_bound(text.size())); + std::size_t size = 0; + lzma_easy_buffer_encode(x::LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64, nullptr, + reinterpret_cast(text.data()), text.size(), out.data(), &size, out.size()); + std::println("{} -> {} bytes", text.size(), size); +} +``` + +## 范围与宏 + +`compat.xz` 构建单线程编解码器、全部过滤器(LZMA1、LZMA2、delta,以及 x86、ARM、ARM64、ARM-Thumb、PowerPC、IA-64、 +SPARC、RISC-V 的 BCJ 过滤器)、三种校验与 .lzip 解码器,不构建多线程编解码器:`lzma_stream_encoder_mt`、 +`lzma_stream_decoder_mt`、`lzma_stream_encoder_mt_memusage` 与 `lzma_cputhreads` 不导出。 + +命名模块不能导出宏。常量映射为 `mcpplibs::mwrap::xz::macros` 中同名的 `inline constexpr`。`LZMA_STREAM_INIT`(初始化器) +与带参数的宏保留在 `` 中;其中两个转换为 `uint32_t`,所以该翻译单元导入 `std.compat`。宏在限定名中也会 +展开。同时包含 `` 的翻译单元经头文件的宏读取常量([docs/usage.md](docs/usage.md))。 + +## 构建与测试 + +Linux、macOS、Windows 的 CI 用 GCC 与 LLVM 构建模块并运行测试:缓冲区往返、经 `LZMA_STREAM_INIT` 的流、由自动解码器读回的 +BCJ 加 LZMA2 过滤链、.lzma 格式、`123456789` 的 CRC-32 与 CRC-64 公布值、无效输入的错误、版本同其宏相等、每个常量同其宏 +相等、每个导出函数都能链接、头文件混用。CI 还运行: + +- 导出审计([tools/generate.py](tools/generate.py) `--check`):按 clang 读到的头文件重新生成模块、宏映射、宏头文件与 + 生成的测试并逐字节比较;在 macOS 与 Windows 上检查头文件在该平台声明的每个名字都已导出; +- 上游的 89 个测试文件:好文件都能解码,坏文件都被拒绝([tools/run_conformance.py](tools/run_conformance.py))。 + +另一个作业把默认模板当作已安装的包生成项目并运行。 + +## 许可 + +包装部分为 [Apache-2.0](LICENSE),作者 mcpp-community([NOTICE](NOTICE))。liblzma 仍遵循 BSD Zero Clause 许可 +([upstream/xz/COPYING](upstream/xz/COPYING)、[COPYING.0BSD](upstream/xz/COPYING.0BSD)、[README](upstream/xz/README))。 diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 0000000..6183729 --- /dev/null +++ b/docs/usage.md @@ -0,0 +1,56 @@ +# Usage and API + +`mcpplibs.mwrap.xz` 5.8.3 exports the public API of liblzma, upstream's names +unchanged. This page lists what each module holds and the places where a module +and a header differ. [tools/generate.py](../tools/generate.py) writes the lists +from the headers, and CI checks them on every target. + +## Modules + +| Module | Holds | +|---|---| +| `mcpplibs.mwrap.xz` | everything `` declares through `lzma/*.h`: `lzma_stream`, `lzma_code`, `lzma_end`, `lzma_ret`, `lzma_action` and the memory and progress queries (base.h); the .xz, .lzma, .lz and raw coders and their buffer forms (`lzma_easy_*`, `lzma_stream_*`, `lzma_alone_*`, `lzma_lzip_decoder`, `lzma_auto_decoder`, `lzma_raw_*`, `lzma_microlzma_*`, container.h); filters (`lzma_filter`, `lzma_options_lzma`, `lzma_options_delta`, `lzma_options_bcj`, `lzma_lzma_preset`, `lzma_filters_*`, `lzma_str_to_filters`, filter.h, lzma12.h, delta.h, bcj.h); blocks, stream flags, indexes and index hashes; checks (`lzma_check`, `lzma_crc32`, `lzma_crc64`, `lzma_check_size`); integers (`lzma_vli` and its coders); `lzma_physmem`; `lzma_version_number`, `lzma_version_string`. Re-exports the macros module | +| `mcpplibs.mwrap.xz.macros` | in `mcpplibs::mwrap::xz::macros`: the presets and their flags (`LZMA_PRESET_DEFAULT`, `LZMA_PRESET_EXTREME`, `LZMA_PRESET_LEVEL_MASK`), the decoder flags (`LZMA_CONCATENATED`, `LZMA_TELL_*`, `LZMA_IGNORE_CHECK`, `LZMA_FAIL_FAST`), the filter IDs (`LZMA_FILTER_*`), the limits and defaults of the LZMA options, of blocks, streams, indexes and VLIs, the `LZMA_STR_*` flags, and the version (`LZMA_VERSION`, `LZMA_VERSION_STRING`, `LZMA_VERSION_MAJOR` …) | + +## What is not exported + +`compat.xz` does not build `hardware_cputhreads.c`, `stream_encoder_mt.c` or +`stream_decoder_mt.c`: the multithreaded coders need a thread library the +package does not configure. Their functions (`lzma_cputhreads`, +`lzma_stream_encoder_mt`, `lzma_stream_encoder_mt_memusage`, +`lzma_stream_decoder_mt`) would not link and are not exported; `lzma_mt`, the +options structure, is. tools/surface.toml lists them as `unbuilt`, and +tests/linked.cpp links every function that is exported. + +## Macros + +- **Constants.** Each object-like macro whose expansion is a constant + expression is an `inline constexpr` of the same name and value; + tests/macros.cpp compares every one with its macro. +- **`LZMA_STREAM_INIT`** is an initializer, not a value, and stays a macro in + ``, with upstream's `NULL` written `nullptr`. +- **Macros with arguments.** `lzma_vli_is_valid(vli)` (reading `LZMA_VLI_MAX` + and `LZMA_VLI_UNKNOWN` from the macros module), `lzma_set_ext_size(opt, + size)`, `lzma_block_header_size_decode(b)` and `LZMA_VLI_C(n)` stay macros + there. The second and third cast to `uint32_t` and `uint64_t`: import + `std.compat`. `LZMA_VLI_C` expands to ``'s `UINT64_C`; in a + module-only unit write `lzma_vli{n}`. +- **Declaration syntax.** `LZMA_API`, `LZMA_API_CALL`, `LZMA_API_IMPORT`, + `lzma_attribute`, `lzma_attr_*`, `lzma_nothrow`, and the stringification + steps `LZMA_VERSION_STRING_C` and `LZMA_VERSION_STRING_C_` are not offered. + tools/surface.toml lists them. + +## A header beside the module + +`#include ` and `import mcpplibs.mwrap.xz;` may appear in one unit: +they declare the same entities. A macro expands even inside a qualified name, +so `mcpplibs::mwrap::xz::macros::LZMA_CONCATENATED` written after the header is +`...::macros::0x08`. In such a unit use the header's macros, or read their +values under other names and `#undef` the macros before naming the constants +(tests/macros.cpp does this). + +## Linking + +The package depends on `compat.xz` 5.8.3, which builds liblzma from the release +sources with the target's toolchain. A program that imports the module links +that library; it does not look for a system liblzma. diff --git a/include/mwrap/xz/macros.hpp b/include/mwrap/xz/macros.hpp new file mode 100644 index 0000000..c91e3a8 --- /dev/null +++ b/include/mwrap/xz/macros.hpp @@ -0,0 +1,32 @@ +// --- the macros of the headers that are neither a constant +// nor a type: they take arguments, or call a function. A named module cannot export a +// macro: include this header before `import mcpplibs.mwrap.xz;`. Each definition is +// upstream's (liblzma of XZ Utils 5.8.3, 0BSD), made only where the upstream header has +// not defined it, with the constants it names read from mcpplibs::mwrap::xz::macros +// and NULL written nullptr. +// The upstream header included after this one redefines these macros. +// Written by tools/generate.py. +#ifndef MWRAP_XZ_MACROS_HPP +#define MWRAP_XZ_MACROS_HPP + +#ifndef LZMA_STREAM_INIT +#define LZMA_STREAM_INIT { nullptr, 0, 0, nullptr, 0, 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, 0, 0, 0, 0, LZMA_RESERVED_ENUM, LZMA_RESERVED_ENUM } +#endif + +#ifndef LZMA_VLI_C +#define LZMA_VLI_C(n) UINT64_C(n) +#endif + +#ifndef lzma_block_header_size_decode +#define lzma_block_header_size_decode(b) (((uint32_t)(b) + 1) * 4) +#endif + +#ifndef lzma_set_ext_size +#define lzma_set_ext_size(opt_lzma2,u64size) do { (opt_lzma2).ext_size_low = (uint32_t)(u64size); (opt_lzma2).ext_size_high = (uint32_t)((uint64_t)(u64size) >> 32); } while (0) +#endif + +#ifndef lzma_vli_is_valid +#define lzma_vli_is_valid(vli) ((vli) <= ::mcpplibs::mwrap::xz::macros::LZMA_VLI_MAX || (vli) == ::mcpplibs::mwrap::xz::macros::LZMA_VLI_UNKNOWN) +#endif + +#endif // MWRAP_XZ_MACROS_HPP diff --git a/mcpp.toml b/mcpp.toml new file mode 100644 index 0000000..5b4f981 --- /dev/null +++ b/mcpp.toml @@ -0,0 +1,47 @@ +# Module wrapper; upstream API preserved. Policy: mcpp-index/docs/mwrap.md. +[package] +namespace = "mcpplibs.mwrap" +name = "xz" +version = "5.8.3" +description = "XZ Utils 5.8.3 liblzma C++ modules for mcpp (import mcpplibs.mwrap.xz;): .xz and .lzma streams, filters, blocks, indexes and checks over compat.xz, with the constants of lzma.h as C++ constants" +license = "Apache-2.0" +authors = ["mcpp-community"] +repo = "https://github.com/mcpplibs/xz-m" +platforms = ["linux", "macos", "windows"] + +[targets.xz] +kind = "lib" + +[build] +sources = ["src/*.cppm"] +# : LZMA_STREAM_INIT and the other macros that are not constants. +include_dirs = ["include"] + +[dependencies] +compat.xz = "5.8.3" + +[test] +discover = ["tests/*.cpp"] + +[package.metadata.mwrap] +upstream-version = "5.8.3" + +[[runtime.deploy]] +from = "LICENSE" +to = "licenses/xz" + +[[runtime.deploy]] +from = "NOTICE" +to = "licenses/xz" + +[[runtime.deploy]] +from = "upstream/xz/README" +to = "licenses/xz/upstream/xz" + +[[runtime.deploy]] +from = "upstream/xz/COPYING" +to = "licenses/xz/upstream/xz" + +[[runtime.deploy]] +from = "upstream/xz/COPYING.0BSD" +to = "licenses/xz/upstream/xz" diff --git a/src/macros.cppm b/src/macros.cppm new file mode 100644 index 0000000..8915253 --- /dev/null +++ b/src/macros.cppm @@ -0,0 +1,210 @@ +// mcpplibs.mwrap.xz.macros --- the macros of the headers that are a constant or a +// type, under their original names in mcpplibs::mwrap::xz::macros, each equal to +// the macro where the module is built (a named module cannot export a macro). The +// other macros are in . Written by tools/generate.py. +module; +#include + +// The values, taken under other names while the macros are defined. +namespace mwrap_xz_capture { +inline constexpr auto value_LZMA_BACKWARD_SIZE_MAX = (LZMA_BACKWARD_SIZE_MAX); +inline constexpr auto value_LZMA_BACKWARD_SIZE_MIN = (LZMA_BACKWARD_SIZE_MIN); +inline constexpr auto value_LZMA_BLOCK_HEADER_SIZE_MAX = (LZMA_BLOCK_HEADER_SIZE_MAX); +inline constexpr auto value_LZMA_BLOCK_HEADER_SIZE_MIN = (LZMA_BLOCK_HEADER_SIZE_MIN); +inline constexpr auto value_LZMA_CHECK_ID_MAX = (LZMA_CHECK_ID_MAX); +inline constexpr auto value_LZMA_CHECK_SIZE_MAX = (LZMA_CHECK_SIZE_MAX); +inline constexpr auto value_LZMA_CONCATENATED = (LZMA_CONCATENATED); +inline constexpr auto value_LZMA_DELTA_DIST_MAX = (LZMA_DELTA_DIST_MAX); +inline constexpr auto value_LZMA_DELTA_DIST_MIN = (LZMA_DELTA_DIST_MIN); +inline constexpr auto value_LZMA_DICT_SIZE_DEFAULT = (LZMA_DICT_SIZE_DEFAULT); +inline constexpr auto value_LZMA_DICT_SIZE_MIN = (LZMA_DICT_SIZE_MIN); +inline constexpr auto value_LZMA_FAIL_FAST = (LZMA_FAIL_FAST); +inline constexpr auto value_LZMA_FILTERS_MAX = (LZMA_FILTERS_MAX); +inline constexpr auto value_LZMA_FILTER_ARM = (LZMA_FILTER_ARM); +inline constexpr auto value_LZMA_FILTER_ARM64 = (LZMA_FILTER_ARM64); +inline constexpr auto value_LZMA_FILTER_ARMTHUMB = (LZMA_FILTER_ARMTHUMB); +inline constexpr auto value_LZMA_FILTER_DELTA = (LZMA_FILTER_DELTA); +inline constexpr auto value_LZMA_FILTER_IA64 = (LZMA_FILTER_IA64); +inline constexpr auto value_LZMA_FILTER_LZMA1 = (LZMA_FILTER_LZMA1); +inline constexpr auto value_LZMA_FILTER_LZMA1EXT = (LZMA_FILTER_LZMA1EXT); +inline constexpr auto value_LZMA_FILTER_LZMA2 = (LZMA_FILTER_LZMA2); +inline constexpr auto value_LZMA_FILTER_POWERPC = (LZMA_FILTER_POWERPC); +inline constexpr auto value_LZMA_FILTER_RISCV = (LZMA_FILTER_RISCV); +inline constexpr auto value_LZMA_FILTER_SPARC = (LZMA_FILTER_SPARC); +inline constexpr auto value_LZMA_FILTER_X86 = (LZMA_FILTER_X86); +inline constexpr auto value_LZMA_IGNORE_CHECK = (LZMA_IGNORE_CHECK); +inline constexpr auto value_LZMA_INDEX_CHECK_MASK_CRC32 = (LZMA_INDEX_CHECK_MASK_CRC32); +inline constexpr auto value_LZMA_INDEX_CHECK_MASK_CRC64 = (LZMA_INDEX_CHECK_MASK_CRC64); +inline constexpr auto value_LZMA_INDEX_CHECK_MASK_NONE = (LZMA_INDEX_CHECK_MASK_NONE); +inline constexpr auto value_LZMA_INDEX_CHECK_MASK_SHA256 = (LZMA_INDEX_CHECK_MASK_SHA256); +inline constexpr auto value_LZMA_LCLP_MAX = (LZMA_LCLP_MAX); +inline constexpr auto value_LZMA_LCLP_MIN = (LZMA_LCLP_MIN); +inline constexpr auto value_LZMA_LC_DEFAULT = (LZMA_LC_DEFAULT); +inline constexpr auto value_LZMA_LP_DEFAULT = (LZMA_LP_DEFAULT); +inline constexpr auto value_LZMA_LZMA1EXT_ALLOW_EOPM = (LZMA_LZMA1EXT_ALLOW_EOPM); +inline constexpr auto value_LZMA_PB_DEFAULT = (LZMA_PB_DEFAULT); +inline constexpr auto value_LZMA_PB_MAX = (LZMA_PB_MAX); +inline constexpr auto value_LZMA_PB_MIN = (LZMA_PB_MIN); +inline constexpr auto value_LZMA_PRESET_DEFAULT = (LZMA_PRESET_DEFAULT); +inline constexpr auto value_LZMA_PRESET_EXTREME = (LZMA_PRESET_EXTREME); +inline constexpr auto value_LZMA_PRESET_LEVEL_MASK = (LZMA_PRESET_LEVEL_MASK); +inline constexpr auto value_LZMA_STREAM_HEADER_SIZE = (LZMA_STREAM_HEADER_SIZE); +inline constexpr auto value_LZMA_STR_ALL_FILTERS = (LZMA_STR_ALL_FILTERS); +inline constexpr auto value_LZMA_STR_DECODER = (LZMA_STR_DECODER); +inline constexpr auto value_LZMA_STR_ENCODER = (LZMA_STR_ENCODER); +inline constexpr auto value_LZMA_STR_GETOPT_LONG = (LZMA_STR_GETOPT_LONG); +inline constexpr auto value_LZMA_STR_NO_SPACES = (LZMA_STR_NO_SPACES); +inline constexpr auto value_LZMA_STR_NO_VALIDATION = (LZMA_STR_NO_VALIDATION); +inline constexpr auto value_LZMA_TELL_ANY_CHECK = (LZMA_TELL_ANY_CHECK); +inline constexpr auto value_LZMA_TELL_NO_CHECK = (LZMA_TELL_NO_CHECK); +inline constexpr auto value_LZMA_TELL_UNSUPPORTED_CHECK = (LZMA_TELL_UNSUPPORTED_CHECK); +inline constexpr auto value_LZMA_VERSION = (LZMA_VERSION); +inline constexpr auto value_LZMA_VERSION_COMMIT = (LZMA_VERSION_COMMIT); +inline constexpr auto value_LZMA_VERSION_MAJOR = (LZMA_VERSION_MAJOR); +inline constexpr auto value_LZMA_VERSION_MINOR = (LZMA_VERSION_MINOR); +inline constexpr auto value_LZMA_VERSION_PATCH = (LZMA_VERSION_PATCH); +inline constexpr auto value_LZMA_VERSION_STABILITY = (LZMA_VERSION_STABILITY); +inline constexpr auto value_LZMA_VERSION_STABILITY_ALPHA = (LZMA_VERSION_STABILITY_ALPHA); +inline constexpr auto value_LZMA_VERSION_STABILITY_BETA = (LZMA_VERSION_STABILITY_BETA); +inline constexpr auto value_LZMA_VERSION_STABILITY_STABLE = (LZMA_VERSION_STABILITY_STABLE); +inline constexpr auto value_LZMA_VERSION_STABILITY_STRING = (LZMA_VERSION_STABILITY_STRING); +inline constexpr auto value_LZMA_VERSION_STRING = (LZMA_VERSION_STRING); +inline constexpr auto value_LZMA_VLI_BYTES_MAX = (LZMA_VLI_BYTES_MAX); +inline constexpr auto value_LZMA_VLI_MAX = (LZMA_VLI_MAX); +inline constexpr auto value_LZMA_VLI_UNKNOWN = (LZMA_VLI_UNKNOWN); +} +#undef LZMA_BACKWARD_SIZE_MAX +#undef LZMA_BACKWARD_SIZE_MIN +#undef LZMA_BLOCK_HEADER_SIZE_MAX +#undef LZMA_BLOCK_HEADER_SIZE_MIN +#undef LZMA_CHECK_ID_MAX +#undef LZMA_CHECK_SIZE_MAX +#undef LZMA_CONCATENATED +#undef LZMA_DELTA_DIST_MAX +#undef LZMA_DELTA_DIST_MIN +#undef LZMA_DICT_SIZE_DEFAULT +#undef LZMA_DICT_SIZE_MIN +#undef LZMA_FAIL_FAST +#undef LZMA_FILTERS_MAX +#undef LZMA_FILTER_ARM +#undef LZMA_FILTER_ARM64 +#undef LZMA_FILTER_ARMTHUMB +#undef LZMA_FILTER_DELTA +#undef LZMA_FILTER_IA64 +#undef LZMA_FILTER_LZMA1 +#undef LZMA_FILTER_LZMA1EXT +#undef LZMA_FILTER_LZMA2 +#undef LZMA_FILTER_POWERPC +#undef LZMA_FILTER_RISCV +#undef LZMA_FILTER_SPARC +#undef LZMA_FILTER_X86 +#undef LZMA_IGNORE_CHECK +#undef LZMA_INDEX_CHECK_MASK_CRC32 +#undef LZMA_INDEX_CHECK_MASK_CRC64 +#undef LZMA_INDEX_CHECK_MASK_NONE +#undef LZMA_INDEX_CHECK_MASK_SHA256 +#undef LZMA_LCLP_MAX +#undef LZMA_LCLP_MIN +#undef LZMA_LC_DEFAULT +#undef LZMA_LP_DEFAULT +#undef LZMA_LZMA1EXT_ALLOW_EOPM +#undef LZMA_PB_DEFAULT +#undef LZMA_PB_MAX +#undef LZMA_PB_MIN +#undef LZMA_PRESET_DEFAULT +#undef LZMA_PRESET_EXTREME +#undef LZMA_PRESET_LEVEL_MASK +#undef LZMA_STREAM_HEADER_SIZE +#undef LZMA_STR_ALL_FILTERS +#undef LZMA_STR_DECODER +#undef LZMA_STR_ENCODER +#undef LZMA_STR_GETOPT_LONG +#undef LZMA_STR_NO_SPACES +#undef LZMA_STR_NO_VALIDATION +#undef LZMA_TELL_ANY_CHECK +#undef LZMA_TELL_NO_CHECK +#undef LZMA_TELL_UNSUPPORTED_CHECK +#undef LZMA_VERSION +#undef LZMA_VERSION_COMMIT +#undef LZMA_VERSION_MAJOR +#undef LZMA_VERSION_MINOR +#undef LZMA_VERSION_PATCH +#undef LZMA_VERSION_STABILITY +#undef LZMA_VERSION_STABILITY_ALPHA +#undef LZMA_VERSION_STABILITY_BETA +#undef LZMA_VERSION_STABILITY_STABLE +#undef LZMA_VERSION_STABILITY_STRING +#undef LZMA_VERSION_STRING +#undef LZMA_VLI_BYTES_MAX +#undef LZMA_VLI_MAX +#undef LZMA_VLI_UNKNOWN + +export module mcpplibs.mwrap.xz.macros; + +export namespace mcpplibs::mwrap::xz::macros { +inline constexpr auto LZMA_BACKWARD_SIZE_MAX = mwrap_xz_capture::value_LZMA_BACKWARD_SIZE_MAX; +inline constexpr auto LZMA_BACKWARD_SIZE_MIN = mwrap_xz_capture::value_LZMA_BACKWARD_SIZE_MIN; +inline constexpr auto LZMA_BLOCK_HEADER_SIZE_MAX = mwrap_xz_capture::value_LZMA_BLOCK_HEADER_SIZE_MAX; +inline constexpr auto LZMA_BLOCK_HEADER_SIZE_MIN = mwrap_xz_capture::value_LZMA_BLOCK_HEADER_SIZE_MIN; +inline constexpr auto LZMA_CHECK_ID_MAX = mwrap_xz_capture::value_LZMA_CHECK_ID_MAX; +inline constexpr auto LZMA_CHECK_SIZE_MAX = mwrap_xz_capture::value_LZMA_CHECK_SIZE_MAX; +inline constexpr auto LZMA_CONCATENATED = mwrap_xz_capture::value_LZMA_CONCATENATED; +inline constexpr auto LZMA_DELTA_DIST_MAX = mwrap_xz_capture::value_LZMA_DELTA_DIST_MAX; +inline constexpr auto LZMA_DELTA_DIST_MIN = mwrap_xz_capture::value_LZMA_DELTA_DIST_MIN; +inline constexpr auto LZMA_DICT_SIZE_DEFAULT = mwrap_xz_capture::value_LZMA_DICT_SIZE_DEFAULT; +inline constexpr auto LZMA_DICT_SIZE_MIN = mwrap_xz_capture::value_LZMA_DICT_SIZE_MIN; +inline constexpr auto LZMA_FAIL_FAST = mwrap_xz_capture::value_LZMA_FAIL_FAST; +inline constexpr auto LZMA_FILTERS_MAX = mwrap_xz_capture::value_LZMA_FILTERS_MAX; +inline constexpr auto LZMA_FILTER_ARM = mwrap_xz_capture::value_LZMA_FILTER_ARM; +inline constexpr auto LZMA_FILTER_ARM64 = mwrap_xz_capture::value_LZMA_FILTER_ARM64; +inline constexpr auto LZMA_FILTER_ARMTHUMB = mwrap_xz_capture::value_LZMA_FILTER_ARMTHUMB; +inline constexpr auto LZMA_FILTER_DELTA = mwrap_xz_capture::value_LZMA_FILTER_DELTA; +inline constexpr auto LZMA_FILTER_IA64 = mwrap_xz_capture::value_LZMA_FILTER_IA64; +inline constexpr auto LZMA_FILTER_LZMA1 = mwrap_xz_capture::value_LZMA_FILTER_LZMA1; +inline constexpr auto LZMA_FILTER_LZMA1EXT = mwrap_xz_capture::value_LZMA_FILTER_LZMA1EXT; +inline constexpr auto LZMA_FILTER_LZMA2 = mwrap_xz_capture::value_LZMA_FILTER_LZMA2; +inline constexpr auto LZMA_FILTER_POWERPC = mwrap_xz_capture::value_LZMA_FILTER_POWERPC; +inline constexpr auto LZMA_FILTER_RISCV = mwrap_xz_capture::value_LZMA_FILTER_RISCV; +inline constexpr auto LZMA_FILTER_SPARC = mwrap_xz_capture::value_LZMA_FILTER_SPARC; +inline constexpr auto LZMA_FILTER_X86 = mwrap_xz_capture::value_LZMA_FILTER_X86; +inline constexpr auto LZMA_IGNORE_CHECK = mwrap_xz_capture::value_LZMA_IGNORE_CHECK; +inline constexpr auto LZMA_INDEX_CHECK_MASK_CRC32 = mwrap_xz_capture::value_LZMA_INDEX_CHECK_MASK_CRC32; +inline constexpr auto LZMA_INDEX_CHECK_MASK_CRC64 = mwrap_xz_capture::value_LZMA_INDEX_CHECK_MASK_CRC64; +inline constexpr auto LZMA_INDEX_CHECK_MASK_NONE = mwrap_xz_capture::value_LZMA_INDEX_CHECK_MASK_NONE; +inline constexpr auto LZMA_INDEX_CHECK_MASK_SHA256 = mwrap_xz_capture::value_LZMA_INDEX_CHECK_MASK_SHA256; +inline constexpr auto LZMA_LCLP_MAX = mwrap_xz_capture::value_LZMA_LCLP_MAX; +inline constexpr auto LZMA_LCLP_MIN = mwrap_xz_capture::value_LZMA_LCLP_MIN; +inline constexpr auto LZMA_LC_DEFAULT = mwrap_xz_capture::value_LZMA_LC_DEFAULT; +inline constexpr auto LZMA_LP_DEFAULT = mwrap_xz_capture::value_LZMA_LP_DEFAULT; +inline constexpr auto LZMA_LZMA1EXT_ALLOW_EOPM = mwrap_xz_capture::value_LZMA_LZMA1EXT_ALLOW_EOPM; +inline constexpr auto LZMA_PB_DEFAULT = mwrap_xz_capture::value_LZMA_PB_DEFAULT; +inline constexpr auto LZMA_PB_MAX = mwrap_xz_capture::value_LZMA_PB_MAX; +inline constexpr auto LZMA_PB_MIN = mwrap_xz_capture::value_LZMA_PB_MIN; +inline constexpr auto LZMA_PRESET_DEFAULT = mwrap_xz_capture::value_LZMA_PRESET_DEFAULT; +inline constexpr auto LZMA_PRESET_EXTREME = mwrap_xz_capture::value_LZMA_PRESET_EXTREME; +inline constexpr auto LZMA_PRESET_LEVEL_MASK = mwrap_xz_capture::value_LZMA_PRESET_LEVEL_MASK; +inline constexpr auto LZMA_STREAM_HEADER_SIZE = mwrap_xz_capture::value_LZMA_STREAM_HEADER_SIZE; +inline constexpr auto LZMA_STR_ALL_FILTERS = mwrap_xz_capture::value_LZMA_STR_ALL_FILTERS; +inline constexpr auto LZMA_STR_DECODER = mwrap_xz_capture::value_LZMA_STR_DECODER; +inline constexpr auto LZMA_STR_ENCODER = mwrap_xz_capture::value_LZMA_STR_ENCODER; +inline constexpr auto LZMA_STR_GETOPT_LONG = mwrap_xz_capture::value_LZMA_STR_GETOPT_LONG; +inline constexpr auto LZMA_STR_NO_SPACES = mwrap_xz_capture::value_LZMA_STR_NO_SPACES; +inline constexpr auto LZMA_STR_NO_VALIDATION = mwrap_xz_capture::value_LZMA_STR_NO_VALIDATION; +inline constexpr auto LZMA_TELL_ANY_CHECK = mwrap_xz_capture::value_LZMA_TELL_ANY_CHECK; +inline constexpr auto LZMA_TELL_NO_CHECK = mwrap_xz_capture::value_LZMA_TELL_NO_CHECK; +inline constexpr auto LZMA_TELL_UNSUPPORTED_CHECK = mwrap_xz_capture::value_LZMA_TELL_UNSUPPORTED_CHECK; +inline constexpr auto LZMA_VERSION = mwrap_xz_capture::value_LZMA_VERSION; +inline constexpr auto LZMA_VERSION_COMMIT = mwrap_xz_capture::value_LZMA_VERSION_COMMIT; +inline constexpr auto LZMA_VERSION_MAJOR = mwrap_xz_capture::value_LZMA_VERSION_MAJOR; +inline constexpr auto LZMA_VERSION_MINOR = mwrap_xz_capture::value_LZMA_VERSION_MINOR; +inline constexpr auto LZMA_VERSION_PATCH = mwrap_xz_capture::value_LZMA_VERSION_PATCH; +inline constexpr auto LZMA_VERSION_STABILITY = mwrap_xz_capture::value_LZMA_VERSION_STABILITY; +inline constexpr auto LZMA_VERSION_STABILITY_ALPHA = mwrap_xz_capture::value_LZMA_VERSION_STABILITY_ALPHA; +inline constexpr auto LZMA_VERSION_STABILITY_BETA = mwrap_xz_capture::value_LZMA_VERSION_STABILITY_BETA; +inline constexpr auto LZMA_VERSION_STABILITY_STABLE = mwrap_xz_capture::value_LZMA_VERSION_STABILITY_STABLE; +inline constexpr auto LZMA_VERSION_STABILITY_STRING = mwrap_xz_capture::value_LZMA_VERSION_STABILITY_STRING; +inline constexpr auto LZMA_VERSION_STRING = mwrap_xz_capture::value_LZMA_VERSION_STRING; +inline constexpr auto LZMA_VLI_BYTES_MAX = mwrap_xz_capture::value_LZMA_VLI_BYTES_MAX; +inline constexpr auto LZMA_VLI_MAX = mwrap_xz_capture::value_LZMA_VLI_MAX; +inline constexpr auto LZMA_VLI_UNKNOWN = mwrap_xz_capture::value_LZMA_VLI_UNKNOWN; +} diff --git a/src/xz.cppm b/src/xz.cppm new file mode 100644 index 0000000..1b01175 --- /dev/null +++ b/src/xz.cppm @@ -0,0 +1,192 @@ +// mcpplibs.mwrap.xz --- liblzma of XZ Utils 5.8.3 as a C++ module. Every exported name is +// the upstream entity of the headers below, unchanged, in the global namespace where +// upstream declares it; the root also re-exports the macro mapping. The implementation +// is compat.xz 5.8.3. Written by tools/generate.py from the headers. +module; +#include + +export module mcpplibs.mwrap.xz; + +export import mcpplibs.mwrap.xz.macros; + +export { +using ::LZMA_BUF_ERROR; +using ::LZMA_CHECK_CRC32; +using ::LZMA_CHECK_CRC64; +using ::LZMA_CHECK_NONE; +using ::LZMA_CHECK_SHA256; +using ::LZMA_DATA_ERROR; +using ::LZMA_DELTA_TYPE_BYTE; +using ::LZMA_FINISH; +using ::LZMA_FORMAT_ERROR; +using ::LZMA_FULL_BARRIER; +using ::LZMA_FULL_FLUSH; +using ::LZMA_GET_CHECK; +using ::LZMA_INDEX_ITER_ANY; +using ::LZMA_INDEX_ITER_BLOCK; +using ::LZMA_INDEX_ITER_NONEMPTY_BLOCK; +using ::LZMA_INDEX_ITER_STREAM; +using ::LZMA_MEMLIMIT_ERROR; +using ::LZMA_MEM_ERROR; +using ::LZMA_MF_BT2; +using ::LZMA_MF_BT3; +using ::LZMA_MF_BT4; +using ::LZMA_MF_HC3; +using ::LZMA_MF_HC4; +using ::LZMA_MODE_FAST; +using ::LZMA_MODE_NORMAL; +using ::LZMA_NO_CHECK; +using ::LZMA_OK; +using ::LZMA_OPTIONS_ERROR; +using ::LZMA_PROG_ERROR; +using ::LZMA_RESERVED_ENUM; +using ::LZMA_RET_INTERNAL1; +using ::LZMA_RET_INTERNAL2; +using ::LZMA_RET_INTERNAL3; +using ::LZMA_RET_INTERNAL4; +using ::LZMA_RET_INTERNAL5; +using ::LZMA_RET_INTERNAL6; +using ::LZMA_RET_INTERNAL7; +using ::LZMA_RET_INTERNAL8; +using ::LZMA_RUN; +using ::LZMA_SEEK_NEEDED; +using ::LZMA_STREAM_END; +using ::LZMA_SYNC_FLUSH; +using ::LZMA_UNSUPPORTED_CHECK; +using ::lzma_action; +using ::lzma_allocator; +using ::lzma_alone_decoder; +using ::lzma_alone_encoder; +using ::lzma_auto_decoder; +using ::lzma_bcj_arm64_decode; +using ::lzma_bcj_arm64_encode; +using ::lzma_bcj_riscv_decode; +using ::lzma_bcj_riscv_encode; +using ::lzma_bcj_x86_decode; +using ::lzma_bcj_x86_encode; +using ::lzma_block; +using ::lzma_block_buffer_bound; +using ::lzma_block_buffer_decode; +using ::lzma_block_buffer_encode; +using ::lzma_block_compressed_size; +using ::lzma_block_decoder; +using ::lzma_block_encoder; +using ::lzma_block_header_decode; +using ::lzma_block_header_encode; +using ::lzma_block_header_size; +using ::lzma_block_total_size; +using ::lzma_block_uncomp_encode; +using ::lzma_block_unpadded_size; +using ::lzma_bool; +using ::lzma_check; +using ::lzma_check_is_supported; +using ::lzma_check_size; +using ::lzma_code; +using ::lzma_crc32; +using ::lzma_crc64; +using ::lzma_delta_type; +using ::lzma_easy_buffer_encode; +using ::lzma_easy_decoder_memusage; +using ::lzma_easy_encoder; +using ::lzma_easy_encoder_memusage; +using ::lzma_end; +using ::lzma_file_info_decoder; +using ::lzma_filter; +using ::lzma_filter_decoder_is_supported; +using ::lzma_filter_encoder_is_supported; +using ::lzma_filter_flags_decode; +using ::lzma_filter_flags_encode; +using ::lzma_filter_flags_size; +using ::lzma_filters_copy; +using ::lzma_filters_free; +using ::lzma_filters_update; +using ::lzma_get_check; +using ::lzma_get_progress; +using ::lzma_index; +using ::lzma_index_append; +using ::lzma_index_block_count; +using ::lzma_index_buffer_decode; +using ::lzma_index_buffer_encode; +using ::lzma_index_cat; +using ::lzma_index_checks; +using ::lzma_index_decoder; +using ::lzma_index_dup; +using ::lzma_index_encoder; +using ::lzma_index_end; +using ::lzma_index_file_size; +using ::lzma_index_hash; +using ::lzma_index_hash_append; +using ::lzma_index_hash_decode; +using ::lzma_index_hash_end; +using ::lzma_index_hash_init; +using ::lzma_index_hash_s; +using ::lzma_index_hash_size; +using ::lzma_index_init; +using ::lzma_index_iter; +using ::lzma_index_iter_init; +using ::lzma_index_iter_locate; +using ::lzma_index_iter_mode; +using ::lzma_index_iter_next; +using ::lzma_index_iter_rewind; +using ::lzma_index_memusage; +using ::lzma_index_memused; +using ::lzma_index_s; +using ::lzma_index_size; +using ::lzma_index_stream_count; +using ::lzma_index_stream_flags; +using ::lzma_index_stream_padding; +using ::lzma_index_stream_size; +using ::lzma_index_total_size; +using ::lzma_index_uncompressed_size; +using ::lzma_internal; +using ::lzma_internal_s; +using ::lzma_lzip_decoder; +using ::lzma_lzma_preset; +using ::lzma_match_finder; +using ::lzma_memlimit_get; +using ::lzma_memlimit_set; +using ::lzma_memusage; +using ::lzma_mf_is_supported; +using ::lzma_microlzma_decoder; +using ::lzma_microlzma_encoder; +using ::lzma_mode; +using ::lzma_mode_is_supported; +using ::lzma_mt; +using ::lzma_mt_block_size; +using ::lzma_options_bcj; +using ::lzma_options_delta; +using ::lzma_options_lzma; +using ::lzma_physmem; +using ::lzma_properties_decode; +using ::lzma_properties_encode; +using ::lzma_properties_size; +using ::lzma_raw_buffer_decode; +using ::lzma_raw_buffer_encode; +using ::lzma_raw_decoder; +using ::lzma_raw_decoder_memusage; +using ::lzma_raw_encoder; +using ::lzma_raw_encoder_memusage; +using ::lzma_reserved_enum; +using ::lzma_ret; +using ::lzma_str_from_filters; +using ::lzma_str_list_filters; +using ::lzma_str_to_filters; +using ::lzma_stream; +using ::lzma_stream_buffer_bound; +using ::lzma_stream_buffer_decode; +using ::lzma_stream_buffer_encode; +using ::lzma_stream_decoder; +using ::lzma_stream_encoder; +using ::lzma_stream_flags; +using ::lzma_stream_flags_compare; +using ::lzma_stream_footer_decode; +using ::lzma_stream_footer_encode; +using ::lzma_stream_header_decode; +using ::lzma_stream_header_encode; +using ::lzma_version_number; +using ::lzma_version_string; +using ::lzma_vli; +using ::lzma_vli_decode; +using ::lzma_vli_encode; +using ::lzma_vli_size; +} diff --git a/templates/pack/mcpp.toml.in b/templates/pack/mcpp.toml.in new file mode 100644 index 0000000..a637560 --- /dev/null +++ b/templates/pack/mcpp.toml.in @@ -0,0 +1,10 @@ +[package] +name = "{{project.name}}" +version = "0.1.0" + +[targets.{{project.name}}] +kind = "bin" +main = "src/main.cpp" + +[dependencies] +"{{self.name}}" = "{{self.version}}" diff --git a/templates/pack/src/main.cpp.in b/templates/pack/src/main.cpp.in new file mode 100644 index 0000000..09ceda8 --- /dev/null +++ b/templates/pack/src/main.cpp.in @@ -0,0 +1,53 @@ +// {{project.name}}: liblzma through import mcpplibs.mwrap.xz. The functions, types +// and enumerators are liblzma's own names; the constants are in +// mcpplibs::mwrap::xz::macros, and LZMA_STREAM_INIT, a macro, comes from +// . +#include +import std; +import mcpplibs.mwrap.xz; + +namespace x = mcpplibs::mwrap::xz::macros; + +// Runs a coder over `in` to the end of the stream. +static std::vector code(lzma_stream& s, std::span in, lzma_ret& rc) +{ + std::vector out; + s.next_in = in.data(); + s.avail_in = in.size(); + do { + std::uint8_t chunk[16384]; + s.next_out = chunk; + s.avail_out = sizeof chunk; + rc = lzma_code(&s, LZMA_FINISH); + out.insert(out.end(), chunk, chunk + (sizeof chunk - s.avail_out)); + } while (rc == LZMA_OK); + lzma_end(&s); + return out; +} + +int main(int argc, char** argv) +{ + std::string text; + if (argc > 1) { + std::ifstream file(argv[1], std::ios::binary); + text.assign(std::istreambuf_iterator(file), {}); + } else { + for (int i = 0; i < 300; ++i) text += std::format("liblzma through a C++ module, row {}\n", i % 12); + } + const std::span bytes(reinterpret_cast(text.data()), text.size()); + + lzma_ret rc; + lzma_stream enc = LZMA_STREAM_INIT; + if (lzma_easy_encoder(&enc, x::LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64) != LZMA_OK) return 1; + const auto packed = code(enc, bytes, rc); + if (rc != LZMA_STREAM_END) return 1; + + lzma_stream dec = LZMA_STREAM_INIT; + if (lzma_stream_decoder(&dec, std::numeric_limits::max(), 0) != LZMA_OK) return 1; + const auto unpacked = code(dec, packed, rc); + const bool intact = rc == LZMA_STREAM_END && std::ranges::equal(unpacked, bytes); + + std::println("liblzma {}: {} bytes -> {} bytes (.xz, CRC-64 {:016x}), unpacked {}", lzma_version_string(), + text.size(), packed.size(), lzma_crc64(bytes.data(), bytes.size(), 0), intact ? "intact" : "DIFFERENT"); + return intact ? 0 : 1; +} diff --git a/templates/pack/template.toml b/templates/pack/template.toml new file mode 100644 index 0000000..7b4321e --- /dev/null +++ b/templates/pack/template.toml @@ -0,0 +1,4 @@ +[template] +description = "Compression with import mcpplibs.mwrap.xz: a text packed into an .xz stream with a CRC-64 and unpacked again" +default = true +post_message = "Run `mcpp run`, or `mcpp run -- ` to pack a file of your own." diff --git a/tests/api_test.cpp b/tests/api_test.cpp new file mode 100644 index 0000000..1761f6b --- /dev/null +++ b/tests/api_test.cpp @@ -0,0 +1,101 @@ +// The module's functions over compat.xz: a buffer round trip, a stream through +// LZMA_STREAM_INIT and lzma_code, a filter chain with a BCJ filter, the two +// checksums against published values, the error of garbage input, the .lzma +// format, and the version against its macros. +#include +import std.compat; +import mcpplibs.mwrap.xz; + +namespace m = mcpplibs::mwrap::xz::macros; + +int main() +{ + int failures = 0; + auto check = [&](bool ok, std::string_view what) { + std::println("{} {}", ok ? "ok " : "FAIL", what); + failures += ok ? 0 : 1; + }; + + constexpr auto unlimited = std::numeric_limits::max(); + std::string text; + for (int i = 0; i < 3000; ++i) text += std::format("entry {} of a repetitive journal\n", i % 29); + const auto* bytes = reinterpret_cast(text.data()); + + // Buffers, one call each way. + std::vector xz(lzma_stream_buffer_bound(text.size())); + std::size_t xz_size = 0; + check(lzma_easy_buffer_encode(m::LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64, nullptr, bytes, text.size(), xz.data(), + &xz_size, xz.size()) == LZMA_OK && xz_size < text.size() / 10, + "lzma_easy_buffer_encode shrinks the text"); + std::string back(text.size(), '\0'); + std::uint64_t limit = unlimited; + std::size_t in_pos = 0, out_pos = 0; + check(lzma_stream_buffer_decode(&limit, 0, nullptr, xz.data(), &in_pos, xz_size, + reinterpret_cast(back.data()), &out_pos, back.size()) == LZMA_OK + && out_pos == text.size() && back == text, "lzma_stream_buffer_decode restores it"); + + // A stream: LZMA_STREAM_INIT is a macro, lzma_code runs it to LZMA_STREAM_END. + auto run = [](lzma_stream& s, const std::uint8_t* in, std::size_t n, std::vector& out) { + s.next_in = in; + s.avail_in = n; + lzma_ret rc = LZMA_OK; + while (rc == LZMA_OK) { + std::uint8_t chunk[4096]; + s.next_out = chunk; + s.avail_out = sizeof chunk; + rc = lzma_code(&s, LZMA_FINISH); + out.insert(out.end(), chunk, chunk + (sizeof chunk - s.avail_out)); + } + lzma_end(&s); + return rc; + }; + lzma_stream enc = LZMA_STREAM_INIT; + std::vector streamed; + check(lzma_easy_encoder(&enc, 6 | m::LZMA_PRESET_EXTREME, LZMA_CHECK_SHA256) == LZMA_OK + && run(enc, bytes, text.size(), streamed) == LZMA_STREAM_END, "lzma_easy_encoder, extreme, SHA-256"); + lzma_stream dec = LZMA_STREAM_INIT; + std::vector restored; + check(lzma_stream_decoder(&dec, unlimited, m::LZMA_CONCATENATED) == LZMA_OK + && run(dec, streamed.data(), streamed.size(), restored) == LZMA_STREAM_END + && std::string(restored.begin(), restored.end()) == text, "lzma_stream_decoder restores the stream"); + + // A filter chain: x86 BCJ before LZMA2. + lzma_options_lzma lzma2{}; + check(!lzma_lzma_preset(&lzma2, 1), "lzma_lzma_preset"); + lzma_filter chain[] = {{m::LZMA_FILTER_X86, nullptr}, {m::LZMA_FILTER_LZMA2, &lzma2}, {m::LZMA_VLI_UNKNOWN, nullptr}}; + check(lzma_filter_encoder_is_supported(m::LZMA_FILTER_X86) && lzma_filter_decoder_is_supported(m::LZMA_FILTER_LZMA2), + "the filters are built"); + lzma_stream chained = LZMA_STREAM_INIT; + std::vector filtered; + check(lzma_stream_encoder(&chained, chain, LZMA_CHECK_CRC32) == LZMA_OK + && run(chained, bytes, text.size(), filtered) == LZMA_STREAM_END, "lzma_stream_encoder with BCJ and LZMA2"); + lzma_stream auto_dec = LZMA_STREAM_INIT; + std::vector unfiltered; + check(lzma_auto_decoder(&auto_dec, unlimited, 0) == LZMA_OK + && run(auto_dec, filtered.data(), filtered.size(), unfiltered) == LZMA_STREAM_END + && std::string(unfiltered.begin(), unfiltered.end()) == text, "lzma_auto_decoder restores it"); + + // The .lzma format. + lzma_stream alone = LZMA_STREAM_INIT; + std::vector legacy; + check(lzma_alone_encoder(&alone, &lzma2) == LZMA_OK && run(alone, bytes, text.size(), legacy) == LZMA_STREAM_END, + "lzma_alone_encoder writes .lzma"); + + // Checks of "123456789" as their specifications publish them. + const auto* digits = reinterpret_cast("123456789"); + check(lzma_crc32(digits, 9, 0) == 0xCBF43926u, "lzma_crc32 of 123456789"); + check(lzma_crc64(digits, 9, 0) == 0x995DC9BBDF1939FAull, "lzma_crc64 of 123456789"); + check(lzma_check_is_supported(LZMA_CHECK_SHA256) && lzma_check_size(LZMA_CHECK_SHA256) == 32, "SHA-256 is built"); + + // Errors, macros and the version. + lzma_stream bad = LZMA_STREAM_INIT; + std::vector nothing; + const std::uint8_t garbage[] = "this is not an xz stream"; + check(lzma_stream_decoder(&bad, unlimited, 0) == LZMA_OK + && run(bad, garbage, sizeof garbage, nothing) == LZMA_FORMAT_ERROR, "garbage is LZMA_FORMAT_ERROR"); + check(lzma_vli_is_valid(m::LZMA_VLI_MAX) && !lzma_vli_is_valid(m::LZMA_VLI_MAX + 1), "lzma_vli_is_valid"); + check(lzma_version_number() == m::LZMA_VERSION && std::string_view(lzma_version_string()) == m::LZMA_VERSION_STRING, + "the library is the headers' version"); + std::println("{} failures", failures); + return failures != 0; +} diff --git a/tests/header_mix_test.cpp b/tests/header_mix_test.cpp new file mode 100644 index 0000000..67a3329 --- /dev/null +++ b/tests/header_mix_test.cpp @@ -0,0 +1,15 @@ +// The header and the module in one translation unit: the same entities, so a +// stream set up through one spelling is ended through the other. +#include +import std; +import mcpplibs.mwrap.xz; + +int main() +{ + lzma_stream s = LZMA_STREAM_INIT; // the header's macro + const bool init = ::lzma_easy_encoder(&s, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64) == LZMA_OK; + lzma_end(&s); // the module's + const bool same = &::lzma_code == &lzma_code; + std::println("{}", init && same ? "header and module agree" : "FAIL"); + return init && same ? 0 : 1; +} diff --git a/tests/linked.cpp b/tests/linked.cpp new file mode 100644 index 0000000..bb1c4ff --- /dev/null +++ b/tests/linked.cpp @@ -0,0 +1,125 @@ +// Every function that mcpplibs.mwrap.xz exports, by address: the program links only +// where compat.xz defines each one. Written by tools/generate.py. +import std; +import mcpplibs.mwrap.xz; + +int main() +{ + // volatile: an optimiser may not drop a reference, so each one reaches the link. + const void* volatile functions[] = { + reinterpret_cast(&lzma_alone_decoder), + reinterpret_cast(&lzma_alone_encoder), + reinterpret_cast(&lzma_auto_decoder), + reinterpret_cast(&lzma_bcj_arm64_decode), + reinterpret_cast(&lzma_bcj_arm64_encode), + reinterpret_cast(&lzma_bcj_riscv_decode), + reinterpret_cast(&lzma_bcj_riscv_encode), + reinterpret_cast(&lzma_bcj_x86_decode), + reinterpret_cast(&lzma_bcj_x86_encode), + reinterpret_cast(&lzma_block_buffer_bound), + reinterpret_cast(&lzma_block_buffer_decode), + reinterpret_cast(&lzma_block_buffer_encode), + reinterpret_cast(&lzma_block_compressed_size), + reinterpret_cast(&lzma_block_decoder), + reinterpret_cast(&lzma_block_encoder), + reinterpret_cast(&lzma_block_header_decode), + reinterpret_cast(&lzma_block_header_encode), + reinterpret_cast(&lzma_block_header_size), + reinterpret_cast(&lzma_block_total_size), + reinterpret_cast(&lzma_block_uncomp_encode), + reinterpret_cast(&lzma_block_unpadded_size), + reinterpret_cast(&lzma_check_is_supported), + reinterpret_cast(&lzma_check_size), + reinterpret_cast(&lzma_code), + reinterpret_cast(&lzma_crc32), + reinterpret_cast(&lzma_crc64), + reinterpret_cast(&lzma_easy_buffer_encode), + reinterpret_cast(&lzma_easy_decoder_memusage), + reinterpret_cast(&lzma_easy_encoder), + reinterpret_cast(&lzma_easy_encoder_memusage), + reinterpret_cast(&lzma_end), + reinterpret_cast(&lzma_file_info_decoder), + reinterpret_cast(&lzma_filter_decoder_is_supported), + reinterpret_cast(&lzma_filter_encoder_is_supported), + reinterpret_cast(&lzma_filter_flags_decode), + reinterpret_cast(&lzma_filter_flags_encode), + reinterpret_cast(&lzma_filter_flags_size), + reinterpret_cast(&lzma_filters_copy), + reinterpret_cast(&lzma_filters_free), + reinterpret_cast(&lzma_filters_update), + reinterpret_cast(&lzma_get_check), + reinterpret_cast(&lzma_get_progress), + reinterpret_cast(&lzma_index_append), + reinterpret_cast(&lzma_index_block_count), + reinterpret_cast(&lzma_index_buffer_decode), + reinterpret_cast(&lzma_index_buffer_encode), + reinterpret_cast(&lzma_index_cat), + reinterpret_cast(&lzma_index_checks), + reinterpret_cast(&lzma_index_decoder), + reinterpret_cast(&lzma_index_dup), + reinterpret_cast(&lzma_index_encoder), + reinterpret_cast(&lzma_index_end), + reinterpret_cast(&lzma_index_file_size), + reinterpret_cast(&lzma_index_hash_append), + reinterpret_cast(&lzma_index_hash_decode), + reinterpret_cast(&lzma_index_hash_end), + reinterpret_cast(&lzma_index_hash_init), + reinterpret_cast(&lzma_index_hash_size), + reinterpret_cast(&lzma_index_init), + reinterpret_cast(&lzma_index_iter_init), + reinterpret_cast(&lzma_index_iter_locate), + reinterpret_cast(&lzma_index_iter_next), + reinterpret_cast(&lzma_index_iter_rewind), + reinterpret_cast(&lzma_index_memusage), + reinterpret_cast(&lzma_index_memused), + reinterpret_cast(&lzma_index_size), + reinterpret_cast(&lzma_index_stream_count), + reinterpret_cast(&lzma_index_stream_flags), + reinterpret_cast(&lzma_index_stream_padding), + reinterpret_cast(&lzma_index_stream_size), + reinterpret_cast(&lzma_index_total_size), + reinterpret_cast(&lzma_index_uncompressed_size), + reinterpret_cast(&lzma_lzip_decoder), + reinterpret_cast(&lzma_lzma_preset), + reinterpret_cast(&lzma_memlimit_get), + reinterpret_cast(&lzma_memlimit_set), + reinterpret_cast(&lzma_memusage), + reinterpret_cast(&lzma_mf_is_supported), + reinterpret_cast(&lzma_microlzma_decoder), + reinterpret_cast(&lzma_microlzma_encoder), + reinterpret_cast(&lzma_mode_is_supported), + reinterpret_cast(&lzma_mt_block_size), + reinterpret_cast(&lzma_physmem), + reinterpret_cast(&lzma_properties_decode), + reinterpret_cast(&lzma_properties_encode), + reinterpret_cast(&lzma_properties_size), + reinterpret_cast(&lzma_raw_buffer_decode), + reinterpret_cast(&lzma_raw_buffer_encode), + reinterpret_cast(&lzma_raw_decoder), + reinterpret_cast(&lzma_raw_decoder_memusage), + reinterpret_cast(&lzma_raw_encoder), + reinterpret_cast(&lzma_raw_encoder_memusage), + reinterpret_cast(&lzma_str_from_filters), + reinterpret_cast(&lzma_str_list_filters), + reinterpret_cast(&lzma_str_to_filters), + reinterpret_cast(&lzma_stream_buffer_bound), + reinterpret_cast(&lzma_stream_buffer_decode), + reinterpret_cast(&lzma_stream_buffer_encode), + reinterpret_cast(&lzma_stream_decoder), + reinterpret_cast(&lzma_stream_encoder), + reinterpret_cast(&lzma_stream_flags_compare), + reinterpret_cast(&lzma_stream_footer_decode), + reinterpret_cast(&lzma_stream_footer_encode), + reinterpret_cast(&lzma_stream_header_decode), + reinterpret_cast(&lzma_stream_header_encode), + reinterpret_cast(&lzma_version_number), + reinterpret_cast(&lzma_version_string), + reinterpret_cast(&lzma_vli_decode), + reinterpret_cast(&lzma_vli_encode), + reinterpret_cast(&lzma_vli_size), + }; + int missing = 0; + for (const void* f : functions) missing += f == nullptr; + std::println("110 functions linked, {} null", missing); + return missing != 0; +} diff --git a/tests/macros.cpp b/tests/macros.cpp new file mode 100644 index 0000000..fd669bf --- /dev/null +++ b/tests/macros.cpp @@ -0,0 +1,225 @@ +// Each constant and type of mcpplibs.mwrap.xz.macros equals its macro. A macro +// expands even in a qualified name, so the values of the headers are taken under other +// names and the macros undefined before the mapped names are used. Written by +// tools/generate.py. +#include + +namespace header { +inline constexpr auto value_LZMA_BACKWARD_SIZE_MAX = (LZMA_BACKWARD_SIZE_MAX); +inline constexpr auto value_LZMA_BACKWARD_SIZE_MIN = (LZMA_BACKWARD_SIZE_MIN); +inline constexpr auto value_LZMA_BLOCK_HEADER_SIZE_MAX = (LZMA_BLOCK_HEADER_SIZE_MAX); +inline constexpr auto value_LZMA_BLOCK_HEADER_SIZE_MIN = (LZMA_BLOCK_HEADER_SIZE_MIN); +inline constexpr auto value_LZMA_CHECK_ID_MAX = (LZMA_CHECK_ID_MAX); +inline constexpr auto value_LZMA_CHECK_SIZE_MAX = (LZMA_CHECK_SIZE_MAX); +inline constexpr auto value_LZMA_CONCATENATED = (LZMA_CONCATENATED); +inline constexpr auto value_LZMA_DELTA_DIST_MAX = (LZMA_DELTA_DIST_MAX); +inline constexpr auto value_LZMA_DELTA_DIST_MIN = (LZMA_DELTA_DIST_MIN); +inline constexpr auto value_LZMA_DICT_SIZE_DEFAULT = (LZMA_DICT_SIZE_DEFAULT); +inline constexpr auto value_LZMA_DICT_SIZE_MIN = (LZMA_DICT_SIZE_MIN); +inline constexpr auto value_LZMA_FAIL_FAST = (LZMA_FAIL_FAST); +inline constexpr auto value_LZMA_FILTERS_MAX = (LZMA_FILTERS_MAX); +inline constexpr auto value_LZMA_FILTER_ARM = (LZMA_FILTER_ARM); +inline constexpr auto value_LZMA_FILTER_ARM64 = (LZMA_FILTER_ARM64); +inline constexpr auto value_LZMA_FILTER_ARMTHUMB = (LZMA_FILTER_ARMTHUMB); +inline constexpr auto value_LZMA_FILTER_DELTA = (LZMA_FILTER_DELTA); +inline constexpr auto value_LZMA_FILTER_IA64 = (LZMA_FILTER_IA64); +inline constexpr auto value_LZMA_FILTER_LZMA1 = (LZMA_FILTER_LZMA1); +inline constexpr auto value_LZMA_FILTER_LZMA1EXT = (LZMA_FILTER_LZMA1EXT); +inline constexpr auto value_LZMA_FILTER_LZMA2 = (LZMA_FILTER_LZMA2); +inline constexpr auto value_LZMA_FILTER_POWERPC = (LZMA_FILTER_POWERPC); +inline constexpr auto value_LZMA_FILTER_RISCV = (LZMA_FILTER_RISCV); +inline constexpr auto value_LZMA_FILTER_SPARC = (LZMA_FILTER_SPARC); +inline constexpr auto value_LZMA_FILTER_X86 = (LZMA_FILTER_X86); +inline constexpr auto value_LZMA_IGNORE_CHECK = (LZMA_IGNORE_CHECK); +inline constexpr auto value_LZMA_INDEX_CHECK_MASK_CRC32 = (LZMA_INDEX_CHECK_MASK_CRC32); +inline constexpr auto value_LZMA_INDEX_CHECK_MASK_CRC64 = (LZMA_INDEX_CHECK_MASK_CRC64); +inline constexpr auto value_LZMA_INDEX_CHECK_MASK_NONE = (LZMA_INDEX_CHECK_MASK_NONE); +inline constexpr auto value_LZMA_INDEX_CHECK_MASK_SHA256 = (LZMA_INDEX_CHECK_MASK_SHA256); +inline constexpr auto value_LZMA_LCLP_MAX = (LZMA_LCLP_MAX); +inline constexpr auto value_LZMA_LCLP_MIN = (LZMA_LCLP_MIN); +inline constexpr auto value_LZMA_LC_DEFAULT = (LZMA_LC_DEFAULT); +inline constexpr auto value_LZMA_LP_DEFAULT = (LZMA_LP_DEFAULT); +inline constexpr auto value_LZMA_LZMA1EXT_ALLOW_EOPM = (LZMA_LZMA1EXT_ALLOW_EOPM); +inline constexpr auto value_LZMA_PB_DEFAULT = (LZMA_PB_DEFAULT); +inline constexpr auto value_LZMA_PB_MAX = (LZMA_PB_MAX); +inline constexpr auto value_LZMA_PB_MIN = (LZMA_PB_MIN); +inline constexpr auto value_LZMA_PRESET_DEFAULT = (LZMA_PRESET_DEFAULT); +inline constexpr auto value_LZMA_PRESET_EXTREME = (LZMA_PRESET_EXTREME); +inline constexpr auto value_LZMA_PRESET_LEVEL_MASK = (LZMA_PRESET_LEVEL_MASK); +inline constexpr auto value_LZMA_STREAM_HEADER_SIZE = (LZMA_STREAM_HEADER_SIZE); +inline constexpr auto value_LZMA_STR_ALL_FILTERS = (LZMA_STR_ALL_FILTERS); +inline constexpr auto value_LZMA_STR_DECODER = (LZMA_STR_DECODER); +inline constexpr auto value_LZMA_STR_ENCODER = (LZMA_STR_ENCODER); +inline constexpr auto value_LZMA_STR_GETOPT_LONG = (LZMA_STR_GETOPT_LONG); +inline constexpr auto value_LZMA_STR_NO_SPACES = (LZMA_STR_NO_SPACES); +inline constexpr auto value_LZMA_STR_NO_VALIDATION = (LZMA_STR_NO_VALIDATION); +inline constexpr auto value_LZMA_TELL_ANY_CHECK = (LZMA_TELL_ANY_CHECK); +inline constexpr auto value_LZMA_TELL_NO_CHECK = (LZMA_TELL_NO_CHECK); +inline constexpr auto value_LZMA_TELL_UNSUPPORTED_CHECK = (LZMA_TELL_UNSUPPORTED_CHECK); +inline constexpr auto value_LZMA_VERSION = (LZMA_VERSION); +inline constexpr auto value_LZMA_VERSION_COMMIT = (LZMA_VERSION_COMMIT); +inline constexpr auto value_LZMA_VERSION_MAJOR = (LZMA_VERSION_MAJOR); +inline constexpr auto value_LZMA_VERSION_MINOR = (LZMA_VERSION_MINOR); +inline constexpr auto value_LZMA_VERSION_PATCH = (LZMA_VERSION_PATCH); +inline constexpr auto value_LZMA_VERSION_STABILITY = (LZMA_VERSION_STABILITY); +inline constexpr auto value_LZMA_VERSION_STABILITY_ALPHA = (LZMA_VERSION_STABILITY_ALPHA); +inline constexpr auto value_LZMA_VERSION_STABILITY_BETA = (LZMA_VERSION_STABILITY_BETA); +inline constexpr auto value_LZMA_VERSION_STABILITY_STABLE = (LZMA_VERSION_STABILITY_STABLE); +inline constexpr auto value_LZMA_VERSION_STABILITY_STRING = (LZMA_VERSION_STABILITY_STRING); +inline constexpr auto value_LZMA_VERSION_STRING = (LZMA_VERSION_STRING); +inline constexpr auto value_LZMA_VLI_BYTES_MAX = (LZMA_VLI_BYTES_MAX); +inline constexpr auto value_LZMA_VLI_MAX = (LZMA_VLI_MAX); +inline constexpr auto value_LZMA_VLI_UNKNOWN = (LZMA_VLI_UNKNOWN); +} +#undef LZMA_BACKWARD_SIZE_MAX +#undef LZMA_BACKWARD_SIZE_MIN +#undef LZMA_BLOCK_HEADER_SIZE_MAX +#undef LZMA_BLOCK_HEADER_SIZE_MIN +#undef LZMA_CHECK_ID_MAX +#undef LZMA_CHECK_SIZE_MAX +#undef LZMA_CONCATENATED +#undef LZMA_DELTA_DIST_MAX +#undef LZMA_DELTA_DIST_MIN +#undef LZMA_DICT_SIZE_DEFAULT +#undef LZMA_DICT_SIZE_MIN +#undef LZMA_FAIL_FAST +#undef LZMA_FILTERS_MAX +#undef LZMA_FILTER_ARM +#undef LZMA_FILTER_ARM64 +#undef LZMA_FILTER_ARMTHUMB +#undef LZMA_FILTER_DELTA +#undef LZMA_FILTER_IA64 +#undef LZMA_FILTER_LZMA1 +#undef LZMA_FILTER_LZMA1EXT +#undef LZMA_FILTER_LZMA2 +#undef LZMA_FILTER_POWERPC +#undef LZMA_FILTER_RISCV +#undef LZMA_FILTER_SPARC +#undef LZMA_FILTER_X86 +#undef LZMA_IGNORE_CHECK +#undef LZMA_INDEX_CHECK_MASK_CRC32 +#undef LZMA_INDEX_CHECK_MASK_CRC64 +#undef LZMA_INDEX_CHECK_MASK_NONE +#undef LZMA_INDEX_CHECK_MASK_SHA256 +#undef LZMA_LCLP_MAX +#undef LZMA_LCLP_MIN +#undef LZMA_LC_DEFAULT +#undef LZMA_LP_DEFAULT +#undef LZMA_LZMA1EXT_ALLOW_EOPM +#undef LZMA_PB_DEFAULT +#undef LZMA_PB_MAX +#undef LZMA_PB_MIN +#undef LZMA_PRESET_DEFAULT +#undef LZMA_PRESET_EXTREME +#undef LZMA_PRESET_LEVEL_MASK +#undef LZMA_STREAM_HEADER_SIZE +#undef LZMA_STR_ALL_FILTERS +#undef LZMA_STR_DECODER +#undef LZMA_STR_ENCODER +#undef LZMA_STR_GETOPT_LONG +#undef LZMA_STR_NO_SPACES +#undef LZMA_STR_NO_VALIDATION +#undef LZMA_TELL_ANY_CHECK +#undef LZMA_TELL_NO_CHECK +#undef LZMA_TELL_UNSUPPORTED_CHECK +#undef LZMA_VERSION +#undef LZMA_VERSION_COMMIT +#undef LZMA_VERSION_MAJOR +#undef LZMA_VERSION_MINOR +#undef LZMA_VERSION_PATCH +#undef LZMA_VERSION_STABILITY +#undef LZMA_VERSION_STABILITY_ALPHA +#undef LZMA_VERSION_STABILITY_BETA +#undef LZMA_VERSION_STABILITY_STABLE +#undef LZMA_VERSION_STABILITY_STRING +#undef LZMA_VERSION_STRING +#undef LZMA_VLI_BYTES_MAX +#undef LZMA_VLI_MAX +#undef LZMA_VLI_UNKNOWN + +import std; +import mcpplibs.mwrap.xz.macros; + +namespace m = mcpplibs::mwrap::xz::macros; + + +template +bool same(A a, B b) +{ + if constexpr (std::is_convertible_v && std::is_convertible_v) + return std::string_view(a) == std::string_view(b); + else + return a == b; +} + +int main() +{ + int differ = 0; + if (!same(m::LZMA_BACKWARD_SIZE_MAX, header::value_LZMA_BACKWARD_SIZE_MAX)) { std::println("FAIL LZMA_BACKWARD_SIZE_MAX"); ++differ; } + if (!same(m::LZMA_BACKWARD_SIZE_MIN, header::value_LZMA_BACKWARD_SIZE_MIN)) { std::println("FAIL LZMA_BACKWARD_SIZE_MIN"); ++differ; } + if (!same(m::LZMA_BLOCK_HEADER_SIZE_MAX, header::value_LZMA_BLOCK_HEADER_SIZE_MAX)) { std::println("FAIL LZMA_BLOCK_HEADER_SIZE_MAX"); ++differ; } + if (!same(m::LZMA_BLOCK_HEADER_SIZE_MIN, header::value_LZMA_BLOCK_HEADER_SIZE_MIN)) { std::println("FAIL LZMA_BLOCK_HEADER_SIZE_MIN"); ++differ; } + if (!same(m::LZMA_CHECK_ID_MAX, header::value_LZMA_CHECK_ID_MAX)) { std::println("FAIL LZMA_CHECK_ID_MAX"); ++differ; } + if (!same(m::LZMA_CHECK_SIZE_MAX, header::value_LZMA_CHECK_SIZE_MAX)) { std::println("FAIL LZMA_CHECK_SIZE_MAX"); ++differ; } + if (!same(m::LZMA_CONCATENATED, header::value_LZMA_CONCATENATED)) { std::println("FAIL LZMA_CONCATENATED"); ++differ; } + if (!same(m::LZMA_DELTA_DIST_MAX, header::value_LZMA_DELTA_DIST_MAX)) { std::println("FAIL LZMA_DELTA_DIST_MAX"); ++differ; } + if (!same(m::LZMA_DELTA_DIST_MIN, header::value_LZMA_DELTA_DIST_MIN)) { std::println("FAIL LZMA_DELTA_DIST_MIN"); ++differ; } + if (!same(m::LZMA_DICT_SIZE_DEFAULT, header::value_LZMA_DICT_SIZE_DEFAULT)) { std::println("FAIL LZMA_DICT_SIZE_DEFAULT"); ++differ; } + if (!same(m::LZMA_DICT_SIZE_MIN, header::value_LZMA_DICT_SIZE_MIN)) { std::println("FAIL LZMA_DICT_SIZE_MIN"); ++differ; } + if (!same(m::LZMA_FAIL_FAST, header::value_LZMA_FAIL_FAST)) { std::println("FAIL LZMA_FAIL_FAST"); ++differ; } + if (!same(m::LZMA_FILTERS_MAX, header::value_LZMA_FILTERS_MAX)) { std::println("FAIL LZMA_FILTERS_MAX"); ++differ; } + if (!same(m::LZMA_FILTER_ARM, header::value_LZMA_FILTER_ARM)) { std::println("FAIL LZMA_FILTER_ARM"); ++differ; } + if (!same(m::LZMA_FILTER_ARM64, header::value_LZMA_FILTER_ARM64)) { std::println("FAIL LZMA_FILTER_ARM64"); ++differ; } + if (!same(m::LZMA_FILTER_ARMTHUMB, header::value_LZMA_FILTER_ARMTHUMB)) { std::println("FAIL LZMA_FILTER_ARMTHUMB"); ++differ; } + if (!same(m::LZMA_FILTER_DELTA, header::value_LZMA_FILTER_DELTA)) { std::println("FAIL LZMA_FILTER_DELTA"); ++differ; } + if (!same(m::LZMA_FILTER_IA64, header::value_LZMA_FILTER_IA64)) { std::println("FAIL LZMA_FILTER_IA64"); ++differ; } + if (!same(m::LZMA_FILTER_LZMA1, header::value_LZMA_FILTER_LZMA1)) { std::println("FAIL LZMA_FILTER_LZMA1"); ++differ; } + if (!same(m::LZMA_FILTER_LZMA1EXT, header::value_LZMA_FILTER_LZMA1EXT)) { std::println("FAIL LZMA_FILTER_LZMA1EXT"); ++differ; } + if (!same(m::LZMA_FILTER_LZMA2, header::value_LZMA_FILTER_LZMA2)) { std::println("FAIL LZMA_FILTER_LZMA2"); ++differ; } + if (!same(m::LZMA_FILTER_POWERPC, header::value_LZMA_FILTER_POWERPC)) { std::println("FAIL LZMA_FILTER_POWERPC"); ++differ; } + if (!same(m::LZMA_FILTER_RISCV, header::value_LZMA_FILTER_RISCV)) { std::println("FAIL LZMA_FILTER_RISCV"); ++differ; } + if (!same(m::LZMA_FILTER_SPARC, header::value_LZMA_FILTER_SPARC)) { std::println("FAIL LZMA_FILTER_SPARC"); ++differ; } + if (!same(m::LZMA_FILTER_X86, header::value_LZMA_FILTER_X86)) { std::println("FAIL LZMA_FILTER_X86"); ++differ; } + if (!same(m::LZMA_IGNORE_CHECK, header::value_LZMA_IGNORE_CHECK)) { std::println("FAIL LZMA_IGNORE_CHECK"); ++differ; } + if (!same(m::LZMA_INDEX_CHECK_MASK_CRC32, header::value_LZMA_INDEX_CHECK_MASK_CRC32)) { std::println("FAIL LZMA_INDEX_CHECK_MASK_CRC32"); ++differ; } + if (!same(m::LZMA_INDEX_CHECK_MASK_CRC64, header::value_LZMA_INDEX_CHECK_MASK_CRC64)) { std::println("FAIL LZMA_INDEX_CHECK_MASK_CRC64"); ++differ; } + if (!same(m::LZMA_INDEX_CHECK_MASK_NONE, header::value_LZMA_INDEX_CHECK_MASK_NONE)) { std::println("FAIL LZMA_INDEX_CHECK_MASK_NONE"); ++differ; } + if (!same(m::LZMA_INDEX_CHECK_MASK_SHA256, header::value_LZMA_INDEX_CHECK_MASK_SHA256)) { std::println("FAIL LZMA_INDEX_CHECK_MASK_SHA256"); ++differ; } + if (!same(m::LZMA_LCLP_MAX, header::value_LZMA_LCLP_MAX)) { std::println("FAIL LZMA_LCLP_MAX"); ++differ; } + if (!same(m::LZMA_LCLP_MIN, header::value_LZMA_LCLP_MIN)) { std::println("FAIL LZMA_LCLP_MIN"); ++differ; } + if (!same(m::LZMA_LC_DEFAULT, header::value_LZMA_LC_DEFAULT)) { std::println("FAIL LZMA_LC_DEFAULT"); ++differ; } + if (!same(m::LZMA_LP_DEFAULT, header::value_LZMA_LP_DEFAULT)) { std::println("FAIL LZMA_LP_DEFAULT"); ++differ; } + if (!same(m::LZMA_LZMA1EXT_ALLOW_EOPM, header::value_LZMA_LZMA1EXT_ALLOW_EOPM)) { std::println("FAIL LZMA_LZMA1EXT_ALLOW_EOPM"); ++differ; } + if (!same(m::LZMA_PB_DEFAULT, header::value_LZMA_PB_DEFAULT)) { std::println("FAIL LZMA_PB_DEFAULT"); ++differ; } + if (!same(m::LZMA_PB_MAX, header::value_LZMA_PB_MAX)) { std::println("FAIL LZMA_PB_MAX"); ++differ; } + if (!same(m::LZMA_PB_MIN, header::value_LZMA_PB_MIN)) { std::println("FAIL LZMA_PB_MIN"); ++differ; } + if (!same(m::LZMA_PRESET_DEFAULT, header::value_LZMA_PRESET_DEFAULT)) { std::println("FAIL LZMA_PRESET_DEFAULT"); ++differ; } + if (!same(m::LZMA_PRESET_EXTREME, header::value_LZMA_PRESET_EXTREME)) { std::println("FAIL LZMA_PRESET_EXTREME"); ++differ; } + if (!same(m::LZMA_PRESET_LEVEL_MASK, header::value_LZMA_PRESET_LEVEL_MASK)) { std::println("FAIL LZMA_PRESET_LEVEL_MASK"); ++differ; } + if (!same(m::LZMA_STREAM_HEADER_SIZE, header::value_LZMA_STREAM_HEADER_SIZE)) { std::println("FAIL LZMA_STREAM_HEADER_SIZE"); ++differ; } + if (!same(m::LZMA_STR_ALL_FILTERS, header::value_LZMA_STR_ALL_FILTERS)) { std::println("FAIL LZMA_STR_ALL_FILTERS"); ++differ; } + if (!same(m::LZMA_STR_DECODER, header::value_LZMA_STR_DECODER)) { std::println("FAIL LZMA_STR_DECODER"); ++differ; } + if (!same(m::LZMA_STR_ENCODER, header::value_LZMA_STR_ENCODER)) { std::println("FAIL LZMA_STR_ENCODER"); ++differ; } + if (!same(m::LZMA_STR_GETOPT_LONG, header::value_LZMA_STR_GETOPT_LONG)) { std::println("FAIL LZMA_STR_GETOPT_LONG"); ++differ; } + if (!same(m::LZMA_STR_NO_SPACES, header::value_LZMA_STR_NO_SPACES)) { std::println("FAIL LZMA_STR_NO_SPACES"); ++differ; } + if (!same(m::LZMA_STR_NO_VALIDATION, header::value_LZMA_STR_NO_VALIDATION)) { std::println("FAIL LZMA_STR_NO_VALIDATION"); ++differ; } + if (!same(m::LZMA_TELL_ANY_CHECK, header::value_LZMA_TELL_ANY_CHECK)) { std::println("FAIL LZMA_TELL_ANY_CHECK"); ++differ; } + if (!same(m::LZMA_TELL_NO_CHECK, header::value_LZMA_TELL_NO_CHECK)) { std::println("FAIL LZMA_TELL_NO_CHECK"); ++differ; } + if (!same(m::LZMA_TELL_UNSUPPORTED_CHECK, header::value_LZMA_TELL_UNSUPPORTED_CHECK)) { std::println("FAIL LZMA_TELL_UNSUPPORTED_CHECK"); ++differ; } + if (!same(m::LZMA_VERSION, header::value_LZMA_VERSION)) { std::println("FAIL LZMA_VERSION"); ++differ; } + if (!same(m::LZMA_VERSION_COMMIT, header::value_LZMA_VERSION_COMMIT)) { std::println("FAIL LZMA_VERSION_COMMIT"); ++differ; } + if (!same(m::LZMA_VERSION_MAJOR, header::value_LZMA_VERSION_MAJOR)) { std::println("FAIL LZMA_VERSION_MAJOR"); ++differ; } + if (!same(m::LZMA_VERSION_MINOR, header::value_LZMA_VERSION_MINOR)) { std::println("FAIL LZMA_VERSION_MINOR"); ++differ; } + if (!same(m::LZMA_VERSION_PATCH, header::value_LZMA_VERSION_PATCH)) { std::println("FAIL LZMA_VERSION_PATCH"); ++differ; } + if (!same(m::LZMA_VERSION_STABILITY, header::value_LZMA_VERSION_STABILITY)) { std::println("FAIL LZMA_VERSION_STABILITY"); ++differ; } + if (!same(m::LZMA_VERSION_STABILITY_ALPHA, header::value_LZMA_VERSION_STABILITY_ALPHA)) { std::println("FAIL LZMA_VERSION_STABILITY_ALPHA"); ++differ; } + if (!same(m::LZMA_VERSION_STABILITY_BETA, header::value_LZMA_VERSION_STABILITY_BETA)) { std::println("FAIL LZMA_VERSION_STABILITY_BETA"); ++differ; } + if (!same(m::LZMA_VERSION_STABILITY_STABLE, header::value_LZMA_VERSION_STABILITY_STABLE)) { std::println("FAIL LZMA_VERSION_STABILITY_STABLE"); ++differ; } + if (!same(m::LZMA_VERSION_STABILITY_STRING, header::value_LZMA_VERSION_STABILITY_STRING)) { std::println("FAIL LZMA_VERSION_STABILITY_STRING"); ++differ; } + if (!same(m::LZMA_VERSION_STRING, header::value_LZMA_VERSION_STRING)) { std::println("FAIL LZMA_VERSION_STRING"); ++differ; } + if (!same(m::LZMA_VLI_BYTES_MAX, header::value_LZMA_VLI_BYTES_MAX)) { std::println("FAIL LZMA_VLI_BYTES_MAX"); ++differ; } + if (!same(m::LZMA_VLI_MAX, header::value_LZMA_VLI_MAX)) { std::println("FAIL LZMA_VLI_MAX"); ++differ; } + if (!same(m::LZMA_VLI_UNKNOWN, header::value_LZMA_VLI_UNKNOWN)) { std::println("FAIL LZMA_VLI_UNKNOWN"); ++differ; } + std::println("65 constants and 0 types compared, {} differ", differ); + return differ != 0; +} diff --git a/tools/api.py b/tools/api.py new file mode 100644 index 0000000..1ee5ff8 --- /dev/null +++ b/tools/api.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +"""The public API of a set of C headers, as the compiler reads them. + +`extract` returns the functions, variables, types and enumerators that the +owned headers declare, and the macros that they define, as the compiler's AST and +preprocessor see them in a C++23 translation unit including the headers. +`constant_macros` and `type_macros` select the object-like macros whose +expansion is a constant expression or a type. tools/generate.py writes the +module from these sets and audits it against them. +""" +import json +from pathlib import Path +import re +import subprocess + + +def _run(cmd, stdin): + return subprocess.run(cmd, input=stdin, capture_output=True, text=True, check=True).stdout + + +def _owned(path, owned): + path = path.replace('\\', '/') + return any(path == name or path.endswith('/' + name) for name in owned) + + +def extract(compiler, flags, headers, owned=None): + """`flags`: the compiler's include, define and target flags for the module; + `owned`: the header files whose declarations are the API (default: `headers`).""" + owned = list(owned or headers) + source = ''.join(f'#include <{h}>\n' for h in headers) + flags = ['-x', 'c++', '-std=c++23', *flags] + ast = json.loads(_run([compiler, *flags, '-fsyntax-only', '-Xclang', '-ast-dump=json', '-'], source)) + api = {'functions': set(), 'internal': set(), 'variables': set(), 'types': set(), 'enumerators': set()} + current = [''] + + def mine(node): + # The AST names a file only where it changes from the previous node. + loc = node.get('loc', {}) + for where in (loc, loc.get('spellingLoc', {}), loc.get('expansionLoc', {})): + if 'file' in where: + current[0] = where['file'] + return _owned(current[0], owned) + + def walk(nodes): + for node in nodes: + kind = node.get('kind') + if kind == 'LinkageSpecDecl': + mine(node) + walk(node.get('inner', [])) + continue + if not mine(node) or node.get('isImplicit'): + continue + name = node.get('name') + if kind == 'EnumDecl': + api['enumerators'] |= {e['name'] for e in node.get('inner', []) if e.get('kind') == 'EnumConstantDecl'} + if not name: + continue + if kind == 'FunctionDecl': + api['internal' if node.get('storageClass') == 'static' else 'functions'].add(name) + elif kind == 'VarDecl' and node.get('storageClass') == 'extern': + api['variables'].add(name) + elif kind in ('TypedefDecl', 'TypeAliasDecl', 'RecordDecl', 'CXXRecordDecl', 'EnumDecl'): + api['types'].add(name) + walk(ast.get('inner', [])) + api['functions'] -= api['internal'] + + macros = {} + current = '' + for line in _run([compiler, *flags, '-E', '-dD', '-'], source).splitlines(): + marker = re.match(r'#\s*(?:line\s+)?\d+\s+"([^"]*)"', line) + if marker: + current = marker.group(1) + continue + define = re.match(r'#define\s+([A-Za-z_][A-Za-z_0-9]*)(\([^)]*\))?\s*(.*)$', line) + if define and _owned(current, owned): + macros[define.group(1)] = {'params': define.group(2), 'body': define.group(3).strip(), 'file': current} + undefine = re.match(r'#undef\s+([A-Za-z_][A-Za-z_0-9]*)', line) + if undefine: + macros.pop(undefine.group(1), None) + return api, macros + + +def guards(macros): + """The include guards among `macros`. An empty macro that the first + conditional directive of its file tests (`#ifndef G` or `#if !defined(G)`), + or a macro named like a guard (`..._H`, `..._H_`, `..._INCLUDED`) + that its file defines right after `#ifndef G`.""" + found = set() + for name, macro in macros.items(): + if macro['params'] is not None: + continue + try: + text = Path(macro['file']).read_text(encoding='utf-8', errors='replace') + except OSError: + continue + text = re.sub(r'/\*.*?\*/', ' ', text, flags=re.S) + first = re.search(r'^\s*#\s*if.*$', text, flags=re.M) + tested_first = first and re.fullmatch( + r'\s*#\s*(?:ifndef\s+' + name + r'|if\s+!\s*defined\s*\(?\s*' + name + r'\s*\)?)\s*', first.group(0)) + named = re.search(r'_H(?:_\w+)?$|_INCLUDED$', name) and re.search( + r'^\s*#\s*ifndef\s+' + name + r'\s*\n\s*#\s*define\s+' + name + r'\b', text, flags=re.M) + if (tested_first and not macro['body']) or named: + found.add(name) + return found + + +def _accepted(compiler, flags, headers, names, form): + """The names for which `form` (with {i} and {n}) compiles after the headers: + one translation unit, dropping the lines with an error until none remain.""" + head = ''.join(f'#include <{h}>\n' for h in headers) + pending = sorted(names) + while pending: + probe = head + '#line 1 "mwrap-probe"\n' + ''.join(form.format(i=i, n=n) + '\n' for i, n in enumerate(pending)) + result = subprocess.run([compiler, '-x', 'c++', '-std=c++23', *flags, '-fsyntax-only', '-ferror-limit=0', '-'], + input=probe, capture_output=True, text=True) + if result.returncode == 0: + return set(pending) + bad = {int(m.group(1)) - 1 for m in re.finditer(r'mwrap-probe:(\d+):\d+: error', result.stderr)} + if not bad: + raise RuntimeError(result.stderr[-2000:]) + pending = [n for i, n in enumerate(pending) if i not in bad] + return set() + + +def constant_macros(compiler, flags, headers, macros): + """The object-like macros among `macros` whose expansion is a constant expression.""" + names = [n for n, m in macros.items() if m['params'] is None and m['body']] + return _accepted(compiler, flags, headers, names, 'inline constexpr auto mwrap_probe_{i} = ({n});') + + +def type_macros(compiler, flags, headers, macros): + """The object-like macros among `macros` whose expansion is a type.""" + names = [n for n, m in macros.items() if m['params'] is None and m['body']] + return _accepted(compiler, flags, headers, names, 'using mwrap_probe_{i} = {n};') diff --git a/tools/check_mwrap_release.py b/tools/check_mwrap_release.py new file mode 100644 index 0000000..9bedddb --- /dev/null +++ b/tools/check_mwrap_release.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +"""Check the immutable mwrap release coordinate and bilingual entry pages.""" +import argparse +import hashlib +import json +from pathlib import Path +import re +import tomllib + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--tag', help='release tag; omitted during PR checks') + args = parser.parse_args() + root = Path(__file__).resolve().parents[1] + package = tomllib.loads((root / 'mcpp.toml').read_text(encoding="utf-8"))['package'] + assert package['namespace'] == 'mcpplibs.mwrap', package + assert re.fullmatch(r'[A-Za-z_][A-Za-z_0-9]*', package['name']), package['name'] + upstream = package['metadata']['mwrap']['upstream-version'] + version = package['version'] + assert version == upstream or re.fullmatch(re.escape(upstream) + r'\.[1-9][0-9]*', version), (version, upstream) + if args.tag: + assert args.tag == 'mwrap-' + version, (args.tag, version) + assert package['license'] == 'Apache-2.0', package['license'] + assert package['authors'] == ['mcpp-community'], package.get('authors') + license_text = (root / 'LICENSE').read_text(encoding='utf-8') + assert 'Apache License' in license_text and 'Version 2.0' in license_text + assert 'mcpp-community' in (root / 'NOTICE').read_text(encoding='utf-8') + documents = json.loads((root / 'upstream/documents.json').read_text(encoding='utf-8'))['documents'] + assert any(Path(d['path']).name.startswith('README') for d in documents) + assert any(Path(d['path']).name.startswith(('LICENSE', 'COPYING')) for d in documents) + deployed = {entry['from'] for entry in tomllib.loads((root / 'mcpp.toml').read_text(encoding='utf-8')).get('runtime', {}).get('deploy', [])} + assert {'LICENSE', 'NOTICE'} <= deployed + for document in documents: + path = Path(document['path']) + assert not path.is_absolute() and '..' not in path.parts and path.parts[0] == 'upstream' + assert hashlib.sha256((root / path).read_bytes()).hexdigest() == document['sha256'], path + assert document['source'] and path.as_posix() in deployed, path + identity = package['namespace'] + '.' + package['name'] + for name, other in [('README.md', 'README.zh.md'), ('README.zh.md', 'README.md')]: + text = (root / name).read_text(encoding="utf-8") + assert len(text.splitlines()) <= 150, name + assert '](' + other + ')' in '\n'.join(text.splitlines()[:12]), name + assert identity in text and version in text, name + assert text.count('```') % 2 == 0, name + print(identity + '@' + version) + + +if __name__ == '__main__': + main() diff --git a/tools/generate.py b/tools/generate.py new file mode 100644 index 0000000..bc63b7a --- /dev/null +++ b/tools/generate.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python3 +"""Write, or with --check audit, the parts of the wrapper that follow from the headers. + +tools/surface.toml names the headers and classifies what the compiler cannot: +the configuration that consumers share with the compat package (`defines`, +defined before the headers), the macros that are how the headers are written +(`syntax`), the names that only some targets declare (`conditional`), and the +functions that the compat package does not build (`unbuilt`, not exported). +Everything else follows from the headers as the compiler reads them +(tools/api.py), with the include, define and target flags of the last +`mcpp build` (target/**/compile_commands.json): + + src/.cppm every function, variable, type and enumerator, + `export using ::name` + src/macros.cppm every object-like macro that is a constant + expression or a type, under its own name in + mcpplibs::mwrap::::macros + include/mwrap//macros.hpp every other macro, upstream's definition + tests/macros.cpp each mapped constant and type against its macro + tests/linked.cpp the address of each exported function, so that + the link proves the compat package defines it + +The include guards and reserved identifiers (`__x`, `_X`) are recognised and +omitted. --check renders the same files in memory: on the host named by +`generated-on` they must be byte-identical; on every host, each name the +headers declare there must be exported, mapped, kept or classified, and each +unconditional export must be declared there. +The compiler is the clang of the build; run it after `mcpp build` with LLVM. +""" +import argparse +import json +from pathlib import Path +import re +import sys +import tomllib + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +import api # noqa: E402 + +ROOT = Path(__file__).resolve().parents[1] +KEEP_JOINED = ('-I', '-isystem', '-idirafter', '-iquote', '-D', '-U', '--sysroot=', '-isysroot', '--target=', + '-target', '-include', '-imsvc', '-fms-compatibility-version=', '-fms-runtime-lib=', '-fms-extensions') +KEEP_ALONE = ('-nostdinc++', '-nostdlibinc', '-nostdinc', '--no-default-config') +TAKES_VALUE = ('-I', '-isystem', '-idirafter', '-iquote', '-D', '-U', '-isysroot', '-target', '-include', '-imsvc') + + +def build_flags(name): + """The clang of the newest LLVM build and its preprocessing and target flags for src/.cppm.""" + databases = sorted((ROOT / 'target').glob('**/compile_commands.json'), key=lambda p: p.stat().st_mtime) + for database in reversed(databases): + for entry in json.loads(database.read_text(encoding='utf-8')): + words = entry.get('arguments') or entry['command'].split() + if Path(entry['file']).as_posix().endswith(f'src/{name}.cppm') and 'clang' in Path(words[0]).name: + flags, rest = [], iter(words[1:]) + for word in rest: + if word in TAKES_VALUE: + flags += [word, next(rest)] + elif word in KEEP_ALONE or word.startswith(KEEP_JOINED): + flags.append(word) + return words[0], flags + sys.exit(f'no LLVM build of src/{name}.cppm; run `mcpp build --toolchain llvm@23.1.3` first') + + +def qualify(body, mapped, scope): + return re.sub(r'(?' for h in cfg['headers']]) + declared = surface['functions'] | surface['variables'] | surface['types'] | surface['enumerators'] + exported = sorted(declared - set(conditional) - set(cfg.get('unbuilt', []))) + cap = f'mwrap_{name}_capture' + + module = [f"// mcpplibs.mwrap.{name} --- {cfg['title']} as a C++ module. Every exported name is", + "// the upstream entity of the headers below, unchanged, in the global namespace where", + f"// upstream declares it; the root also re-exports the macro mapping. The implementation", + f"// is compat.{cfg['compat']} {cfg['version']}. Written by tools/generate.py from the headers.", + 'module;', includes, '', f'export module mcpplibs.mwrap.{name};', '', + f'export import mcpplibs.mwrap.{name}.macros;', '', 'export {'] + module += [f'using ::{n};' for n in exported] + module.append('}') + for condition in sorted(set(conditional.values())): + module += ['', f'#if {condition}', 'export {'] + module += [f'using ::{n};' for n in sorted(conditional) if conditional[n] == condition] + module += ['}', '#endif'] + + mapped = sorted(constants | types) + macros = [f"// mcpplibs.mwrap.{name}.macros --- the macros of the headers that are a constant or a", + "// type, under their original names in " + scope + "::macros, each equal to", + "// the macro where the module is built (a named module cannot export a macro). The", + f"// other macros are in . Written by tools/generate.py.", + 'module;', includes, '', '// The values, taken under other names while the macros are defined.', + f'namespace {cap} {{'] + macros += [f'inline constexpr auto value_{n} = ({n});' if n in constants else f'using type_{n} = {n};' + for n in mapped] + macros += ['}'] + [f'#undef {n}' for n in mapped] + macros += ['', f'export module mcpplibs.mwrap.{name}.macros;', '', f'export namespace {scope}::macros {{'] + macros += [f'inline constexpr auto {n} = {cap}::value_{n};' if n in constants else f'using {n} = {cap}::type_{n};' + for n in mapped] + macros.append('}') + + guard = f'MWRAP_{name.upper()}_MACROS_HPP' + header = [f'// --- the macros of the headers that are neither a constant', + '// nor a type: they take arguments, or call a function. A named module cannot export a', + f'// macro: include this header before `import mcpplibs.mwrap.{name};`. Each definition is', + f"// upstream's ({cfg['title']}, {cfg['license']}), made only where the upstream header has", + f'// not defined it, with the constants it names read from {scope}::macros', + '// and NULL written nullptr.', + '// The upstream header included after this one redefines these macros.', + '// Written by tools/generate.py.', + f'#ifndef {guard}', f'#define {guard}'] + for n in sorted(kept): + # A module-only unit has no NULL macro; in C++ nullptr is its meaning. + body = re.sub(r'\bNULL\b', 'nullptr', qualify(kept[n]['body'], set(mapped), f'{scope}::macros')) + header += ['', f'#ifndef {n}', f"#define {n}{kept[n]['params'] or ''} {body}".rstrip(), '#endif'] + if not kept: + header += ['', '// The headers of this release define no such macro.'] + header += ['', f'#endif // {guard}'] + + test = [f'// Each constant and type of mcpplibs.mwrap.{name}.macros equals its macro. A macro', + '// expands even in a qualified name, so the values of the headers are taken under other', + '// names and the macros undefined before the mapped names are used. Written by', + '// tools/generate.py.', includes, '', 'namespace header {'] + test += [f'inline constexpr auto value_{n} = ({n});' if n in constants else f'using type_{n} = {n};' for n in mapped] + test += ['}'] + [f'#undef {n}' for n in mapped] + test += ['', 'import std;', f'import mcpplibs.mwrap.{name}.macros;', '', + f'namespace m = {scope}::macros;', ''] + test += [f'static_assert(std::is_same_v, "{n}");' for n in sorted(types)] + test += ['', 'template ', 'bool same(A a, B b)', '{', + ' if constexpr (std::is_convertible_v && std::is_convertible_v)', + ' return std::string_view(a) == std::string_view(b);', + ' else', + ' return a == b;', + '}', '', 'int main()', '{', ' int differ = 0;'] + test += [f' if (!same(m::{n}, header::value_{n})) {{ std::println("FAIL {n}"); ++differ; }}' for n in sorted(constants)] + test += [f' std::println("{len(constants)} constants and {len(types)} types compared, {{}} differ", differ);', + ' return differ != 0;', '}'] + functions = sorted(surface['functions'] - set(conditional) - set(cfg.get('unbuilt', []))) + linked = [f'// Every function that mcpplibs.mwrap.{name} exports, by address: the program links only', + f'// where compat.{cfg["compat"]} defines each one. Written by tools/generate.py.', + 'import std;', f'import mcpplibs.mwrap.{name};', '', 'int main()', '{', + ' // volatile: an optimiser may not drop a reference, so each one reaches the link.', + ' const void* volatile functions[] = {'] + linked += [f' reinterpret_cast(&{n}),' for n in functions] + linked += [' };', ' int missing = 0;', ' for (const void* f : functions) missing += f == nullptr;', + f' std::println("{len(functions)} functions linked, {{}} null", missing);', + ' return missing != 0;', '}'] + join = lambda lines: '\n'.join(lines) + '\n' + return {f'src/{name}.cppm': join(module), 'src/macros.cppm': join(macros), + f'include/mwrap/{name}/macros.hpp': join(header), 'tests/macros.cpp': join(test), + 'tests/linked.cpp': join(linked)} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument('--check', action='store_true', help='audit instead of writing') + args = parser.parse_args() + cfg = tomllib.loads((ROOT / 'tools/surface.toml').read_text(encoding='utf-8')) + compiler, flags = build_flags(cfg['name']) + surface, macros, guards, constants, types, kept = classify(cfg, compiler, flags) + files = render(cfg, surface, constants, types, kept) + problems = [] + host = {'linux': 'linux', 'darwin': 'macos', 'win32': 'windows'}.get(sys.platform, sys.platform) + if not args.check: + for path, text in files.items(): + target = ROOT / path + target.parent.mkdir(parents=True, exist_ok=True) + if not target.exists() or target.read_text(encoding='utf-8') != text: + target.write_text(text, encoding='utf-8', newline='\n') + elif host == cfg['generated-on']: + problems += [f'differs from the headers: {p}' for p, t in files.items() + if not (ROOT / p).exists() or (ROOT / p).read_text(encoding='utf-8') != t] + else: + # Another host: its declarations against the files written on `generated-on`. + module = (ROOT / f"src/{cfg['name']}.cppm").read_text(encoding='utf-8') + written = set(re.findall(r'using ::(\w+);', module)) + unconditional = set(re.findall(r'using ::(\w+);', module.split('\n#if ')[0])) + declared = surface['functions'] | surface['variables'] | surface['types'] | surface['enumerators'] + mapping = (ROOT / 'src/macros.cppm').read_text(encoding='utf-8') + mapped = set(re.findall(r'^#undef (\w+)$', mapping, flags=re.M)) + kept_written = set(re.findall(r'^#ifndef (\w+)$', (ROOT / f"include/mwrap/{cfg['name']}/macros.hpp") + .read_text(encoding='utf-8'), flags=re.M)) + problems += [f'declared here, not exported: {n}' for n in sorted(declared - written - set(cfg.get('unbuilt', [])))] + problems += [f'exported, not declared here: {n}' for n in sorted(unconditional - declared)] + problems += [f'macro here, neither mapped, kept nor classified: {n}' for n in sorted( + set(macros) - guards - set(cfg.get('syntax', [])) - mapped - kept_written) + if not re.match(r'_[_A-Z]', n)] + problems += [f'mapped, not a constant or type here: {n}' for n in sorted(mapped - constants - types)] + problems += [f'internal linkage, cannot be exported: {n}' for n in sorted(surface['internal']) + if n not in cfg.get('adapted', [])] + for problem in problems: + print(problem) + print(f"{host}: {len(surface['functions'])} functions, {len(surface['variables'])} variables, " + f"{len(surface['types'])} types, {len(surface['enumerators'])} enumerators; {len(constants)} constant and " + f"{len(types)} type macros mapped, {len(kept)} kept, {len(guards)} guards, " + f"{len(set(cfg.get('syntax', [])) & set(macros))} syntax; {len(problems)} problems") + return 1 if problems else 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/tools/run_conformance.py b/tools/run_conformance.py new file mode 100644 index 0000000..af36f57 --- /dev/null +++ b/tools/run_conformance.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 +"""Decode XZ Utils' own test files (of the release) with compat.xz 5.8.3, the +library this wrapper exports. + +The release tarball is downloaded once into target/upstream and checked +against upstream/documents.json. Its tests/files hold the streams upstream's +tests/test_files.sh decodes: every good-* file must decode, every bad-* file +must be refused. A C program (DECODE below, built over compat.xz in +target/conformance) decodes one file the way `xz -dc` does: .xz with +LZMA_CONCATENATED, .lzma with lzma_alone_decoder, .lz with lzma_lzip_decoder. +""" +import argparse +import hashlib +import json +from pathlib import Path +import shutil +import subprocess +import sys +import tarfile +import urllib.request + +MANIFEST = '''[package] +name = "xz-conformance" +version = "5.8.3" +description = "XZ Utils 5.8.3's test files decoded over compat.xz (tools/run_conformance.py)" + +[targets.decode] +kind = "bin" +main = "decode.c" + +[build] +c_standard = "c11" +sources = [] + +[dependencies] +compat.xz = "5.8.3" +''' + +DECODE = r'''#include +#include +#include + +/* Decode one file as `xz -dc` would; status 0 when it decodes to the end. */ +int main(int argc, char** argv) +{ + if (argc != 2) return 2; + FILE* in = fopen(argv[1], "rb"); + if (!in) return 2; + const char* dot = strrchr(argv[1], '.'); + lzma_stream s = LZMA_STREAM_INIT; + lzma_ret rc; + if (dot && strcmp(dot, ".lzma") == 0) rc = lzma_alone_decoder(&s, UINT64_MAX); + else if (dot && strcmp(dot, ".lz") == 0) rc = lzma_lzip_decoder(&s, UINT64_MAX, LZMA_CONCATENATED); + else rc = lzma_stream_decoder(&s, UINT64_MAX, LZMA_CONCATENATED); + if (rc != LZMA_OK) return 3; + uint8_t ibuf[8192], obuf[8192]; + lzma_action action = LZMA_RUN; + s.avail_in = 0; + for (;;) { + if (s.avail_in == 0 && action == LZMA_RUN) { + s.next_in = ibuf; + s.avail_in = fread(ibuf, 1, sizeof ibuf, in); + if (feof(in)) action = LZMA_FINISH; + } + s.next_out = obuf; + s.avail_out = sizeof obuf; + rc = lzma_code(&s, action); + if (rc == LZMA_STREAM_END) break; + if (rc != LZMA_OK) break; + } + lzma_end(&s); + fclose(in); + return rc == LZMA_STREAM_END ? 0 : 1; +} +''' + + +def fetch(root: Path) -> Path: + pins = json.loads((root / 'upstream/documents.json').read_text(encoding='utf-8')) + cache = root / 'target/upstream' + cache.mkdir(parents=True, exist_ok=True) + archive = cache / 'xz-release.tar.gz' + if not archive.exists() or hashlib.sha256(archive.read_bytes()).hexdigest() != pins['upstream_sha256']: + print('+ fetch', pins['upstream'], flush=True) + with urllib.request.urlopen(pins['upstream'], timeout=120) as response: + archive.write_bytes(response.read()) + digest = hashlib.sha256(archive.read_bytes()).hexdigest() + if digest != pins['upstream_sha256']: + sys.exit(f'upstream archive checksum {digest} != {pins["upstream_sha256"]}') + with tarfile.open(archive) as tar: + top = tar.getmembers()[0].name.split('/')[0] + if not (cache / top).exists(): + tar.extractall(cache, filter='data') + return cache / top + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--mcpp', default='mcpp') + parser.add_argument('--toolchain') + args = parser.parse_args() + root = Path(__file__).resolve().parents[1] + files = fetch(root) / 'tests/files' + work = root / 'target/conformance' + shutil.rmtree(work, ignore_errors=True) + work.mkdir(parents=True) + (work / 'decode.c').write_text(DECODE, encoding='utf-8') + (work / 'mcpp.toml').write_text(MANIFEST, encoding='utf-8') + build = [args.mcpp, 'build', '--release'] + if args.toolchain: + build += ['--toolchain', args.toolchain] + print('+', ' '.join(build), flush=True) + subprocess.run(build, cwd=work, check=True) + decode = next(p for p in (work / 'target').rglob('decode*') if p.is_file() and p.suffix in ('', '.exe')) + wrong = [] + cases = sorted(p for p in files.iterdir() if p.name.startswith(('good-', 'bad-'))) + for case in cases: + decoded = subprocess.run([decode, case], capture_output=True).returncode == 0 + if decoded != case.name.startswith('good-'): + wrong.append(case.name) + for name in wrong: + print(('good file refused: ' if name.startswith('good-') else 'bad file decoded: ') + name) + print(f'XZ Utils 5.8.3: {len(cases)} test files, {len(wrong)} wrong, over compat.xz', flush=True) + return 1 if wrong else 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/tools/surface.toml b/tools/surface.toml new file mode 100644 index 0000000..279052c --- /dev/null +++ b/tools/surface.toml @@ -0,0 +1,26 @@ +# What tools/generate.py cannot read from the headers; everything else it can. +name = "xz" +title = "liblzma of XZ Utils 5.8.3" +compat = "xz" +version = "5.8.3" +license = "0BSD" +generated-on = "linux" +headers = ["lzma.h"] +owned = ["lzma.h", "lzma/base.h", "lzma/bcj.h", "lzma/block.h", "lzma/check.h", "lzma/container.h", + "lzma/delta.h", "lzma/filter.h", "lzma/hardware.h", "lzma/index.h", "lzma/index_hash.h", + "lzma/lzma12.h", "lzma/stream_flags.h", "lzma/version.h", "lzma/vli.h"] + +# How the headers write the declarations, not what liblzma offers: linkage and +# calling convention (LZMA_API, LZMA_API_CALL, LZMA_API_IMPORT), attributes +# (lzma_attribute, lzma_attr_*, lzma_nothrow), and the stringification steps +# that build LZMA_VERSION_STRING, which is mapped. +syntax = [ + "LZMA_API", "LZMA_API_CALL", "LZMA_API_IMPORT", "LZMA_API_STATIC", + "lzma_attribute", "lzma_attr_const", "lzma_attr_pure", "lzma_attr_warn_unused_result", "lzma_nothrow", + "LZMA_VERSION_STRING_C", "LZMA_VERSION_STRING_C_", +] + +# Declared by lzma.h, defined in sources that compat.xz 5.8.3 does not build +# (hardware_cputhreads.c, stream_encoder_mt.c, stream_decoder_mt.c: the +# multithreaded coders need a thread library the package does not configure). +unbuilt = ["lzma_cputhreads", "lzma_stream_encoder_mt", "lzma_stream_encoder_mt_memusage", "lzma_stream_decoder_mt"] diff --git a/tools/test_template.py b/tools/test_template.py new file mode 100644 index 0000000..81ea805 --- /dev/null +++ b/tools/test_template.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +"""Render an installed candidate's default mcpp template, then build and run it.""" +import argparse +import json +import os +from pathlib import Path +import shutil +import subprocess +import tempfile +import tomllib + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--mcpp", default="mcpp") + parser.add_argument("--namespace", help="index namespace for legacy manifests without package.namespace") + parser.add_argument("--toolchain", default="llvm@23.1.3") + parser.add_argument("--template", help="named template; omit for the package default") + parser.add_argument("--run-arg", action="append", default=[]) + parser.add_argument("--index", type=Path, help="candidate SDK/wrapper index for unpublished feature dependencies") + args = parser.parse_args() + root = Path(__file__).resolve().parents[1] + metadata = tomllib.loads((root / "mcpp.toml").read_text())["package"] + namespace = metadata.get("namespace", args.namespace) + assert namespace, "Supply the published index namespace for this legacy manifest" + name, version = metadata["name"], metadata["version"] + identity = namespace + "." + name + with tempfile.TemporaryDirectory(prefix=name + "-template-") as directory: + temporary = Path(directory) + home = temporary / "home" + package = home / "registry/data/xpkgs" / (namespace + "-x-" + name) / version / (name + "-" + version) + shutil.copytree(root, package, ignore=shutil.ignore_patterns( + ".git", "target", ".mcpp", ".xlings", ".candidate-index", "__pycache__", "mcpp.lock", "compile_commands.json", "mcpp-2026.*")) + index = home / "indices/mcpplibs/pkgs/mwrap" + index.mkdir(parents=True) + entry = '{url="https://example.invalid/candidate.tar.gz",sha256="fixture"}' + descriptor = ('package={spec="1",namespace=' + json.dumps(namespace) + ',name=' + json.dumps(name) + + ',description="Installed template fixture",type="package",mcpp="mcpp.toml",xpm={') + descriptor += ",".join(platform + "={[" + json.dumps(version) + "]=" + entry + "}" + for platform in ("linux", "macosx", "windows")) + "}}\n" + (index / (identity + ".lua")).write_text(descriptor) + env = dict(os.environ, MCPP_HOME=str(home)) + selector = identity + "@" + version + for command in ([args.mcpp, "new", "--list-templates", selector], + [args.mcpp, "new", "quickstart", "--template", selector + (":" + args.template if args.template else "")]): + print("+", " ".join(command), flush=True) + subprocess.run(command, cwd=temporary, env=env, check=True) + project = temporary / "quickstart" + manifest = project / "mcpp.toml" + text = manifest.read_text() + dependency = tomllib.loads(text)["dependencies"][identity] + options = {"version": dependency} if isinstance(dependency, str) else dependency + assert options["version"] == version, options + replacement = '{ path = ' + json.dumps(package.as_posix()) + if options.get("features"): + replacement += ', features = ' + json.dumps(options["features"]) + replacement += ' }' + lines = text.splitlines() + matches = [i for i, line in enumerate(lines) if line.startswith(json.dumps(identity) + " =")] + assert len(matches) == 1, "Expected exactly one qualified dependency from self.name" + lines[matches[0]] = json.dumps(identity) + " = " + replacement + manifest.write_text("\n".join(lines) + "\n") + if args.index: + candidate_index = args.index.resolve() + subprocess.run([os.sys.executable, str(candidate_index / "tests/prepare_mwrap_indices.py")], + cwd=candidate_index, check=True) + with manifest.open("a") as out: + out.write("\n[indices]\n") + for namespace in ("compat", "mcpplibs.mwrap"): + view = candidate_index / "tests/mwrap/.indices" / namespace + out.write(json.dumps(namespace) + ' = { path = ' + json.dumps(view.as_posix()) + ' }\n') + command = [args.mcpp, "build", "--toolchain", args.toolchain, "--strict"] + print("+", " ".join(command), flush=True) + subprocess.run(command, cwd=project, check=True) + command = [args.mcpp, "run", "--toolchain", args.toolchain] + if args.run_arg: + command += ["--", *args.run_arg] + print("+", " ".join(command), flush=True) + subprocess.run(command, cwd=project, check=True) + + +if __name__ == "__main__": + main() diff --git a/upstream/documents.json b/upstream/documents.json new file mode 100644 index 0000000..381ab86 --- /dev/null +++ b/upstream/documents.json @@ -0,0 +1,22 @@ +{ + "upstream": "https://github.com/tukaani-project/xz/releases/download/v5.8.3/xz-5.8.3.tar.gz", + "upstream_sha256": "3d3a1b973af218114f4f889bbaa2f4c037deaae0c8e815eec381c3d546b974a0", + "pruning": "Nothing of XZ Utils is vendored: liblzma is compat.xz 5.8.3's, from the same tarball, and the macros of include/mwrap/xz/macros.hpp are upstream's definitions, written out by tools/generate.py. Its README and licenses are retained here. The conformance run decodes the release's tests/files with compat.xz, from the tarball downloaded at test time.", + "documents": [ + { + "path": "upstream/xz/README", + "source": "https://github.com/tukaani-project/xz/blob/v5.8.3/README", + "sha256": "63835489293d61f417043b97bad206447c390e007605ddff97fc70a31e9d6d55" + }, + { + "path": "upstream/xz/COPYING", + "source": "https://github.com/tukaani-project/xz/blob/v5.8.3/COPYING", + "sha256": "616a3ad264ce29b8f1cb97e53037b139d406899ca8d1f799651e17bfa09830b8" + }, + { + "path": "upstream/xz/COPYING.0BSD", + "source": "https://github.com/tukaani-project/xz/blob/v5.8.3/COPYING.0BSD", + "sha256": "0b01625d853911cd0e2e088dcfb743261034a091bb379246cb25a14cc4c74bf1" + } + ] +} diff --git a/upstream/xz/COPYING b/upstream/xz/COPYING new file mode 100644 index 0000000..ef33713 --- /dev/null +++ b/upstream/xz/COPYING @@ -0,0 +1,70 @@ + +XZ Utils Licensing +================== + + Different licenses apply to different files in this package. Here + is a summary of which licenses apply to which parts of this package: + + - liblzma is under the BSD Zero Clause License (0BSD). + + - The command line tools xz, xzdec, lzmadec, and lzmainfo are + under 0BSD except that, on systems that don't have a usable + getopt_long, GNU getopt_long is compiled and linked in from the + 'lib' directory. The getopt_long code is under GNU LGPLv2.1+. + + - The scripts to grep, diff, and view compressed files have been + adapted from GNU gzip. These scripts (xzgrep, xzdiff, xzless, + and xzmore) are under GNU GPLv2+. The man pages of the scripts + are under 0BSD; they aren't based on the man pages of GNU gzip. + + - Most of the XZ Utils specific documentation that is in + plain text files (like README, INSTALL, PACKAGERS, NEWS, + and ChangeLog) are under 0BSD unless stated otherwise in + the file itself. The files xz-file-format.txt and + lzma-file-format.xt are in the public domain but may + be distributed under the terms of 0BSD too. + + - Translated messages and man pages are under 0BSD except that + some old translations are in the public domain. + + - Test files and test code in the 'tests' directory, and + debugging utilities in the 'debug' directory are under + the BSD Zero Clause License (0BSD). + + - The GNU Autotools based build system contains files that are + under GNU GPLv2+, GNU GPLv3+, and a few permissive licenses. + These files don't affect the licensing of the binaries being + built. + + - The 'extra' directory contains files that are under various + free software licenses. These aren't built or installed as + part of XZ Utils. + + The following command may be helpful in finding per-file license + information. It works on xz.git and on a clean file tree extracted + from a release tarball. + + sh build-aux/license-check.sh -v + + For the files under the BSD Zero Clause License (0BSD), if + a copyright notice is needed, the following is sufficient: + + Copyright (C) The XZ Utils authors and contributors + + If you copy significant amounts of 0BSD-licensed code from XZ Utils + into your project, acknowledging this somewhere in your software is + polite (especially if it is proprietary, non-free software), but + it is not legally required by the license terms. Here is an example + of a good notice to put into "about box" or into documentation: + + This software includes code from XZ Utils . + + The following license texts are included in the following files: + - COPYING.0BSD: BSD Zero Clause License + - COPYING.LGPLv2.1: GNU Lesser General Public License version 2.1 + - COPYING.GPLv2: GNU General Public License version 2 + - COPYING.GPLv3: GNU General Public License version 3 + + If you have questions, don't hesitate to ask for more information. + The contact information is in the README file. + diff --git a/upstream/xz/COPYING.0BSD b/upstream/xz/COPYING.0BSD new file mode 100644 index 0000000..4322122 --- /dev/null +++ b/upstream/xz/COPYING.0BSD @@ -0,0 +1,11 @@ +Permission to use, copy, modify, and/or distribute this +software for any purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/upstream/xz/README b/upstream/xz/README new file mode 100644 index 0000000..4167167 --- /dev/null +++ b/upstream/xz/README @@ -0,0 +1,281 @@ + +XZ Utils +======== + + 0. Overview + 1. Documentation + 1.1. Overall documentation + 1.2. Documentation for command-line tools + 1.3. Documentation for liblzma + 2. Version numbering + 3. Reporting bugs + 4. Translations + 4.1. Testing translations + 5. Other implementations of the .xz format + 6. Contact information + + +0. Overview +----------- + + XZ Utils provide a general-purpose data-compression library plus + command-line tools. The native file format is the .xz format, but + also the legacy .lzma format is supported. The .xz format supports + multiple compression algorithms, which are called "filters" in the + context of XZ Utils. The primary filter is currently LZMA2. With + typical files, XZ Utils create about 30 % smaller files than gzip. + + To ease adapting support for the .xz format into existing applications + and scripts, the API of liblzma is somewhat similar to the API of the + popular zlib library. For the same reason, the command-line tool xz + has a command-line syntax similar to that of gzip. + + When aiming for the highest compression ratio, the LZMA2 encoder uses + a lot of CPU time and may use, depending on the settings, even + hundreds of megabytes of RAM. However, in fast modes, the LZMA2 encoder + competes with bzip2 in compression speed, RAM usage, and compression + ratio. + + LZMA2 is reasonably fast to decompress. It is a little slower than + gzip, but a lot faster than bzip2. Being fast to decompress means + that the .xz format is especially nice when the same file will be + decompressed very many times (usually on different computers), which + is the case e.g. when distributing software packages. In such + situations, it's not too bad if the compression takes some time, + since that needs to be done only once to benefit many people. + + With some file types, combining (or "chaining") LZMA2 with an + additional filter can improve the compression ratio. A filter chain may + contain up to four filters, although usually only one or two are used. + For example, putting a BCJ (Branch/Call/Jump) filter before LZMA2 + in the filter chain can improve compression ratio of executable files. + + Since the .xz format allows adding new filter IDs, it is possible that + some day there will be a filter that is, for example, much faster to + compress than LZMA2 (but probably with worse compression ratio). + Similarly, it is possible that some day there is a filter that will + compress better than LZMA2. + + XZ Utils supports multithreaded compression. XZ Utils doesn't support + multithreaded decompression yet. It has been planned though and taken + into account when designing the .xz file format. In the future, files + that were created in threaded mode can be decompressed in threaded + mode too. + + +1. Documentation +---------------- + +1.1. Overall documentation + + README This file + + INSTALL.generic Generic install instructions for those not + familiar with packages using GNU Autotools + INSTALL Installation instructions specific to XZ Utils + PACKAGERS Information to packagers of XZ Utils + + COPYING XZ Utils copyright and license information + COPYING.0BSD BSD Zero Clause License + COPYING.GPLv2 GNU General Public License version 2 + COPYING.GPLv3 GNU General Public License version 3 + COPYING.LGPLv2.1 GNU Lesser General Public License version 2.1 + + AUTHORS The main authors of XZ Utils + THANKS Incomplete list of people who have helped making + this software + NEWS User-visible changes between XZ Utils releases + ChangeLog Detailed list of changes (commit log) + TODO Known bugs and some sort of to-do list + + Note that only some of the above files are included in binary + packages. + + +1.2. Documentation for command-line tools + + The command-line tools are documented as man pages. In source code + releases (and possibly also in some binary packages), the man pages + are also provided in plain text (ASCII only) format in the directory + "doc/man" to make the man pages more accessible to those whose + operating system doesn't provide an easy way to view man pages. + + +1.3. Documentation for liblzma + + The liblzma API headers include short docs about each function + and data type as Doxygen tags. These docs should be quite OK as + a quick reference. + + There are a few example/tutorial programs that should help in + getting started with liblzma. In the source package the examples + are in "doc/examples" and in binary packages they may be under + "examples" in the same directory as this README. + + Since the liblzma API has similarities to the zlib API, some people + may find it useful to read the zlib docs and tutorial too: + + https://zlib.net/manual.html + https://zlib.net/zlib_how.html + + +2. Version numbering +-------------------- + + The version number format of XZ Utils is X.Y.ZS: + + - X is the major version. When this is incremented, the library + API and ABI break. + + - Y is the minor version. It is incremented when new features + are added without breaking the existing API or ABI. An even Y + indicates a stable release and an odd Y indicates unstable + (alpha or beta version). + + - Z is the revision. This has a different meaning for stable and + unstable releases: + + * Stable: Z is incremented when bugs get fixed without adding + any new features. This is intended to be convenient for + downstream distributors that want bug fixes but don't want + any new features to minimize the risk of introducing new bugs. + + * Unstable: Z is just a counter. API or ABI of features added + in earlier unstable releases having the same X.Y may break. + + - S indicates stability of the release. It is missing from the + stable releases, where Y is an even number. When Y is odd, S + is either "alpha" or "beta" to make it very clear that such + versions are not stable releases. The same X.Y.Z combination is + not used for more than one stability level, i.e. after X.Y.Zalpha, + the next version can be X.Y.(Z+1)beta but not X.Y.Zbeta. + + +3. Reporting bugs +----------------- + + Naturally it is easiest for me if you already know what causes the + unexpected behavior. Even better if you have a patch to propose. + However, quite often the reason for unexpected behavior is unknown, + so here are a few things to do before sending a bug report: + + 1. Try to create a small example how to reproduce the issue. + + 2. Compile XZ Utils with debugging code using configure switches + --enable-debug and, if possible, --disable-shared. If you are + using GCC, use CFLAGS='-O0 -ggdb3'. Don't strip the resulting + binaries. + + 3. Turn on core dumps. The exact command depends on your shell; + for example in GNU bash it is done with "ulimit -c unlimited", + and in tcsh with "limit coredumpsize unlimited". + + 4. Try to reproduce the suspected bug. If you get "assertion failed" + message, be sure to include the complete message in your bug + report. If the application leaves a coredump, get a backtrace + using gdb: + $ gdb /path/to/app-binary # Load the app to the debugger. + (gdb) core core # Open the coredump. + (gdb) bt # Print the backtrace. Copy & paste to bug report. + (gdb) quit # Quit gdb. + + Report your bug via email or IRC (see Contact information below). + Don't send core dump files or any executables. If you have a small + example file(s) (total size less than 256 KiB), please include + it/them as an attachment. If you have bigger test files, put them + online somewhere and include a URL to the file(s) in the bug report. + + Always include the exact version number of XZ Utils in the bug report. + If you are using a snapshot from the git repository, use "git describe" + to get the exact snapshot version. If you are using XZ Utils shipped + in an operating system distribution, mention the distribution name, + distribution version, and exact xz package version; if you cannot + repeat the bug with the code compiled from unpatched source code, + you probably need to report a bug to your distribution's bug tracking + system. + + +4. Translations +--------------- + + The xz command line tool and all man pages can be translated. + The translations are handled via the Translation Project. If you + wish to help translating xz, please join the Translation Project: + + https://translationproject.org/html/translators.html + + Updates to translations won't be accepted by methods that bypass + the Translation Project because there is a risk of duplicate work: + translation updates made in the xz repository aren't seen by the + translators in the Translation Project. If you have found bugs in + a translation, please report them to the Language-Team address + which can be found near the beginning of the PO file. + + If you find language problems in the original English strings, + feel free to suggest improvements. Ask if something is unclear. + + +4.1. Testing translations + + Testing can be done by installing xz into a temporary directory. + + If building from Git repository (not tarball), generate the + Autotools files: + + ./autogen.sh + + Create a subdirectory for the build files. The tmp-build directory + can be deleted after testing. + + mkdir tmp-build + cd tmp-build + ../configure --disable-shared --enable-debug --prefix=$PWD/inst + + Edit the .po file in the po directory. Then build and install to + the "tmp-build/inst" directory, and use translations.bash to see + how some of the messages look. Repeat these steps if needed: + + make -C po update-po + make -j"$(nproc)" install + bash ../debug/translation.bash | less + bash ../debug/translation.bash | less -S # For --list outputs + + To test other languages, set the LANGUAGE environment variable + before running translations.bash. The value should match the PO file + name without the .po suffix. Example: + + export LANGUAGE=fi + + +5. Other implementations of the .xz format +------------------------------------------ + + 7-Zip and the p7zip port of 7-Zip support the .xz format starting + from the version 9.00alpha. + + https://7-zip.org/ + https://p7zip.sourceforge.net/ + + XZ Embedded is a limited implementation written for use in the Linux + kernel, but it is also suitable for other embedded use. + + https://tukaani.org/xz/embedded.html + + XZ for Java is a complete implementation written in pure Java. + + https://tukaani.org/xz/java.html + + +6. Contact information +---------------------- + + XZ Utils in general: + - Home page: https://tukaani.org/xz/ + - Email to maintainer(s): xz@tukaani.org + - IRC: #tukaani on Libera Chat + - GitHub: https://github.com/tukaani-project/xz + + Lead maintainer: + - Email: Lasse Collin + - IRC: Larhzu on Libera Chat +