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
92 changes: 92 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
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 (formats and filters written and read back, entries, 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

# Linux only: the suite's sources expect the configuration that upstream's own
# build generates for each host, and compat.libarchive configures macOS and
# Windows from the Android host headers with substitutions, which the test
# sources do not accept (dirent.h on Windows, the macOS ACL calls).
- name: Upstream's test suite over the same library
if: ${{ !matrix.target && runner.os == 'Linux' }}
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"
}
}
13 changes: 13 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
libarchive-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. libarchive is distributed under the BSD
2-Clause license, with the exceptions its COPYING lists:

Copyright (c) 2003-2018 Tim Kientzle and the libarchive contributors

Its original README and license summary are retained in upstream/libarchive/.
The library itself is provided by compat.libarchive from the same release.
Provenance and checksums are recorded in upstream/documents.json.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,11 @@ linked, and header mixing. CI also runs:
module, the macro mapping, the macro header and the generated tests are
regenerated from the headers as clang reads them and compared, and on macOS
and Windows every name the headers declare there is exported;
- upstream's test suite over the same library
- on Linux, upstream's test suite over the same library
([tools/run_conformance.py](tools/run_conformance.py)); one test fails by
compat.libarchive's configuration and is listed with its reason.
compat.libarchive's configuration and is listed with its reason. The suite's
sources need the configuration upstream's build generates per host, which
compat.libarchive does not produce for macOS and Windows.

A separate job scaffolds the default template as an installed package and runs
it.
Expand Down
83 changes: 83 additions & 0 deletions README.zh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# libarchive

> libarchive——多格式归档与压缩库,以 C++23 模块提供。

