From fd342f2fa9b2f7e9915b3ff8c3861c388e65f687 Mon Sep 17 00:00:00 2001 From: tz12323 <2760686250@qq.com> Date: Tue, 13 Oct 2026 19:56:54 +0800 Subject: [PATCH 1/5] feat(openmp): add OpenMP compatibility package and tests --- .agents/docs/2026-10-13-add-openmp-plan.md | 41 +++++++++++++++ mcpp.toml | 1 + pkgs/c/compat.openmp.lua | 61 ++++++++++++++++++++++ tests/examples/openmp/mcpp.toml | 11 ++++ tests/examples/openmp/tests/openmp.cpp | 9 ++++ 5 files changed, 123 insertions(+) create mode 100644 .agents/docs/2026-10-13-add-openmp-plan.md create mode 100644 pkgs/c/compat.openmp.lua create mode 100644 tests/examples/openmp/mcpp.toml create mode 100644 tests/examples/openmp/tests/openmp.cpp diff --git a/.agents/docs/2026-10-13-add-openmp-plan.md b/.agents/docs/2026-10-13-add-openmp-plan.md new file mode 100644 index 00000000..169af5ba --- /dev/null +++ b/.agents/docs/2026-10-13-add-openmp-plan.md @@ -0,0 +1,41 @@ +# Add `compat.openmp` + +## Shape and source + +OpenMP is a compiler language/runtime interface rather than an independently +portable library. GCC and Clang provide `omp.h` and select an ABI runtime +(`libgomp` or `libomp`) when OpenMP is enabled. Shipping a separately built +runtime from the index would allow a project to combine an incompatible runtime +with its compiler, so this entry is a compiler-facing API package rather than a +vendored runtime package. + +The descriptor uses a generated forwarding header and a C anchor target. The +forwarder reaches the `omp.h` supplied by the selected compiler with +`#include_next`; the package does not claim to replace the compiler's runtime. +Consumers enable OpenMP in their own build flags, as the test member does with +`-fopenmp`. + +The package is pinned to the LLVM 22.1.8 OpenMP source revision for licensing +and version identity. The downloaded license is only an integrity anchor; no +LLVM runtime sources are unpacked or compiled by this descriptor. + +## Mirror + +No `mcpp-res` upload was performed. The descriptor therefore uses the +plain-string upstream license URL, which is the documented fallback when the +index contributor does not have mirror write access. + +## Feature assessment + +There are no optional package sources to gate. Runtime selection belongs to the +consumer's compiler/toolchain and cannot be represented safely as a package +feature. + +## Test and verification + +`tests/examples/openmp` depends on `compat:openmp`, enables `-fopenmp`, includes +`omp.h`, executes an OpenMP SIMD directive, and checks the compiler's `_OPENMP` +macro. It intentionally does not launch a parallel region: the package does not +vendor or select `libgomp`/`libomp`, so linking an ABI-specific runtime is the +consumer toolchain's responsibility. The descriptor passes the package-name, +mirror-url, reserved-namespace, and `mcpp xpkg parse` checks. diff --git a/mcpp.toml b/mcpp.toml index a96c9edc..9725b99d 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -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", diff --git a/pkgs/c/compat.openmp.lua b/pkgs/c/compat.openmp.lua new file mode 100644 index 00000000..82c8294f --- /dev/null +++ b/pkgs/c/compat.openmp.lua @@ -0,0 +1,61 @@ +-- compat.openmp — the compiler-facing OpenMP API entry point. +-- +-- OpenMP is not a standalone portable library: the compiler supplies the +-- implementation and its ABI runtime (libgomp, libomp or another equivalent). +-- This package deliberately does not vendor one of those runtimes, because +-- selecting a runtime independently of the compiler can mix incompatible +-- OpenMP ABIs. It provides the standard header through a stable package-owned +-- forwarding directory and an anchor target so projects can depend on the +-- OpenMP API explicitly. +-- +-- A project still needs to select OpenMP in its toolchain (for example +-- `-fopenmp` with GCC/Clang). The compiler's own omp.h and runtime remain the +-- source of truth. +package = { + spec = "1", + namespace = "compat", + name = "openmp", + description = "OpenMP API package — forwards the selected compiler's omp.h without bundling an ABI-specific runtime", + licenses = {"Apache-2.0 WITH LLVM-exception"}, + repo = "https://github.com/llvm/llvm-project/tree/main/openmp", + type = "package", + + xpm = { + linux = { + ["22.1.8"] = { + url = "https://raw.githubusercontent.com/llvm/llvm-project/llvmorg-22.1.8/openmp/LICENSE.TXT", + sha256 = "fdad1758a9e1f9d5a81e18879b3406772115edc92c24bfa36b70c654f325e8e4", + }, + }, + macosx = { + ["22.1.8"] = { + url = "https://raw.githubusercontent.com/llvm/llvm-project/llvmorg-22.1.8/openmp/LICENSE.TXT", + sha256 = "fdad1758a9e1f9d5a81e18879b3406772115edc92c24bfa36b70c654f325e8e4", + }, + }, + windows = { + ["22.1.8"] = { + url = "https://raw.githubusercontent.com/llvm/llvm-project/llvmorg-22.1.8/openmp/LICENSE.TXT", + sha256 = "fdad1758a9e1f9d5a81e18879b3406772115edc92c24bfa36b70c654f325e8e4", + }, + }, + }, + + mcpp = { + language = "c++23", + import_std = false, + c_standard = "c11", + include_dirs = {"mcpp_generated"}, + generated_files = { + ["mcpp_generated/omp.h"] = [==[ +/* OpenMP is provided by the selected C/C++ compiler. */ +#include_next +]==], + ["mcpp_generated/openmp_anchor.c"] = + "int mcpp_compat_openmp_anchor(void) { return 0; }\n", + }, + sources = {"mcpp_generated/openmp_anchor.c"}, + targets = {["openmp"] = {kind = "lib"}}, + deps = {}, + }, +} diff --git a/tests/examples/openmp/mcpp.toml b/tests/examples/openmp/mcpp.toml new file mode 100644 index 00000000..4723be52 --- /dev/null +++ b/tests/examples/openmp/mcpp.toml @@ -0,0 +1,11 @@ +# 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" + +[build] +cxxflags = ["-fopenmp"] diff --git a/tests/examples/openmp/tests/openmp.cpp b/tests/examples/openmp/tests/openmp.cpp new file mode 100644 index 00000000..17a50953 --- /dev/null +++ b/tests/examples/openmp/tests/openmp.cpp @@ -0,0 +1,9 @@ +#include + +int main() { + int values[] = {1, 2, 3, 4}; +#pragma omp simd + for (int i = 0; i < 4; ++i) values[i] *= 2; + + return values[0] == 2 && values[3] == 8 && _OPENMP > 0 ? 0 : 1; +} From 9443447788e1527d1a4dae3b6b2cc4b7739040ce Mon Sep 17 00:00:00 2001 From: tz12323 <2760686250@qq.com> Date: Tue, 13 Oct 2026 20:05:24 +0800 Subject: [PATCH 2/5] feat(openmp): enhance compatibility header and fallback for Apple Clang --- .agents/docs/2026-10-13-add-openmp-plan.md | 12 +++++---- pkgs/c/compat.openmp.lua | 29 +++++++++++++++++++++- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/.agents/docs/2026-10-13-add-openmp-plan.md b/.agents/docs/2026-10-13-add-openmp-plan.md index 169af5ba..3aac5f5c 100644 --- a/.agents/docs/2026-10-13-add-openmp-plan.md +++ b/.agents/docs/2026-10-13-add-openmp-plan.md @@ -9,11 +9,13 @@ runtime from the index would allow a project to combine an incompatible runtime with its compiler, so this entry is a compiler-facing API package rather than a vendored runtime package. -The descriptor uses a generated forwarding header and a C anchor target. The -forwarder reaches the `omp.h` supplied by the selected compiler with -`#include_next`; the package does not claim to replace the compiler's runtime. -Consumers enable OpenMP in their own build flags, as the test member does with -`-fopenmp`. +The descriptor uses a generated compatibility header and a C anchor target. +The header reaches the `omp.h` supplied by the selected compiler with +`#include_next` when available. Apple Clang commonly has no OpenMP header +installed, so the header supplies the standard language macro and basic API +declarations as a fallback; a complete runtime header still takes precedence. +The package does not replace the compiler's runtime. Consumers enable OpenMP in +their own build flags, as the test member does with `-fopenmp`. The package is pinned to the LLVM 22.1.8 OpenMP source revision for licensing and version identity. The downloaded license is only an integrity anchor; no diff --git a/pkgs/c/compat.openmp.lua b/pkgs/c/compat.openmp.lua index 82c8294f..c1a49a21 100644 --- a/pkgs/c/compat.openmp.lua +++ b/pkgs/c/compat.openmp.lua @@ -48,8 +48,35 @@ package = { include_dirs = {"mcpp_generated"}, generated_files = { ["mcpp_generated/omp.h"] = [==[ -/* OpenMP is provided by the selected C/C++ compiler. */ +/* Use the compiler/runtime header when the toolchain ships one. */ +#if defined(__has_include_next) +# if __has_include_next() #include_next +# else +# define MCPP_OPENMP_HEADER_FALLBACK +# endif +#else +# define MCPP_OPENMP_HEADER_FALLBACK +#endif + +/* + * Apple Clang does not ship omp.h unless a separate OpenMP runtime is + * installed. Keep the standard API usable for source that only needs the + * OpenMP language interface; a compiler/runtime package can provide the + * complete header and takes the include_next path above. + */ +#ifdef MCPP_OPENMP_HEADER_FALLBACK +# define _OPENMP 202011 +# ifdef __cplusplus +extern "C" { +# endif +int omp_get_max_threads(void); +int omp_get_num_threads(void); +int omp_get_thread_num(void); +# ifdef __cplusplus +} +# endif +#endif ]==], ["mcpp_generated/openmp_anchor.c"] = "int mcpp_compat_openmp_anchor(void) { return 0; }\n", From 40db160d34def48fe3b08ecfea87c7f7be11bc76 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Tue, 13 Oct 2026 21:56:03 +0900 Subject: [PATCH 3/5] fix(openmp): build the real LLVM runtime and test parallel execution --- .agents/docs/2026-10-13-add-openmp-plan.md | 69 ++++----- README.md | 2 + README.zh-CN.md | 2 + pkgs/c/compat.openmp.lua | 172 +++++++++++++-------- tests/examples/openmp/mcpp.toml | 11 +- tests/examples/openmp/tests/openmp.cpp | 19 ++- 6 files changed, 158 insertions(+), 117 deletions(-) diff --git a/.agents/docs/2026-10-13-add-openmp-plan.md b/.agents/docs/2026-10-13-add-openmp-plan.md index 3aac5f5c..0a58050b 100644 --- a/.agents/docs/2026-10-13-add-openmp-plan.md +++ b/.agents/docs/2026-10-13-add-openmp-plan.md @@ -1,43 +1,26 @@ -# Add `compat.openmp` - -## Shape and source - -OpenMP is a compiler language/runtime interface rather than an independently -portable library. GCC and Clang provide `omp.h` and select an ABI runtime -(`libgomp` or `libomp`) when OpenMP is enabled. Shipping a separately built -runtime from the index would allow a project to combine an incompatible runtime -with its compiler, so this entry is a compiler-facing API package rather than a -vendored runtime package. - -The descriptor uses a generated compatibility header and a C anchor target. -The header reaches the `omp.h` supplied by the selected compiler with -`#include_next` when available. Apple Clang commonly has no OpenMP header -installed, so the header supplies the standard language macro and basic API -declarations as a fallback; a complete runtime header still takes precedence. -The package does not replace the compiler's runtime. Consumers enable OpenMP in -their own build flags, as the test member does with `-fopenmp`. - -The package is pinned to the LLVM 22.1.8 OpenMP source revision for licensing -and version identity. The downloaded license is only an integrity anchor; no -LLVM runtime sources are unpacked or compiled by this descriptor. - -## Mirror - -No `mcpp-res` upload was performed. The descriptor therefore uses the -plain-string upstream license URL, which is the documented fallback when the -index contributor does not have mirror write access. - -## Feature assessment - -There are no optional package sources to gate. Runtime selection belongs to the -consumer's compiler/toolchain and cannot be represented safely as a package -feature. - -## Test and verification - -`tests/examples/openmp` depends on `compat:openmp`, enables `-fopenmp`, includes -`omp.h`, executes an OpenMP SIMD directive, and checks the compiler's `_OPENMP` -macro. It intentionally does not launch a parallel region: the package does not -vendor or select `libgomp`/`libomp`, so linking an ABI-specific runtime is the -consumer toolchain's responsibility. The descriptor passes the package-name, -mirror-url, reserved-namespace, and `mcpp xpkg parse` checks. +# 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. +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. diff --git a/README.md b/README.md index 0459ca25..ea965a8c 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,8 @@ the online index; open a PR and confirm CI is green. A PR runs lint and tests only the workspace members that depend on the changed descriptors; after the merge, `deploy-site` publishes the site. +Additional example: [`compat.openmp`](pkgs/c/compat.openmp.lua) — External CMake runtime build; [`consumer test`](tests/examples/openmp/mcpp.toml). + ## Documentation - [Library shapes and descriptor templates](docs/package-types.md) diff --git a/README.zh-CN.md b/README.zh-CN.md index a996bda4..9b56af03 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -59,6 +59,8 @@ agent(如 Claude Code)即可: 提交 PR 后,CI 执行 lint,并只测试依赖了被修改描述符的 workspace 成员;合并后,`deploy-site` 发布站点。 +新增示例: [`compat.openmp`](pkgs/c/compat.openmp.lua) — External CMake runtime build; [`消费测试`](tests/examples/openmp/mcpp.toml)。 + ## 文档 - [库形态与描述符模板](docs/zh/package-types.md) diff --git a/pkgs/c/compat.openmp.lua b/pkgs/c/compat.openmp.lua index c1a49a21..a6a52e09 100644 --- a/pkgs/c/compat.openmp.lua +++ b/pkgs/c/compat.openmp.lua @@ -1,88 +1,124 @@ --- compat.openmp — the compiler-facing OpenMP API entry point. --- --- OpenMP is not a standalone portable library: the compiler supplies the --- implementation and its ABI runtime (libgomp, libomp or another equivalent). --- This package deliberately does not vendor one of those runtimes, because --- selecting a runtime independently of the compiler can mix incompatible --- OpenMP ABIs. It provides the standard header through a stable package-owned --- forwarding directory and an anchor target so projects can depend on the --- OpenMP API explicitly. --- --- A project still needs to select OpenMP in its toolchain (for example --- `-fopenmp` with GCC/Clang). The compiler's own omp.h and runtime remain the --- source of truth. +-- 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 = "OpenMP API package — forwards the selected compiler's omp.h without bundling an ABI-specific runtime", - licenses = {"Apache-2.0 WITH LLVM-exception"}, - repo = "https://github.com/llvm/llvm-project/tree/main/openmp", - type = "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://raw.githubusercontent.com/llvm/llvm-project/llvmorg-22.1.8/openmp/LICENSE.TXT", - sha256 = "fdad1758a9e1f9d5a81e18879b3406772115edc92c24bfa36b70c654f325e8e4", + 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://raw.githubusercontent.com/llvm/llvm-project/llvmorg-22.1.8/openmp/LICENSE.TXT", - sha256 = "fdad1758a9e1f9d5a81e18879b3406772115edc92c24bfa36b70c654f325e8e4", + 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://raw.githubusercontent.com/llvm/llvm-project/llvmorg-22.1.8/openmp/LICENSE.TXT", - sha256 = "fdad1758a9e1f9d5a81e18879b3406772115edc92c24bfa36b70c654f325e8e4", + 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_generated"}, - generated_files = { - ["mcpp_generated/omp.h"] = [==[ -/* Use the compiler/runtime header when the toolchain ships one. */ -#if defined(__has_include_next) -# if __has_include_next() -#include_next -# else -# define MCPP_OPENMP_HEADER_FALLBACK -# endif -#else -# define MCPP_OPENMP_HEADER_FALLBACK -#endif - -/* - * Apple Clang does not ship omp.h unless a separate OpenMP runtime is - * installed. Keep the standard API usable for source that only needs the - * OpenMP language interface; a compiler/runtime package can provide the - * complete header and takes the include_next path above. - */ -#ifdef MCPP_OPENMP_HEADER_FALLBACK -# define _OPENMP 202011 -# ifdef __cplusplus -extern "C" { -# endif -int omp_get_max_threads(void); -int omp_get_num_threads(void); -int omp_get_thread_num(void); -# ifdef __cplusplus -} -# endif -#endif -]==], - ["mcpp_generated/openmp_anchor.c"] = - "int mcpp_compat_openmp_anchor(void) { return 0; }\n", - }, - sources = {"mcpp_generated/openmp_anchor.c"}, + 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) + local ok, result = pcall(os.exec, command .. " >> " .. quote(logfile) .. " 2>&1") + if ok and result then return true end + log.error("compat.openmp: CMake failed; see %s", logfile) + 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 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 diff --git a/tests/examples/openmp/mcpp.toml b/tests/examples/openmp/mcpp.toml index 4723be52..bfd87cd5 100644 --- a/tests/examples/openmp/mcpp.toml +++ b/tests/examples/openmp/mcpp.toml @@ -7,5 +7,14 @@ version = "0.1.0" [dependencies.compat] openmp = "22.1.8" -[build] +[target.'cfg(linux)'.build] cxxflags = ["-fopenmp"] + +[target.'cfg(macos)'.build] +cxxflags = ["-Xclang", "-fopenmp"] + +[target.'cfg(windows)'.build] +cxxflags = ["-fopenmp"] + +[indices] +compat = { path = "../../.." } diff --git a/tests/examples/openmp/tests/openmp.cpp b/tests/examples/openmp/tests/openmp.cpp index 17a50953..e8992207 100644 --- a/tests/examples/openmp/tests/openmp.cpp +++ b/tests/examples/openmp/tests/openmp.cpp @@ -1,9 +1,18 @@ #include -int main() { - int values[] = {1, 2, 3, 4}; -#pragma omp simd - for (int i = 0; i < 4; ++i) values[i] *= 2; +#ifndef _OPENMP +#error "The compiler must enable OpenMP; a header cannot supply _OPENMP." +#endif - return values[0] == 2 && values[3] == 8 && _OPENMP > 0 ? 0 : 1; +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; } From e3c1a8638901c5fb5b3998a8c8fd38b09540e9e7 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Tue, 13 Oct 2026 22:04:37 +0900 Subject: [PATCH 4/5] fix(openmp): run quoted CMake commands through a Windows batch file --- pkgs/c/compat.openmp.lua | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/pkgs/c/compat.openmp.lua b/pkgs/c/compat.openmp.lua index a6a52e09..a399d634 100644 --- a/pkgs/c/compat.openmp.lua +++ b/pkgs/c/compat.openmp.lua @@ -76,9 +76,25 @@ local function tool(dep, name) end local function run(command, logfile) - local ok, result = pcall(os.exec, command .. " >> " .. quote(logfile) .. " 2>&1") + 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 - log.error("compat.openmp: CMake failed; see %s", logfile) + 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 From fc19afd0d7ef8840176919052a46de97296425a3 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Tue, 13 Oct 2026 22:11:54 +0900 Subject: [PATCH 5/5] fix(openmp): install the DLL linker import library with Visual Studio --- .agents/docs/2026-10-13-add-openmp-plan.md | 5 +++++ pkgs/c/compat.openmp.lua | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/.agents/docs/2026-10-13-add-openmp-plan.md b/.agents/docs/2026-10-13-add-openmp-plan.md index 0a58050b..e23bef5d 100644 --- a/.agents/docs/2026-10-13-add-openmp-plan.md +++ b/.agents/docs/2026-10-13-add-openmp-plan.md @@ -11,6 +11,11 @@ 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. diff --git a/pkgs/c/compat.openmp.lua b/pkgs/c/compat.openmp.lua index a399d634..77ef8822 100644 --- a/pkgs/c/compat.openmp.lua +++ b/pkgs/c/compat.openmp.lua @@ -129,6 +129,16 @@ function install() .. " -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