mirror of
https://github.com/open-goal/jak-project
synced 2026-08-12 11:53:26 -04:00
5d5e35fb9b
## 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)
252 lines
5.3 KiB
Perl
Vendored
Generated
252 lines
5.3 KiB
Perl
Vendored
Generated
#!/usr/bin/env perl
|
|
# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
|
#
|
|
# SPDX-License-Identifier: curl
|
|
#
|
|
# Input: number of seconds to run.
|
|
#
|
|
# 1. Figure out all existing command line options
|
|
# 2. Generate random command line using supported options
|
|
# 3. Run the command line
|
|
# 4. Verify that it does not return an unexpected return code
|
|
# 5. Iterate until the time runs out
|
|
#
|
|
# Do the same with regular command lines as well as reading the options from a
|
|
# -K config file
|
|
#
|
|
# BEWARE: this may create a large amount of files using random names in the
|
|
# directory where it runs.
|
|
#
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
my $curl = "../src/curl";
|
|
my $url = "localhost:7777"; # not listening to this
|
|
|
|
my $seconds = $ARGV[0];
|
|
if($ARGV[1]) {
|
|
$curl = $ARGV[1];
|
|
}
|
|
|
|
if(!$seconds) {
|
|
$seconds = 10;
|
|
}
|
|
print "Run $curl for $seconds seconds\n";
|
|
|
|
my @opt;
|
|
my %arg;
|
|
my %uniq;
|
|
my %allrc;
|
|
|
|
my $totalargs = 0;
|
|
my $totalcmds = 0;
|
|
|
|
my $counter = 0xabcdef + time();
|
|
sub getnum {
|
|
my ($max) = @_;
|
|
return int(rand($max));
|
|
}
|
|
|
|
sub storedata {
|
|
my ($short, $long, $arg) = @_;
|
|
push @opt, "-$short" if($short);
|
|
push @opt, "--$long";
|
|
|
|
if($arg =~ /^</) {
|
|
# these take an argument
|
|
$arg{"-$short"} = $arg if($short);
|
|
$arg{"--$long"} = $arg;
|
|
}
|
|
}
|
|
|
|
sub getoptions {
|
|
my @all = qx($curl --help all);
|
|
for my $o (@all) {
|
|
chomp $o;
|
|
if($o =~ /^ -(.), --([^ ]*) (.*)/) {
|
|
storedata($1, $2, $3);
|
|
}
|
|
elsif($o =~ /^ --([^ ]*) (.*)/) {
|
|
storedata("", $1, $2);
|
|
}
|
|
}
|
|
}
|
|
|
|
# this adds a fake randomly generated command line option
|
|
sub addarg {
|
|
my $nice = "abcdefhijklmnopqrstuvwqxyz".
|
|
"ABCDEFHIJKLMNOPQRSTUVWQXYZ".
|
|
"0123456789-";
|
|
my $len = getnum(20) + 2;
|
|
my $o;
|
|
for (1 .. $len) {
|
|
$o .= substr($nice, getnum(length($nice)), 1);
|
|
}
|
|
return "--$o";
|
|
}
|
|
|
|
sub randarg {
|
|
my $nice = "abcdefhijklmnopqrstuvwqxyz".
|
|
"ABCDEFHIJKLMNOPQRSTUVWQXYZ".
|
|
"0123456789".
|
|
",-?#$%!@ ";
|
|
my $len = getnum(20);
|
|
my $o = '';
|
|
for (1 .. $len) {
|
|
$o .= substr($nice, getnum(length($nice)), 1);
|
|
}
|
|
return "\'$o\'";
|
|
}
|
|
|
|
getoptions();
|
|
|
|
my $nopts = scalar(@opt);
|
|
|
|
my %useropt = (
|
|
'-U' => 1,
|
|
'-u' => 1,
|
|
'--user' => 1,
|
|
'--proxy-user' => 1);
|
|
|
|
my %commonrc = (
|
|
'0' => 1,
|
|
'1' => 1,
|
|
'2' => 1,
|
|
'26' => 1,
|
|
);
|
|
|
|
sub runone {
|
|
my $a;
|
|
my $nargs = getnum(60) + 1;
|
|
|
|
$totalargs += $nargs;
|
|
$totalcmds++;
|
|
for (1 .. $nargs) {
|
|
my $o = getnum($nopts);
|
|
my $option = $opt[$o];
|
|
my $ar = "";
|
|
$uniq{$option}++;
|
|
if($arg{$option}) {
|
|
$ar = " ".randarg();
|
|
|
|
if($useropt{$option}) {
|
|
# append password to avoid prompting
|
|
$ar .= ":".randarg();
|
|
}
|
|
}
|
|
$a .= sprintf(" %s%s", $option, $ar);
|
|
}
|
|
if(getnum(100) < 15) {
|
|
# add a fake arg
|
|
$a .= " ".addarg();
|
|
}
|
|
|
|
my $cmd = "$curl$a $url";
|
|
|
|
my $rc = system("$cmd >curl-output 2>&1 </dev/null -M 0.1") >> 8;
|
|
#my $rc = system("valgrind -q $cmd >/dev/null 2>&1 </dev/null -M 0.1") >> 8;
|
|
|
|
$allrc{$rc}++;
|
|
|
|
#print "CMD: $cmd\n";
|
|
if(!$commonrc{$rc}) {
|
|
print "CMD: $cmd\n";
|
|
print "RC: $rc\n";
|
|
print "== curl-output == \n";
|
|
open(D, "<curl-output");
|
|
my @out = <D>;
|
|
print @out;
|
|
close(D);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
sub runconfig {
|
|
my $a;
|
|
my $nargs = getnum(80) + 1;
|
|
|
|
open(C, ">config");
|
|
|
|
$totalargs += $nargs;
|
|
$totalcmds++;
|
|
for (1 .. $nargs) {
|
|
my $o = getnum($nopts);
|
|
my $option = $opt[$o];
|
|
my $ar = "";
|
|
$uniq{$option} = 0 if(!exists $uniq{$option});
|
|
$uniq{$option}++;
|
|
if($arg{$option}) {
|
|
$ar = " ".randarg();
|
|
|
|
if($useropt{$option}) {
|
|
# append password
|
|
$ar .= ":".randarg();
|
|
}
|
|
}
|
|
$a .= sprintf("\n%s%s", $option, $ar);
|
|
}
|
|
if(getnum(100) < 15) {
|
|
# add a fake arg
|
|
$a .= "\n".addarg();
|
|
}
|
|
|
|
print C "$a\n";
|
|
close(C);
|
|
|
|
my $cmd = "$curl -K config $url";
|
|
|
|
my $rc = system("$cmd >curl-output 2>&1 </dev/null -M 0.1") >> 8;
|
|
|
|
$allrc{$rc}++;
|
|
|
|
if(!$commonrc{$rc}) {
|
|
print "CMD: $cmd\n";
|
|
print "RC: $rc\n";
|
|
print "== config == \n";
|
|
open(D, "<config");
|
|
my @all = <D>;
|
|
print @all;
|
|
close(D);
|
|
print "\n== curl-output == \n";
|
|
open(D, "<curl-output");
|
|
my @out = <D>;
|
|
print @out;
|
|
close(D);
|
|
exit 2;
|
|
}
|
|
}
|
|
|
|
# run curl command lines using -K
|
|
my $end = time() + $seconds / 2;
|
|
my $c = 0;
|
|
print "Running command lines\n";
|
|
do {
|
|
runconfig();
|
|
$c++;
|
|
} while(time() <= $end);
|
|
print "$c command lines\n";
|
|
|
|
# run curl command lines
|
|
$end = time() + $seconds / 2;
|
|
$c = 0;
|
|
print "Running config lines\n";
|
|
do {
|
|
runone();
|
|
$c++;
|
|
} while(time() <= $end);
|
|
|
|
print "$c config line uses\n";
|
|
|
|
print "Recorded exit codes:\n";
|
|
for my $rc (keys %allrc) {
|
|
printf " %2d: %d times\n", $rc, $allrc{$rc};
|
|
}
|
|
printf "Number or command lines tested:\n".
|
|
" $totalcmds (%.1f/second)\n", $totalcmds/$seconds;
|
|
printf "Number or command line options tested:\n".
|
|
" $totalargs (average %.1f per command line)\n",
|
|
$totalargs/$totalcmds;
|
|
printf "Number or different options tested:\n".
|
|
" %u out of %u\n", scalar(keys %uniq), $nopts;
|