Skip to content
Merged
31 changes: 31 additions & 0 deletions .agents/docs/2026-10-13-add-openmp-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Add compat.openmp

LLVM libomp 22.1.8 is a separable CMake project inside llvm-project.
The descriptor downloads the full official llvm-project-22.1.8.src.tar.xz,
whose SHA-256 was checked twice; license Apache-2.0 WITH LLVM-exception.
The install hook builds upstream openmp/ and installs the generated omp.h
and real runtime. It writes the C anchor only after installation succeeds.

Unix uses static libomp.a, which implements both Clang and GCC OpenMP ABIs
without a C++ standard-library ABI dependency. The hook uses the system C/C++
SDK explicitly because installation precedes mcpp consumer toolchain selection.
Windows uses the upstream-supported DLL/import library with Visual Studio 2022;
the DLL is declared as a runtime artifact directory for consumer deployment.
The Visual Studio generator can omit the archive for upstream's source-less
ompimp target. In that case the hook installs the import library produced by
the DLL link under the expected libomp.lib name; DLL and imports are from the
same build. Windows CMake commands run through a batch file, preserving quoted
SDK paths and propagating failures with the build log tail.
CMake and Python are declared build dependencies. Offloading and OMPT tools
are disabled; there is no misleading sources-only feature for these builds.

No mirror upload was performed. Plain-string GLOBAL URLs provide CN fallback.
The example redirects compat to this checkout. It enables OpenMP with normal
GCC/Clang flags (Apple Clang uses -Xclang -fopenmp), links the package runtime,
launches two threads, and checks thread identity plus a reduction sum. The
header never defines _OPENMP: a missing compiler flag is a compile error.

Local validation with CI-pinned mcpp 2026.10.1.2 and gcc 16.1.0:
`mcpp test -p openmp`: `test result ok. 1 passed; 0 failed`.
An independent upstream CMake build also succeeded. Cross-platform behavior
is validated by the PR CI matrix rather than inferred from a SIMD-only test.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ One descriptor per common shape:
| 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) |
| External build system (`install()`) | [`compat.openssl`](pkgs/c/compat.openssl.lua) |
| External build system (`install()`) | [`compat.openssl`](pkgs/c/compat.openssl.lua) · [`compat.openmp`](pkgs/c/compat.openmp.lua) |

Every other shape, and why each descriptor is written the way it is, is in
**[Descriptor examples by shape](docs/descriptor-examples.md)**.
Expand Down
2 changes: 1 addition & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ mcpp self config --mirror CN # 切换至国内镜像,默认使用 GLOBAL 上
| 生成 config 头 | [`compat.c-ares`](pkgs/c/compat.c-ares.lua) |
| C++23 module wrapper | [`nlohmann.json`](pkgs/n/nlohmann.json.lua) |
| 上游自带 C++23 module | [`khronos.vulkan-hpp`](pkgs/k/khronos.vulkan-hpp.lua) |
| 外部构建系统(`install()`) | [`compat.openssl`](pkgs/c/compat.openssl.lua) |
| 外部构建系统(`install()`) | [`compat.openssl`](pkgs/c/compat.openssl.lua) · [`compat.openmp`](pkgs/c/compat.openmp.lua) |

其余形态,以及每个描述符为何这样写,见 **[描述符示例总览(按形态)](docs/zh/descriptor-examples.md)**。