[English](README.md) · **简体中文** · [上游](https://github.com/libarchive/libarchive) · [发布](https://github.com/mcpplibs/libarchive-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/libarchive-m/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/mcpplibs/libarchive-m/actions/workflows/ci.yml)

上游 API 不变:`archive_read_new`、`archive_write_open_memory`、`archive_read_next_header`、`archive_entry_pathname`、
`struct archive` 与 `struct archive_entry` 都是 `<archive.h>` 与 `<archive_entry.h>` 中的上游实体,位于上游声明它们的全局
命名空间。库本身是带 zlib、bzip2、liblzma、lz4 与 zstd 的 `compat.libarchive` 3.8.7;常量宏映射为 C++ 常量。每次 CI 都在
同一份库上运行上游自己的测试套件(702 个测试)。

## 模块

- `import mcpplibs.mwrap.libarchive;`:`<archive.h>` 与 `<archive_entry.h>` 的全部函数与类型,并重新导出宏模块
- `import mcpplibs.mwrap.libarchive.macros;`:`ARCHIVE_OK`、`ARCHIVE_EOF`、`ARCHIVE_FATAL`、`ARCHIVE_FORMAT_*`、`ARCHIVE_FILTER_*`、`ARCHIVE_EXTRACT_*`、`AE_IFREG`、`ARCHIVE_ENTRY_ACL_*`、`ARCHIVE_VERSION_NUMBER` 等,位于 `mcpplibs::mwrap::libarchive::macros`

> 更多模块 API -> [用法与 API](docs/usage.md)

## 快速开始

```sh
mcpp new bundle --template mcpplibs.mwrap.libarchive@3.8.7
cd bundle
mcpp run
```

默认模板在内存中写出含三个文件的 tar.gz 并列出其内容;`mcpp run -- <archive>` 列出 libarchive 能读取的任何归档。

```toml
[dependencies]
"mcpplibs.mwrap.libarchive" = "3.8.7"
```

```cpp
import std;
import mcpplibs.mwrap.libarchive;

namespace la = mcpplibs::mwrap::libarchive::macros;

int main(int argc, char** argv) {
archive* a = archive_read_new();
archive_read_support_filter_all(a);
archive_read_support_format_all(a);
archive_entry* e;
if (archive_read_open_filename(a, argv[1], 10240) == la::ARCHIVE_OK)
while (archive_read_next_header(a, &e) == la::ARCHIVE_OK)
std::println("{}", archive_entry_pathname(e));
archive_read_free(a);
}
```

## 范围与宏

模块导出两个头文件的全部函数:共 453 个,在 Windows 上另有只在 Windows 上声明的两个(`archive_read_open_filenames_w`、
`archive_entry_copy_bhfi`)。声明是静态库形式(`LIBARCHIVE_STATIC`),模块在包含头文件之前定义它。

命名模块不能导出宏。常量映射为 `mcpplibs::mwrap::libarchive::macros` 中同名的 `inline constexpr`。头文件没有面向使用者的
带参数宏,因此本版本的 `<mwrap/libarchive/macros.hpp>` 为空。宏在限定名中也会展开。同时包含 `<archive.h>` 的翻译单元经
头文件的宏读取常量([docs/usage.md](docs/usage.md))。

## 构建与测试

Linux、macOS、Windows 的 CI 用 GCC 与 LLVM 构建模块并运行测试:pax 加 gzip、ustar 加 xz、cpio 加 zstd、gnutar 加 bzip2、
ZIP 与 7-Zip 写入内存后读回并识别格式与过滤器,经克隆保留的条目元数据,无效输入被拒绝,版本同其宏相等,每个常量同其宏
相等,每个导出函数都能链接,头文件混用。CI 还运行:

- 导出审计([tools/generate.py](tools/generate.py) `--check`):按 clang 读到的头文件重新生成模块、宏映射、宏头文件与
生成的测试并逐字节比较;在 macOS 与 Windows 上检查头文件在该平台声明的每个名字都已导出;
- 在 Linux 上于同一份库运行上游的测试套件([tools/run_conformance.py](tools/run_conformance.py));其中一个测试因
compat.libarchive 的配置而失败,已连同原因列出。测试源码需要上游构建为各宿主生成的配置,compat.libarchive 不为 macOS
与 Windows 生成这种配置。

另一个作业把默认模板当作已安装的包生成项目并运行。

## 许可

包装部分为 [Apache-2.0](LICENSE),作者 mcpp-community([NOTICE](NOTICE))。libarchive 仍遵循 BSD 2-Clause 许可及其摘要
列出的例外([upstream/libarchive/COPYING](upstream/libarchive/COPYING)、[README](upstream/libarchive/README.md))。
59 changes: 59 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Usage and API

`mcpplibs.mwrap.libarchive` 3.8.7 exports libarchive's public API, 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.libarchive` | `<archive.h>`: `struct archive`; reading (`archive_read_new`, `archive_read_support_filter_*`, `archive_read_support_format_*`, `archive_read_open_*`, `archive_read_next_header`, `archive_read_data*`, `archive_read_extract*`, `archive_read_set_*option*`, passphrases); writing (`archive_write_new`, `archive_write_set_format_*`, `archive_write_add_filter_*`, `archive_write_open_*`, `archive_write_header`, `archive_write_data*`); disk access (`archive_write_disk_*`, `archive_read_disk_*`); matching (`archive_match_*`); errors and status (`archive_errno`, `archive_error_string`, `archive_format_name`, `archive_filter_*`); `archive_version_*`; the callback types and `la_int64_t`, `la_ssize_t`. `<archive_entry.h>`: `struct archive_entry` and every `archive_entry_*` accessor, ACLs, extended attributes, sparse maps, `archive_entry_linkresolver`. Re-exports the macros module |
| `mcpplibs.mwrap.libarchive.macros` | in `mcpplibs::mwrap::libarchive::macros`: the status codes (`ARCHIVE_EOF`, `ARCHIVE_OK`, `ARCHIVE_RETRY`, `ARCHIVE_WARN`, `ARCHIVE_FAILED`, `ARCHIVE_FATAL`); `ARCHIVE_FILTER_*`, `ARCHIVE_COMPRESSION_*` and `ARCHIVE_FORMAT_*`; `ARCHIVE_EXTRACT_*`, `ARCHIVE_READDISK_*`, `ARCHIVE_MATCH_*`; `AE_IF*` file types and `AE_SYMLINK_TYPE_*`; `ARCHIVE_ENTRY_ACL_*`; `ARCHIVE_VERSION_NUMBER`, `ARCHIVE_VERSION_ONLY_STRING`, `ARCHIVE_VERSION_STRING` |

`archive_read_open_filenames_w` and `archive_entry_copy_bhfi` are declared on
Windows only and are exported there. The functions that upstream marks
deprecated for 4.0 are declared in 3.8.7 and are exported.

## Configuration

`compat.libarchive` builds a static library (`LIBARCHIVE_STATIC`) with zlib,
bzip2, liblzma, lz4 and zstd, its bundled BLAKE2, and every format. External
filter programs (`lrzip`, `lzop`, `grzip`, `lzip` through a program) are run
when the system has them. The character set of every locale is UTF-8. The
module defines `LIBARCHIVE_STATIC` before the headers; on Windows the
declarations would otherwise import from a DLL. A unit that includes
`<archive.h>` itself defines it too (tests/header_mix_test.cpp does).

## 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. `AE_IF*` keep upstream's
type (`__LA_MODE_T`: `mode_t`, or `unsigned short` on Windows).
- **Declaration syntax.** The `__LA_*` macros (linkage, deprecation, the
portable `time_t`, `mode_t` and `dev_t` spellings) are reserved names and are
not offered. The headers define no macro with arguments for a user;
`<mwrap/libarchive/macros.hpp>` is empty in this release.

## A header beside the module

`#include <archive.h>` and `import mcpplibs.mwrap.libarchive;` may appear in
one unit: they declare the same entities. A macro expands even inside a
qualified name, so `mcpplibs::mwrap::libarchive::macros::ARCHIVE_OK` written
after the header is `...::macros::0`. 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).

## Conformance

tools/run_conformance.py builds libarchive's own `libarchive/test` (312
sources, 702 tests) with upstream's `test_main.c` over `compat.libarchive` and
runs it against the release's reference files, on Linux: the test sources
expect the configuration upstream's build generates for each host, and
compat.libarchive configures macOS and Windows from the Android host headers
with substitutions, which the sources do not accept. One test fails by
configuration and is expected: `test_archive_string_conversion_fail_c`
expects a conversion to the C locale's ASCII to fail, and `compat.libarchive`
answers UTF-8 for every locale.
14 changes: 14 additions & 0 deletions include/mwrap/libarchive/macros.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// <mwrap/libarchive/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.libarchive;`. Each definition is
// upstream's (libarchive 3.8.7, BSD 2-Clause license), made only where the upstream header has
// not defined it, with the constants it names read from mcpplibs::mwrap::libarchive::macros
// and NULL written nullptr.
// The upstream header included after this one redefines these macros.
// Written by tools/generate.py.
#ifndef MWRAP_LIBARCHIVE_MACROS_HPP
#define MWRAP_LIBARCHIVE_MACROS_HPP

// The headers of this release define no such macro.

#endif // MWRAP_LIBARCHIVE_MACROS_HPP
Loading
Loading