## Problem Windows builds break with a current local toolchain (Scoop LLVM 22.1.8, CMake 4.4.0, VS 2026), in two independent ways: 1. The build stops at curl's deliberate guard: `#error "no non-blocking method was found/used/set"` in `third-party/curl/lib/nonblock.c`. 2. From the second configure onward, `cmake --build` re-runs CMake in an endless loop (observed 42 consecutive reconfigure cycles in a single build). Likely the same mechanism behind the "endlessly building" VS 2026 note in `docs/setup/dev/vs.md`. ## Root cause 1. `third-party/curl/CMake/CurlTests.c` passes `int *` to `ioctlsocket()`, whose third parameter is `u_long *`. Clang 22 promotes `-Wincompatible-pointer-types` to a hard error in C, so the `HAVE_IOCTLSOCKET_FIONBIO` try_compile silently fails and `curl_config.h` never defines it. Upstream CI does not see this because the windows-2022 runner image ships an older LLVM. GCC 14 promotes the same warning to a hard error, which is very likely the `CurlTests.c.obj` failure reported from MSYS2 in open-goal/jak-project#3551. Upstream curl hit the identical problem with GCC 14 and fixed the probe in curl 8.8.0 (curl/curl#13578). 2. The root CMakeLists copies the build tree's `compile_commands.json` into `<src>/build/` for clangd using `configure_file()`, which registers its input as a configure dependency. CMake rewrites `compile_commands.json` late in every generation, after `CTestTestfile.cmake` and `cmake_install.cmake` (outputs of the same Ninja regen rule), so once the dependency is registered the rule is deterministically dirty and every `ninja` invocation re-runs CMake. A pristine first configure is safe (the file does not exist yet, so the `if(EXISTS ...)` guard skips the copy), which is why the loop looks machine- or IDE-specific. ## Fix 1. Per review, re-vendor `third-party/curl` at the `curl-8_21_0` tag (previously `curl-8_3_0`), which carries the upstream probe fix plus two years of upstream development; `vendor.yaml` updated to match. Adjustments the version jump forced: - curl 8.15 removed the native macOS Secure Transport backend (`CURL_USE_SECTRANSP`), so macOS now builds curl against OpenSSL like Linux. The two macOS workflows install Homebrew `openssl@3` and export `OPENSSL_ROOT_DIR` (keg-only), and the macOS setup docs gained the same two lines. - `CURL_BROTLI` / `CURL_ZSTD` switched to AUTO-detection in curl 8.10; pinned OFF to keep the previous no-compression behavior and avoid silently linking whatever the CI images happen to have. - curl's new top-level `BUILD_EXAMPLES` cache option (default ON) leaked into discord-rpc's identically named option and broke configure at a nonexistent `examples/send-presence` directory; pinned OFF ahead of the third-party subdirectories. The diff is dominated by the mechanical tag-tree swap under `third-party/curl` (linguist-vendored, collapsed in review). The hand-written changes are `CMakeLists.txt`, the two macOS workflows, `docs/setup/system/macos.md`, and `vendor.yaml`. 2. Swap `configure_file()` for `file(COPY ...)`: the same clangd copy with no configure dependency registered. (`file(COPY_FILE ... ONLY_IF_DIFFERENT)` would be cleaner still but requires CMake 3.21, above the declared `cmake_minimum_required(VERSION 3.10)`.) ## Test plan - [x] Fresh `cmake --preset Release-windows-clang` (LLVM 22, no cache seeding) completes and logs `Enabled SSL backends: Schannel`; the FIONBIO probe passes without the previous `#error` - [x] Full Windows Release build from scratch in the branch worktree (all 1422 targets) - [x] goalc-test suite: 1509 passed, 0 failed - [x] Second consecutive configure with `compile_commands.json` present: the regen rule in `build.ninja` has no `compile_commands.json` input; `<src>/build/compile_commands.json` is still refreshed for clangd - [x] Repeated `ninja` invocations after a full build no longer re-run CMake - [x] macOS Intel and ARM CI green (first exercise of the OpenSSL backend switch) --- I work off a self-hosted forge, so this GitHub account is quiet; the configure logs and ninja dirty-node traces from the investigation are available if anyone wants the raw data. (AI-assisted)
5.3 KiB
Vendored
Generated
llist - linked lists
#include "llist.h"
This is the internal module for linked lists. The API is designed to be flexible but also to avoid dynamic memory allocation.
None of the involved structs should be accessed using struct fields (outside
of llist.c). Use the functions.
Setup and shutdown
struct Curl_llist is the struct holding a single linked list. It needs to be
initialized with a call to Curl_llist_init() before it can be used
To clean up a list, call Curl_llist_destroy(). Since the linked lists
themselves do not allocate memory, it can also be fine to not clean up the
list.
Add a node
There are two functions for adding a node to a linked list:
- Add it last in the list with
Curl_llist_append - Add it after a specific existing node with
Curl_llist_insert_next
When a node is added to a list, it stores an associated custom pointer to
anything you like and you provide a pointer to a struct Curl_llist_node
struct in which it stores and updates pointers. If you intend to add the same
struct to multiple lists concurrently, you need to have one struct Curl_llist_node for each list.
Add a node to a list with Curl_llist_append(list, elem, node). Where
list: points to astruct Curl_llistelem: points to what you want added to the listnode: is a pointer to astruct Curl_llist_node. Data storage for this node.
Example: to add a struct foobar to a linked list. Add a node struct within
it:
struct foobar {
char *random;
struct Curl_llist_node storage; /* can be anywhere in the struct */
char *data;
};
struct Curl_llist barlist; /* the list for foobar entries */
struct foobar entries[10];
Curl_llist_init(&barlist, NULL);
/* add the first struct to the list */
Curl_llist_append(&barlist, &entries[0], &entries[0].storage);
See also Curl_llist_insert_next.
Remove a node
Remove a node again from a list by calling Curl_llist_remove(). This
destroys the node's elem (e.g. calling a registered free function).
To remove a node without destroying its elem, use Curl_node_take_elem()
which returns the elem pointer and removes the node from the list. The
caller then owns this pointer and has to take care of it.
Iterate
To iterate over a list: first get the head entry and then iterate over the nodes as long there is a next. Each node has an element associated with it, the custom pointer you stored there. Usually a struct pointer or similar.
struct Curl_llist_node *iter;
/* get the first entry of the 'barlist' */
iter = Curl_llist_head(&barlist);
while(iter) {
/* extract the element pointer from the node */
struct foobar *elem = Curl_node_elem(iter);
/* advance to the next node in the list */
iter = Curl_node_next(iter);
}
Function overview
Curl_llist_init
void Curl_llist_init(struct Curl_llist *list, Curl_llist_dtor dtor);
Initializes the list. The argument dtor is NULL or a function pointer that
gets called when list nodes are removed from this list.
The function is infallible.
typedef void (*Curl_llist_dtor)(void *user, void *elem);
dtor is called with two arguments: user and elem. The first being the
user pointer passed in to Curl_llist_remove()or Curl_llist_destroy() and
the second is the elem pointer associated with removed node. The pointer
that Curl_node_elem() would have returned for that node.
Curl_llist_destroy
void Curl_llist_destroy(struct Curl_llist *list, void *user);
This removes all nodes from the list. This leaves the list in a cleared
state.
The function is infallible.
Curl_llist_append
void Curl_llist_append(struct Curl_llist *list,
const void *elem, struct Curl_llist_node *node);
Adds node last in the list with a custom pointer to elem.
The function is infallible.
Curl_llist_insert_next
void Curl_llist_insert_next(struct Curl_llist *list,
struct Curl_llist_node *node,
const void *elem,
struct Curl_llist_node *node);
Adds node to the list with a custom pointer to elem immediately after
the previous list node.
The function is infallible.
Curl_llist_head
struct Curl_llist_node *Curl_llist_head(struct Curl_llist *list);
Returns a pointer to the first node of the list, or a NULL if empty.
Curl_node_uremove
void Curl_node_uremove(struct Curl_llist_node *node, void *user);
Removes the node the list it was previously added to. Passes the user
pointer to the list's destructor function if one was setup.
The function is infallible.
Curl_node_remove
void Curl_node_remove(struct Curl_llist_node *node);
Removes the node the list it was previously added to. Passes a NULL pointer
to the list's destructor function if one was setup.
The function is infallible.
Curl_node_elem
void *Curl_node_elem(struct Curl_llist_node *node);
Given a list node, this function returns the associated element.
Curl_node_next
struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *node);
Given a list node, this function returns the next node in the list.