Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Preserve upstream document bytes and original whitespace on every platform.
upstream/** -text -whitespace
88 changes: 88 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -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 }}"
49 changes: 49 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -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[@]}"
28 changes: 28 additions & 0 deletions .github/workflows/templates.yml
Original file line number Diff line number Diff line change
@@ -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
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
target/
.mcpp/
mcpp.lock
compile_commands.json
__pycache__/
.xlings/
5 changes: 5 additions & 0 deletions .xlings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"workspace": {
"xim:mcpp": "2026.11.5.1"
}
}
14 changes: 14 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
@@ -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.
80 changes: 80 additions & 0 deletions README.zh.md
Original file line number Diff line number Diff line change
@@ -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` 及其枚举值
都是 `<lzma.h>` 中的上游实体,位于上游声明它们的全局命名空间。包名沿用唯一实现 `compat.xz` 5.8.3 的身份;常量宏映射为
C++ 常量。每次 CI 都用同一份库解码上游自己的测试文件。

## 模块

- `import mcpplibs.mwrap.xz;`:`<lzma.h>` 及其 `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 <mwrap/xz/macros.hpp>`:`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<std::uint8_t> 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<const std::uint8_t*>(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`(初始化器)
与带参数的宏保留在 `<mwrap/xz/macros.hpp>` 中;其中两个转换为 `uint32_t`,所以该翻译单元导入 `std.compat`。宏在限定名中也会
展开。同时包含 `<lzma.h>` 的翻译单元经头文件的宏读取常量([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))。
56 changes: 56 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
@@ -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 `<lzma.h>` 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
`<mwrap/xz/macros.hpp>`, 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 `<stdint.h>`'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 <lzma.h>` 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.
32 changes: 32 additions & 0 deletions include/mwrap/xz/macros.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// <mwrap/xz/macros.hpp> --- 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
Loading
Loading