diff --git a/.agents/docs/2026-10-18-glfw-windows-link.md b/.agents/docs/2026-10-18-glfw-windows-link.md new file mode 100644 index 00000000..7d2548a3 --- /dev/null +++ b/.agents/docs/2026-10-18-glfw-windows-link.md @@ -0,0 +1,26 @@ +# GLFW Windows package closure + +The existing gui-stack and imgui-window members gate GLFW behind Linux, leaving +Windows package compilation/linkage untested. Add a standalone glfw workspace +member with a local compat index and no other direct dependencies. Test Null +windows everywhere and require native Win32 initialization and hidden window +behavior on Windows; use GLFW_NO_API and GLFW_INCLUDE_NONE. + +PR #538's initial Windows run (39048720103, job 117442681476) reproduced +undefined LoadCursorW, CreateWindowExW, RegisterClassExW and DragAcceptFiles. +All references originate in GLFW's own win32 sources, not the consumer. + +Declare user32 (window/input), gdi32 (pixel formats), and shell32 (drag/drop) in +windows.runtime.libraries. Upstream CMake gets user32/shell32 from CMake's default +Windows standard libraries; the direct clang driver does not. Retain the existing +dialect-neutral form rather than raw linker flags. GLFW dynamically loads its +OpenGL, XInput, DirectInput and DWM functions; no new import libraries are needed +for those. Application calls to OpenGL/Vulkan remain the consumer's responsibility. + +Keep the existing package versions and upstream 3.4 archive, URLs, SHA and +install hook. This is a descriptor-only fix: xpkg refreshes the descriptor, so +no new package version is needed when the source is unchanged. The new test +consumes upstream version 3.4. + +Validation: CI uses mcpp 2026.10.1.2. Initial Windows failure is the regression +proof; final PR checks must cover the fix, including execution of the Win32 test. diff --git a/mcpp.toml b/mcpp.toml index d1d380a5..206c958d 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -71,6 +71,7 @@ members = [ "tests/examples/godot-cpp-v10", "tests/examples/grpc-codegen", "tests/examples/grpc-module", + "tests/examples/glfw", "tests/examples/gui-stack", "tests/examples/imgui", "tests/examples/imgui-module", diff --git a/pkgs/c/compat.glfw.lua b/pkgs/c/compat.glfw.lua index bfce7a94..e99709f9 100644 --- a/pkgs/c/compat.glfw.lua +++ b/pkgs/c/compat.glfw.lua @@ -168,7 +168,12 @@ package = { -- consumer's build. `runtime.libraries` renders as gdi32.lib for -- MSVC and -lgdi32 for GNU. runtime = { - libraries = { "gdi32" }, + -- GLFW calls User32 window/input APIs, GDI pixel-format APIs, + -- and Shell32 drag/drop APIs itself. CMake's Windows defaults + -- hide user32/shell32; a direct clang consumer needs them here. + -- OpenGL, XInput, DirectInput and DWM are dynamically loaded by + -- GLFW, so they do not require additional import libraries. + libraries = { "user32", "gdi32", "shell32" }, }, }, }, diff --git a/tests/examples/glfw/README.md b/tests/examples/glfw/README.md new file mode 100644 index 00000000..9d2db019 --- /dev/null +++ b/tests/examples/glfw/README.md @@ -0,0 +1,19 @@ +# Standalone GLFW smoke test + +From the repository root, run `mcpp test -p glfw` (including on Windows). +The manifest redirects `compat` to this checkout so the test checks the PR's +package descriptor rather than the published index. + +The test checks the upstream version and creates a Null-platform window on every +platform. On Windows it also requires the Win32 backend, creates a hidden native +window, checks its size and user pointer, polls events, and closes it. A failed +initialization or window creation fails the test. No visible window or graphics +context is required. + +The only direct dependency is `compat.glfw`. `GLFW_INCLUDE_NONE` and +`GLFW_NO_API` keep graphics API headers and context creation out of this test. +GLFW's own native system-library references belong in the package descriptor; +libraries used directly by an application (for example OpenGL entry points or a +Vulkan loader) belong to that application. GLFW dynamically loads several +optional Windows APIs, so their DLL names alone are not a reason to add import +libraries. Keep dialect-neutral native link requirements in `runtime.libraries`. diff --git a/tests/examples/glfw/mcpp.toml b/tests/examples/glfw/mcpp.toml new file mode 100644 index 00000000..fde1aae4 --- /dev/null +++ b/tests/examples/glfw/mcpp.toml @@ -0,0 +1,9 @@ +[package] +name = "glfw-tests" +version = "0.1.0" + +[dependencies.compat] +glfw = "3.4" + +[indices] +compat = { path = "../../.." } diff --git a/tests/examples/glfw/tests/window.cpp b/tests/examples/glfw/tests/window.cpp new file mode 100644 index 00000000..f761c50c --- /dev/null +++ b/tests/examples/glfw/tests/window.cpp @@ -0,0 +1,51 @@ +#define GLFW_INCLUDE_NONE +#include +#include +#include + +static void error(int code, const char* description) { + std::fprintf(stderr, "GLFW error %d: %s\n", code, description); +} + +static bool window_test(int platform) { + glfwInitHint(GLFW_PLATFORM, platform); + if (!glfwInit()) return false; + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + GLFWwindow* window = glfwCreateWindow(640, 480, "compat.glfw smoke", nullptr, nullptr); + bool ok = window && glfwGetPlatform() == platform; + if (window) { + int width = 0, height = 0; + glfwGetWindowSize(window, &width, &height); + int marker = 42; + glfwSetWindowUserPointer(window, &marker); + ok = ok && width == 640 && height == 480 && + glfwGetWindowUserPointer(window) == &marker && + !glfwWindowShouldClose(window); + if (!ok) std::fprintf(stderr, "platform %x: size=%dx%d, expected=640x480, user_pointer=%d, should_close=%d\n", + platform, width, height, + glfwGetWindowUserPointer(window) == &marker, + glfwWindowShouldClose(window)); + glfwSetWindowShouldClose(window, GLFW_TRUE); + ok = ok && glfwWindowShouldClose(window); + glfwPollEvents(); + glfwDestroyWindow(window); + } + glfwTerminate(); + return ok; +} + +int main() { + glfwSetErrorCallback(error); + int major = 0, minor = 0, revision = 0; + glfwGetVersion(&major, &minor, &revision); + if (major != 3 || minor != 4 || revision != 0) return 1; + if (!window_test(GLFW_PLATFORM_NULL)) return 2; +#ifdef _WIN32 + // Require the real Win32 backend too: a null-only test misses native links. + if (!glfwPlatformSupported(GLFW_PLATFORM_WIN32)) return 3; + if (!std::strstr(glfwGetVersionString(), "Win32")) return 4; + if (!window_test(GLFW_PLATFORM_WIN32)) return 5; +#endif + return 0; +}