Expand Down
1 change: 1 addition & 0 deletions mcpp.toml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ members = [
"tests/examples/mysql-connector-cpp",
"tests/examples/nlohmann.json",
"tests/examples/openblas",
"tests/examples/openmp",
"tests/examples/openssl",
"tests/examples/recastnavigation",
"tests/examples/recastnavigation-features",
Expand Down
150 changes: 150 additions & 0 deletions pkgs/c/compat.openmp.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
-- LLVM OpenMP runtime built from its pinned release sources.
-- libomp implements the Clang and (on Unix) GCC OpenMP ABIs. Consumers enable
-- OpenMP compilation themselves; this package supplies the header and runtime.
-- Unix uses a static C-ABI runtime; Windows ships the upstream DLL/import lib.
package = {
spec = "1",
namespace = "compat",
name = "openmp",
description = "LLVM OpenMP runtime and omp.h, built from source",
licenses = {"Apache-2.0 WITH LLVM-exception"},
repo = "https://github.com/llvm/llvm-project/tree/main/openmp",
type = "package",
xpm = {
linux = {
deps = { "xim:cmake@latest", "xim:python@3.13.12" },
["22.1.8"] = {
url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-22.1.8/llvm-project-22.1.8.src.tar.xz",
sha256 = "922f1817a0df7b1489272d18134ee0087a8b068828f87ac63b9861b1a9965888",
},
},
macosx = {
deps = { "xim:cmake@latest", "xim:python@3.13.12" },
["22.1.8"] = {
url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-22.1.8/llvm-project-22.1.8.src.tar.xz",
sha256 = "922f1817a0df7b1489272d18134ee0087a8b068828f87ac63b9861b1a9965888",
},
},
windows = {
deps = { "xim:cmake@latest", "xim:python@3.13.12" },
["22.1.8"] = {
url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-22.1.8/llvm-project-22.1.8.src.tar.xz",
sha256 = "922f1817a0df7b1489272d18134ee0087a8b068828f87ac63b9861b1a9965888",
},
},
},
mcpp = {
language = "c++23",
import_std = false,
c_standard = "c11",
include_dirs = {"mcpp_openmp/include"},
-- Written last by install(), after CMake has installed the runtime.
sources = {"mcpp_openmp_anchor.c"},
targets = {["openmp"] = {kind = "lib"}},
linux = {ldflags = {"-Lmcpp_openmp/lib", "-lomp", "-lpthread", "-ldl"}},
macosx = {ldflags = {"-Lmcpp_openmp/lib", "-lomp"}},
windows = {
ldflags = {"-Lmcpp_openmp/lib", "-llibomp"},
runtime = {library_dirs = {"mcpp_openmp/bin"}},
},
deps = {},
},
}

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

local function quote(value)
value = tostring(value)
if os.host() == "windows" then
return '"' .. value:gsub('"', '\\"') .. '"'
end
return "'" .. value:gsub("'", "'\\''") .. "'"
end

local function tool(dep, name)
local info = pkginfo.build_dep(dep)
if info then
for _, candidate in ipairs({
path.join(info.bin or info.path or "", name),
path.join(info.path or "", "CMake.app", "Contents", "bin", name),
}) do
if os.isfile(candidate) then return candidate end
end
end
return name
end

local function run(command, logfile)
command = command .. " >> " .. quote(logfile) .. " 2>&1"
if os.host() == "windows" then
-- xlings cannot reliably invoke a quoted .exe directly on Windows.
-- A batch file preserves CMake's arguments and reports its exit code.
local bat = path.join(pkginfo.install_dir(), "mcpp_openmp_run.bat")
io.writefile(bat, "@echo off\r\n" .. command
.. "\r\nif errorlevel 1 exit /b 1\r\nexit /b 0\r\n")
command = "cmd /c " .. quote(bat)
end
local ok, result = pcall(os.exec, command)
if ok and result then return true end
local lines = {}
if os.isfile(logfile) then
for line in (io.readfile(logfile) .. "\n"):gmatch("(.-)\n") do
lines[#lines + 1] = line
end
end
local tail = table.concat(lines, "\n", math.max(1, #lines - 59), #lines)
log.error("compat.openmp: CMake failed; see %s\n%s", logfile, tail)
return false
end

function install()
local prefix = pkginfo.install_dir()
local root = "llvm-project-" .. pkginfo.version() .. ".src"
if not os.isdir(root) then
log.error("compat.openmp: missing extracted source %s", root)
return false
end
os.tryrm(prefix)
os.mv(root, prefix)
local build = path.join(prefix, "mcpp_openmp_build")
local output = path.join(prefix, "mcpp_openmp")
local logfile = path.join(prefix, "mcpp_openmp_build.log")
local windows = os.host() == "windows"
local cmake = tool("xim:cmake", windows and "cmake.exe" or "cmake")
local python = tool("xim:python", windows and "python.exe" or "python3")
local options = windows and ' -G "Visual Studio 17 2022" -A x64 -DLIBOMP_ENABLE_SHARED=ON'
or ' -G "Unix Makefiles" -DLIBOMP_ENABLE_SHARED=OFF'
if not windows then
-- install() runs before consumer toolchain resolution. Build the C-ABI
-- runtime with the system SDK; libomp has no C++ standard-library ABI.
options = options .. " -DCMAKE_C_COMPILER=/usr/bin/cc -DCMAKE_CXX_COMPILER=/usr/bin/c++"
.. " -DCMAKE_ASM_COMPILER=/usr/bin/cc -DCMAKE_MAKE_PROGRAM=/usr/bin/make"
end
local configure = quote(cmake) .. " -S " .. quote(path.join(prefix, "openmp"))
.. " -B " .. quote(build) .. " -DCMAKE_BUILD_TYPE=Release"
.. " -DCMAKE_INSTALL_PREFIX=" .. quote(output)
.. " -DPython3_EXECUTABLE=" .. quote(python)
.. " -DOPENMP_ENABLE_LIBOMPTARGET=OFF -DOPENMP_ENABLE_OMPT_TOOLS=OFF"
.. " -DLIBOMP_OMPT_SUPPORT=OFF -DLIBOMP_USE_ITT_NOTIFY=OFF" .. options
if not run(configure, logfile) then return false end
if not run(quote(cmake) .. " --build " .. quote(build) .. " --config Release --parallel 4", logfile) then return false end
if windows then
-- The Visual Studio generator can omit the archive of upstream's
-- source-less ompimp target. The DLL link already produced an import
-- library for these exact exports; give install() its expected name.
local expected = path.join(build, "runtime", "src", "Release", "libomp.lib")
local linked = path.join(build, "runtime", "src", "Release", "libomp.dll.lib")
if not os.isfile(expected) and os.isfile(linked) then
os.cp(linked, expected)
end
end
if not run(quote(cmake) .. " --install " .. quote(build) .. " --config Release", logfile) then return false end
local library = path.join(output, "lib", windows and "libomp.lib" or "libomp.a")
if not os.isfile(library) or not os.isfile(path.join(output, "include", "omp.h")) then
log.error("compat.openmp: missing installed header/library; see %s", logfile)
return false
end
io.writefile(path.join(prefix, "mcpp_openmp_anchor.c"), "int mcpp_compat_openmp_anchor(void) { return 0; }\n")
return true
end
20 changes: 20 additions & 0 deletions tests/examples/openmp/mcpp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# OpenMP consumer test: the package supplies the compiler-facing omp.h entry
# point, while this member enables the selected compiler's OpenMP runtime.
[package]
name = "openmp-tests"
version = "0.1.0"

[dependencies.compat]
openmp = "22.1.8"

[target.'cfg(linux)'.build]
cxxflags = ["-fopenmp"]

[target.'cfg(macos)'.build]
cxxflags = ["-Xclang", "-fopenmp"]

[target.'cfg(windows)'.build]
cxxflags = ["-fopenmp"]

[indices]
compat = { path = "../../.." }
18 changes: 18 additions & 0 deletions tests/examples/openmp/tests/openmp.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#include <omp.h>

#ifndef _OPENMP
#error "The compiler must enable OpenMP; a header cannot supply _OPENMP."
#endif

int main() {
omp_set_dynamic(0);
int sum = 0;
int threads = 0;
#pragma omp parallel num_threads(2) reduction(+:sum)
{
#pragma omp single
threads = omp_get_num_threads();
sum += omp_get_thread_num() + 1;
}
return threads == 2 && sum == 3 ? 0 : 1;
}
Loading