mirror of
https://github.com/open-goal/jak-project
synced 2026-08-06 18:03:30 -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)
421 lines
12 KiB
Perl
Vendored
Generated
421 lines
12 KiB
Perl
Vendored
Generated
#***************************************************************************
|
|
# _ _ ____ _
|
|
# Project ___| | | | _ \| |
|
|
# / __| | | | |_) | |
|
|
# | (__| |_| | _ <| |___
|
|
# \___|\___/|_| \_\_____|
|
|
#
|
|
# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
|
#
|
|
# This software is licensed as described in the file COPYING, which
|
|
# you should have received as part of this distribution. The terms
|
|
# are also available at https://curl.se/docs/copyright.html.
|
|
#
|
|
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
|
# copies of the Software, and permit persons to whom the Software is
|
|
# furnished to do so, under the terms of the COPYING file.
|
|
#
|
|
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
# KIND, either express or implied.
|
|
#
|
|
# SPDX-License-Identifier: curl
|
|
#
|
|
#***************************************************************************
|
|
|
|
package sshhelp;
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
BEGIN {
|
|
use base qw(Exporter);
|
|
|
|
our @EXPORT_OK = qw(
|
|
$sshdexe
|
|
$sshexe
|
|
$sftpsrvexe
|
|
$sftpexe
|
|
$sshkeygenexe
|
|
$sshdconfig
|
|
$sshconfig
|
|
$sftpconfig
|
|
$knownhosts
|
|
$sshdlog
|
|
$sshlog
|
|
$sftplog
|
|
$sftpcmds
|
|
$hstprvkeyf
|
|
$hstpubkeyf
|
|
$hstpubmd5f
|
|
$hstpubsha256f
|
|
$cliprvkeyf
|
|
$clipubkeyf
|
|
display_file_top
|
|
display_sshdconfig
|
|
display_sshconfig
|
|
display_sftpconfig
|
|
display_sshdlog
|
|
display_sshlog
|
|
display_sftplog
|
|
dump_array
|
|
find_sshd
|
|
find_ssh
|
|
find_sftpsrv
|
|
find_sftp
|
|
find_sshkeygen
|
|
find_httptlssrv
|
|
sshversioninfo
|
|
);
|
|
}
|
|
|
|
use File::Spec;
|
|
|
|
use pathhelp qw(
|
|
exe_ext
|
|
);
|
|
|
|
#***************************************************************************
|
|
# Global variables initialization
|
|
#
|
|
our $sshdexe = 'sshd' .exe_ext('SSH'); # base name and ext of ssh daemon
|
|
our $sshexe = 'ssh' .exe_ext('SSH'); # base name and ext of ssh client
|
|
our $sftpsrvexe = 'sftp-server' .exe_ext('SSH'); # base name and ext of sftp-server
|
|
our $sftpexe = 'sftp' .exe_ext('SSH'); # base name and ext of sftp client
|
|
our $sshkeygenexe = 'ssh-keygen' .exe_ext('SSH'); # base name and ext of ssh-keygen
|
|
our $httptlssrvexe = 'gnutls-serv' .exe_ext('SSH'); # base name and ext of gnutls-serv
|
|
our $sshdconfig = 'curl_sshd_config'; # ssh daemon config file
|
|
our $sshconfig = 'curl_ssh_config'; # ssh client config file
|
|
our $sftpconfig = 'curl_sftp_config'; # sftp client config file
|
|
our $sshdlog = undef; # ssh daemon log file
|
|
our $sshlog = undef; # ssh client log file
|
|
our $sftplog = undef; # sftp client log file
|
|
our $sftpcmds = 'curl_sftp_cmds'; # sftp client commands batch file
|
|
our $knownhosts = 'curl_client_knownhosts'; # ssh knownhosts file
|
|
our $hstprvkeyf = 'curl_host_key'; # host private key file
|
|
our $hstpubkeyf = 'curl_host_key.pub'; # host public key file
|
|
our $hstpubmd5f = 'curl_host_key.pub_md5'; # md5 hash of host public key
|
|
our $hstpubsha256f = 'curl_host_key.pub_sha256'; # sha256 hash of host public key
|
|
our $cliprvkeyf = 'curl_client_key'; # client private key file
|
|
our $clipubkeyf = 'curl_client_key.pub'; # client public key file
|
|
|
|
#***************************************************************************
|
|
# Absolute paths where to look for sftp-server plugin, when not in PATH
|
|
#
|
|
our @sftppath = qw(
|
|
/usr/lib/openssh
|
|
/usr/libexec/openssh
|
|
/usr/libexec
|
|
/usr/local/libexec
|
|
/opt/local/libexec
|
|
/usr/lib/ssh
|
|
/usr/libexec/ssh
|
|
/usr/sbin
|
|
/usr/lib
|
|
/usr/lib/ssh/openssh
|
|
/usr/lib64/ssh
|
|
/usr/lib64/misc
|
|
/usr/lib/misc
|
|
/usr/local/sbin
|
|
/usr/freeware/bin
|
|
/usr/freeware/sbin
|
|
/usr/freeware/libexec
|
|
/opt/ssh/sbin
|
|
/opt/ssh/libexec
|
|
);
|
|
|
|
#***************************************************************************
|
|
# Absolute paths where to look for httptlssrv (gnutls-serv), when not in PATH
|
|
#
|
|
our @httptlssrvpath = qw(
|
|
/usr/sbin
|
|
/usr/libexec
|
|
/usr/lib
|
|
/usr/lib/misc
|
|
/usr/lib64/misc
|
|
/usr/local/bin
|
|
/usr/local/sbin
|
|
/usr/local/libexec
|
|
/opt/local/bin
|
|
/opt/local/sbin
|
|
/opt/local/libexec
|
|
/usr/freeware/bin
|
|
/usr/freeware/sbin
|
|
/usr/freeware/libexec
|
|
/opt/gnutls/bin
|
|
/opt/gnutls/sbin
|
|
/opt/gnutls/libexec
|
|
);
|
|
|
|
#***************************************************************************
|
|
# Create or overwrite the given file with lines from an array of strings
|
|
#
|
|
sub dump_array {
|
|
my ($filename, @arr) = @_;
|
|
my $error;
|
|
|
|
if(!$filename) {
|
|
$error = 'Error: Missing argument 1 for dump_array()';
|
|
}
|
|
elsif(open(my $textfh, ">", $filename)) {
|
|
foreach my $line (@arr) {
|
|
$line .= "\n" if($line !~ /\n$/);
|
|
print $textfh $line;
|
|
}
|
|
if(!close($textfh)) {
|
|
$error = "Error: cannot close file $filename";
|
|
}
|
|
}
|
|
else {
|
|
$error = "Error: cannot write file $filename";
|
|
}
|
|
return $error;
|
|
}
|
|
|
|
#***************************************************************************
|
|
# Display contents of the given file
|
|
#
|
|
sub display_file {
|
|
my $filename = $_[0];
|
|
print "=== Start of file $filename\n";
|
|
if(open(my $displayfh, "<", $filename)) {
|
|
while(my $line = <$displayfh>) {
|
|
print $line;
|
|
}
|
|
close $displayfh;
|
|
}
|
|
print "=== End of file $filename\n";
|
|
}
|
|
|
|
#***************************************************************************
|
|
# Display first line of the given file
|
|
#
|
|
sub display_file_top {
|
|
my $filename = $_[0];
|
|
print "=== Top of file $filename\n";
|
|
if(open(my $displayfh, "<", $filename)) {
|
|
my $line = <$displayfh>;
|
|
print $line;
|
|
close $displayfh;
|
|
}
|
|
print "=== End of file $filename\n";
|
|
}
|
|
|
|
#***************************************************************************
|
|
# Display contents of the ssh daemon config file
|
|
#
|
|
sub display_sshdconfig {
|
|
display_file($sshdconfig);
|
|
}
|
|
|
|
#***************************************************************************
|
|
# Display contents of the ssh client config file
|
|
#
|
|
sub display_sshconfig {
|
|
display_file($sshconfig);
|
|
}
|
|
|
|
#***************************************************************************
|
|
# Display contents of the sftp client config file
|
|
#
|
|
sub display_sftpconfig {
|
|
display_file($sftpconfig);
|
|
}
|
|
|
|
#***************************************************************************
|
|
# Display contents of the ssh daemon log file
|
|
#
|
|
sub display_sshdlog {
|
|
die "error: \$sshdlog uninitialized" if(not defined $sshdlog);
|
|
display_file($sshdlog);
|
|
}
|
|
|
|
#***************************************************************************
|
|
# Display contents of the ssh client log file
|
|
#
|
|
sub display_sshlog {
|
|
die "error: \$sshlog uninitialized" if(not defined $sshlog);
|
|
display_file($sshlog);
|
|
}
|
|
|
|
#***************************************************************************
|
|
# Display contents of the sftp client log file
|
|
#
|
|
sub display_sftplog {
|
|
die "error: \$sftplog uninitialized" if(not defined $sftplog);
|
|
display_file($sftplog);
|
|
}
|
|
|
|
#***************************************************************************
|
|
# Find a file somewhere in the given path
|
|
#
|
|
sub find_file {
|
|
my $fn = $_[0];
|
|
shift;
|
|
my @path = @_;
|
|
foreach (@path) {
|
|
my $file = File::Spec->catfile($_, $fn);
|
|
if(-e $file && ! -d $file) {
|
|
return $file;
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
#***************************************************************************
|
|
# Find an executable file somewhere in the given path
|
|
#
|
|
sub find_exe_file {
|
|
my $fn = $_[0];
|
|
shift;
|
|
my @path = @_;
|
|
my $xext = exe_ext('SSH');
|
|
foreach (@path) {
|
|
my $file = File::Spec->catfile($_, $fn);
|
|
if(-e $file && ! -d $file) {
|
|
return $file if(-x $file);
|
|
return $file if(($xext) && (lc($file) =~ /\Q$xext\E$/));
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
#***************************************************************************
|
|
# Find a file in environment path or in our sftppath
|
|
#
|
|
sub find_file_spath {
|
|
my $filename = $_[0];
|
|
my @spath;
|
|
push(@spath, File::Spec->path());
|
|
push(@spath, @sftppath);
|
|
return find_file($filename, @spath);
|
|
}
|
|
|
|
#***************************************************************************
|
|
# Find an executable file in environment path or in our httptlssrvpath
|
|
#
|
|
sub find_exe_file_hpath {
|
|
my $filename = $_[0];
|
|
my @hpath;
|
|
push(@hpath, File::Spec->path());
|
|
push(@hpath, @httptlssrvpath);
|
|
return find_exe_file($filename, @hpath);
|
|
}
|
|
|
|
#***************************************************************************
|
|
# Find ssh daemon and return canonical filename
|
|
#
|
|
sub find_sshd {
|
|
return find_file_spath($sshdexe);
|
|
}
|
|
|
|
#***************************************************************************
|
|
# Find ssh client and return canonical filename
|
|
#
|
|
sub find_ssh {
|
|
return find_file_spath($sshexe);
|
|
}
|
|
|
|
#***************************************************************************
|
|
# Find sftp-server plugin and return canonical filename
|
|
#
|
|
sub find_sftpsrv {
|
|
return find_file_spath($sftpsrvexe);
|
|
}
|
|
|
|
#***************************************************************************
|
|
# Find sftp client and return canonical filename
|
|
#
|
|
sub find_sftp {
|
|
return find_file_spath($sftpexe);
|
|
}
|
|
|
|
#***************************************************************************
|
|
# Find ssh-keygen and return canonical filename
|
|
#
|
|
sub find_sshkeygen {
|
|
return find_file_spath($sshkeygenexe);
|
|
}
|
|
|
|
#***************************************************************************
|
|
# Find httptlssrv (gnutls-serv) and return canonical filename
|
|
#
|
|
sub find_httptlssrv {
|
|
my $p = find_exe_file_hpath($httptlssrvexe);
|
|
if($p) {
|
|
my @o = qx("$p" -l);
|
|
my $found;
|
|
for(@o) {
|
|
if(/Key exchange: SRP/) {
|
|
$found = 1;
|
|
last;
|
|
}
|
|
}
|
|
return $p if($found);
|
|
}
|
|
return "";
|
|
}
|
|
|
|
#***************************************************************************
|
|
# Return version info for the given ssh client or server binaries
|
|
#
|
|
sub sshversioninfo {
|
|
my $sshbin = $_[0]; # canonical filename
|
|
my $major;
|
|
my $minor;
|
|
my $patch;
|
|
my $sshid;
|
|
my $versnum;
|
|
my $versstr;
|
|
my $error;
|
|
|
|
if(!$sshbin) {
|
|
$error = 'Error: Missing argument 1 for sshversioninfo()';
|
|
}
|
|
elsif(! -x $sshbin) {
|
|
$error = "Error: cannot read or execute $sshbin";
|
|
}
|
|
else {
|
|
my $cmd = ($sshbin =~ /$sshdexe$/) ? "\"$sshbin\" -?" : "\"$sshbin\" -V";
|
|
$error = "$cmd\n";
|
|
foreach my $tmpstr (qx($cmd 2>&1)) {
|
|
if($tmpstr =~ /OpenSSH[_-](\d+)\.(\d+)(\.(\d+))*/i) {
|
|
$major = $1;
|
|
$minor = $2;
|
|
$patch = $4 ? $4 : 0;
|
|
$sshid = 'OpenSSH';
|
|
$versnum = (100*$major) + (10*$minor) + $patch;
|
|
$versstr = "$sshid $major.$minor.$patch";
|
|
$error = undef;
|
|
last;
|
|
}
|
|
if($tmpstr =~ /OpenSSH[_-]for[_-]Windows[_-](\d+)\.(\d+)(\.(\d+))*/i) {
|
|
$major = $1;
|
|
$minor = $2;
|
|
$patch = $4 ? $4 : 0;
|
|
$sshid = 'OpenSSH-Windows';
|
|
$versnum = (100*$major) + (10*$minor) + $patch;
|
|
$versstr = "$sshid $major.$minor.$patch";
|
|
$error = undef;
|
|
last;
|
|
}
|
|
if($tmpstr =~ /Sun[_-]SSH[_-](\d+)\.(\d+)(\.(\d+))*/i) {
|
|
$major = $1;
|
|
$minor = $2;
|
|
$patch = $4 ? $4 : 0;
|
|
$sshid = 'SunSSH';
|
|
$versnum = (100*$major) + (10*$minor) + $patch;
|
|
$versstr = "$sshid $major.$minor.$patch";
|
|
$error = undef;
|
|
last;
|
|
}
|
|
$error .= $tmpstr;
|
|
}
|
|
chomp $error if($error);
|
|
}
|
|
return ($sshid, $versnum, $versstr, $error);
|
|
}
|
|
|
|
#***************************************************************************
|
|
# End of library
|
|
1;
|