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
10 changes: 10 additions & 0 deletions .agents/docs/2026-10-02-add-libevent-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Libevent 2.1.13 compat package

- Source: `libevent/libevent`, latest stable tag observed `release-2.1.13-stable` (2.2 tags were alpha prereleases); GitHub tag archive SHA-256 `1a0885e17dc78afbaeddf13cf849f9238bbc24acdc178464a0d1934d7c5ffbd5`, calculated twice.
- Shape: Form B, C source package. The release needs `event2/event-config.h` and `evconfig-private.h` generated by CMake feature probes. `install()` invokes declared `xim:cmake` only to produce those platform headers; mcpp compiles the upstream C translation units so the library follows the consumer's toolchain. Its source list mirrors CMake's `SRC_CORE` and `SRC_EXTRA`, plus the per-platform backends (Linux epoll, macOS kqueue, Windows IOCP/select), and POSIX/Win32 threading. TLS integrations are not part of the default library and are deliberately excluded, not exposed as unverified features.
- Platforms: linux, macosx, windows all declared against the same archive and sha256. The test member executes the same event timer, evbuffer round trip, and HTTP object construction on each. Windows system libraries are explicitly linked.
- License: BSD-3-Clause for the main sources, ISC for arc4random included by `evutil_rand.c`, and MIT for the Windows timer code derived from libutp (`evutil_time.c`; see upstream LICENSE). The separate wepoll and TLS integrations are not compiled.
- Mirror: no GitCode credentials/assets available; use a plain upstream URL (not a fictional CN URL).
- Local Linux verification: mcpp 2026.10.1.2 `mcpp xpkg parse --all-os pkgs/c/compat.libevent.lua` reports 24/24/25 sources; cold `mcpp test -p libevent --cache off` passed under GCC 16.1.0. The test deliberately uses `EVLOOP_ONCE` because `event_base_dispatch()` returns one after a one-shot timer drains the queue even when its callback ran.
- PR #503 initially failed only on Linux default GCC: CMake's host probes produced an incomplete `event-config.h` (missing pointer-size and socket/address-family macros) when using the ambient toolchain wrapper. The hook now selects native `/usr/bin/cc` for CMake's Linux/macOS host probes; mcpp still compiles the sources with the selected consumer compiler. The fix passed a cold local Linux GCC build and the full PR matrix on Linux GCC/LLVM, macOS and Windows.
- The final PR validation run passed lint, mirror checks, build, Linux default, all Linux LLVM shards, macOS, and Windows. Results: [PR #503 checks](https://github.com/mcpplibs/mcpp-index/pull/503/checks).
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@

### Added

- **`compat.libevent` 2.1.13。** Linux、macOS、Windows 分别通过上游 CMake 生成
平台配置头,再由 mcpp 编译 libevent 核心、HTTP/DNS 扩展和对应系统后端;
TLS 可选组件未收录。workspace 成员测试事件分派、缓冲区和 HTTP 对象。

- **`compat.muduo` 2.0.3 与 Boost 头组件。** muduo 仅支持 Linux;内联构建
`muduo_base`/`muduo_net` 源码,按上游 2.0.3 的依赖补充 `compat.boost-any`、
`compat.boost-circular-buffer`、`compat.boost-concept-check` 并复用既有 `compat.boost-utility`。
Expand Down
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@ One descriptor per common shape:
| Native module library (Form A) | [`mcpplibs.tinyhttps`](pkgs/t/tinyhttps.lua) · [`gzj-creator.galay`](pkgs/g/gzj-creator.galay.lua) |
| C sources + `features` | [`compat.cjson`](pkgs/c/compat.cjson.lua) |
| C++ sources, several versions | [`compat.yaml-cpp`](pkgs/c/compat.yaml-cpp.lua) |
| Boost header-only components | [`compat.boost-any`](pkgs/c/compat.boost-any.lua) · [`compat.boost-circular-buffer`](pkgs/c/compat.boost-circular-buffer.lua) · [`compat.boost-concept-check`](pkgs/c/compat.boost-concept-check.lua) · [`compat.boost-utility`](pkgs/c/compat.boost-utility.lua) |
| Header-only | [`compat.gtl`](pkgs/c/compat.gtl.lua) |
| Linux-only C++ network library | [`compat.muduo`](pkgs/c/compat.muduo.lua) |
| Generated config header | [`compat.c-ares`](pkgs/c/compat.c-ares.lua) |
| C++23 module wrapper | [`nlohmann.json`](pkgs/n/nlohmann.json.lua) |
| C++23 module shipped by upstream | [`khronos.vulkan-hpp`](pkgs/k/khronos.vulkan-hpp.lua) |
Expand Down
2 changes: 0 additions & 2 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ mcpp self config --mirror CN # 切换至国内镜像,默认使用 GLOBAL 上
| 原生模块库(Form A) | [`mcpplibs.tinyhttps`](pkgs/t/tinyhttps.lua) · [`gzj-creator.galay`](pkgs/g/gzj-creator.galay.lua) |
| C 源码 + `features` | [`compat.cjson`](pkgs/c/compat.cjson.lua) |
| C++ 源码,多版本 | [`compat.yaml-cpp`](pkgs/c/compat.yaml-cpp.lua) |
| Boost 头文件组件 | [`compat.boost-any`](pkgs/c/compat.boost-any.lua) · [`compat.boost-circular-buffer`](pkgs/c/compat.boost-circular-buffer.lua) · [`compat.boost-concept-check`](pkgs/c/compat.boost-concept-check.lua) · [`compat.boost-utility`](pkgs/c/compat.boost-utility.lua) |
| 仅 Linux 的 C++ 网络库 | [`compat.muduo`](pkgs/c/compat.muduo.lua) |
| header-only | [`compat.gtl`](pkgs/c/compat.gtl.lua) |
| 生成 config 头 | [`compat.c-ares`](pkgs/c/compat.c-ares.lua) |
| C++23 module wrapper | [`nlohmann.json`](pkgs/n/nlohmann.json.lua) |
Expand Down
1 change: 1 addition & 0 deletions mcpp.toml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ members = [
"tests/examples/libassert",
"tests/examples/libcoro",
"tests/examples/libdrm",
"tests/examples/libevent",
"tests/examples/libffi",
"tests/examples/expat",
"tests/examples/spirv-headers",
Expand Down
133 changes: 133 additions & 0 deletions pkgs/c/compat.libevent.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
-- libevent 2.1.13-stable: configure headers with upstream CMake, then compile
-- core and extra sources with mcpp's C toolchain on Linux, macOS and Windows.
package = {
spec = "1",
namespace = "compat",
name = "libevent",
description = "Libevent asynchronous event notification and networking library",
licenses = {"BSD-3-Clause", "ISC", "MIT"},
repo = "https://github.com/libevent/libevent",
type = "package",

xpm = {
linux = {
deps = { "xim:cmake@latest" },
["2.1.13"] = {
url = "https://github.com/libevent/libevent/archive/refs/tags/release-2.1.13-stable.tar.gz",
sha256 = "1a0885e17dc78afbaeddf13cf849f9238bbc24acdc178464a0d1934d7c5ffbd5",
},
},
macosx = {
deps = { "xim:cmake@latest" },
["2.1.13"] = {
url = "https://github.com/libevent/libevent/archive/refs/tags/release-2.1.13-stable.tar.gz",
sha256 = "1a0885e17dc78afbaeddf13cf849f9238bbc24acdc178464a0d1934d7c5ffbd5",
},
},
windows = {
deps = { "xim:cmake@latest" },
["2.1.13"] = {
url = "https://github.com/libevent/libevent/archive/refs/tags/release-2.1.13-stable.tar.gz",
sha256 = "1a0885e17dc78afbaeddf13cf849f9238bbc24acdc178464a0d1934d7c5ffbd5",
},
},
},

mcpp = {
language = "c++23",
import_std = false,
c_standard = "c11",
include_dirs = { "*/include", "config/include", "*/compat", "*/WIN32-Code" },
cflags = { "-DHAVE_CONFIG_H" },
sources = {
"*/buffer.c", "*/bufferevent.c", "*/bufferevent_filter.c",
"*/bufferevent_pair.c", "*/bufferevent_ratelim.c",
"*/bufferevent_sock.c", "*/event.c", "*/evmap.c",
"*/evthread.c", "*/evutil.c", "*/evutil_rand.c",
"*/evutil_time.c", "*/listener.c", "*/log.c",
"*/signal.c", "*/strlcpy.c", "*/event_tagging.c",
"*/http.c", "*/evdns.c", "*/evrpc.c",
},
targets = { ["event"] = { kind = "lib" } },
deps = {},
linux = {
sources = { "*/select.c", "*/poll.c", "*/epoll.c", "*/evthread_pthread.c" },
},
macosx = {
sources = { "*/select.c", "*/poll.c", "*/kqueue.c", "*/evthread_pthread.c" },
},
windows = {
sources = { "*/buffer_iocp.c", "*/bufferevent_async.c", "*/event_iocp.c",
"*/win32select.c", "*/evthread_win32.c" },
cflags = { "-D_CRT_SECURE_NO_WARNINGS", "-D_CRT_NONSTDC_NO_DEPRECATE" },
ldflags = { "-lws2_32", "-lshell32", "-ladvapi32", "-liphlpapi" },
},
},
}

import("xim.libxpkg.pkginfo")
import("xim.libxpkg.log")

local function quote(s)
return '"' .. tostring(s):gsub('"', '\\"') .. '"'
end

function install()
local wrap = "libevent-release-" .. pkginfo.version() .. "-stable"
if not os.isfile(path.join(wrap, "CMakeLists.txt")) then
log.error("compat.libevent: expected %s/CMakeLists.txt", wrap)
return false
end
local prefix = pkginfo.install_dir()
os.tryrm(prefix)
os.mkdir(prefix)
local srcroot = path.join(prefix, wrap)
os.mv(wrap, srcroot)

local pkg = pkginfo.build_dep("xim:cmake")
local cmake
if pkg then
for _, candidate in ipairs({
pkg.bin and path.join(pkg.bin, "cmake") or "",
pkg.bin and path.join(pkg.bin, "cmake.exe") or "",
pkg.path and path.join(pkg.path, "cmake.app/Contents/bin/cmake") or "",
pkg.path and path.join(pkg.path, "CMake.app/Contents/bin/cmake") or "",
}) do
if os.isfile(candidate) then cmake = candidate; break end
end
end
if not cmake then
log.error("compat.libevent: xim:cmake executable not found")
return false
end
local command = quote(cmake) .. " -S " .. quote(srcroot)
.. " -B " .. quote(path.join(prefix, "config"))
.. " -DCMAKE_POLICY_VERSION_MINIMUM=3.5"
-- Probe the native platform with its system compiler, not a toolchain
-- wrapper whose sysroot may differ from the generated headers' ABI.
.. (os.host() == "windows" and "" or " -DCMAKE_C_COMPILER=/usr/bin/cc")
.. " -DEVENT__DISABLE_OPENSSL=ON -DEVENT__DISABLE_MBEDTLS=ON"
.. " -DEVENT__DISABLE_TESTS=ON -DEVENT__DISABLE_SAMPLES=ON"
.. " -DEVENT__DISABLE_BENCHMARK=ON -DEVENT__LIBRARY_TYPE=STATIC"
local logfile = path.join(prefix, "mcpp_cmake_configure.log")
if os.host() == "windows" then
-- Windows xlings does not reliably execute a quoted .exe directly.
-- Drive it through cmd as compat.openssl does, and inspect its output.
local bat = path.join(prefix, "mcpp_configure.bat")
io.writefile(bat, "@echo off\r\n" .. command .. " > " .. quote(logfile)
.. " 2>&1\r\nif errorlevel 1 exit /b 1\r\n")
command = "cmd /c " .. quote(bat)
end
local ok, result = pcall(os.exec, command)
if not ok or not result then
log.error("compat.libevent: CMake configuration failed: %s; log: %s",
tostring(result), os.isfile(logfile) and io.readfile(logfile) or "<not written>")
return false
end
local generated = path.join(prefix, "config/include/event2/event-config.h")
if not os.isfile(generated) then
log.error("compat.libevent: CMake did not generate %s", generated)
return false
end
return true
end
6 changes: 6 additions & 0 deletions tests/examples/libevent/mcpp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "libevent-tests"
version = "0.1.0"

[dependencies.compat]
libevent = "2.1.13"
61 changes: 61 additions & 0 deletions tests/examples/libevent/tests/event.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#ifdef _WIN32
#include <winsock2.h>
#endif

#include <event2/buffer.h>
#include <event2/event.h>
#include <event2/http.h>

#include <cstring>
#include <cstdio>

static void fired(evutil_socket_t, short, void* arg) {
++*static_cast<int*>(arg);
}

int main() {
#ifdef _WIN32
WSADATA winsock;
if (WSAStartup(MAKEWORD(2, 2), &winsock) != 0) return 1;
#endif
event_base* base = event_base_new();
if (!base) {
#ifdef _WIN32
WSACleanup();
#endif
return 1;
}
int count = 0;
timeval delay = {0, 0};
const int scheduled = event_base_once(base, -1, EV_TIMEOUT, fired, &count, &delay);
const int dispatched = scheduled == 0 ? event_base_loop(base, EVLOOP_ONCE) : -1;
bool ok = scheduled == 0 && dispatched == 0 && count == 1;
if (!ok) std::fprintf(stderr, "event loop: schedule=%d dispatch=%d count=%d\n", scheduled, dispatched, count);

evbuffer* buffer = evbuffer_new();
if (!buffer) {
event_base_free(base);
#ifdef _WIN32
WSACleanup();
#endif
return 1;
}
const char text[] = "libevent";
char out[sizeof(text)] = {};
const bool buffer_ok = evbuffer_add(buffer, text, sizeof(text) - 1) == 0
&& evbuffer_remove(buffer, out, sizeof(text) - 1) == sizeof(text) - 1
&& std::strcmp(out, text) == 0 && evbuffer_get_length(buffer) == 0;
if (!buffer_ok) std::fprintf(stderr, "buffer round trip failed\n");
ok = ok && buffer_ok;

evhttp* http = evhttp_new(base);
ok = ok && http != nullptr;
if (!http) std::fprintf(stderr, "evhttp_new failed\n");
if (http) evhttp_free(http);
evbuffer_free(buffer);
event_base_free(base);
#ifdef _WIN32
WSACleanup();
#endif
return ok ? 0 : 1;
}
Loading