Files
jak-project/third-party/curl/projects/Windows/generate.bat
T
Alexander J. Semenuk 5d5e35fb9b fix: Windows toolchain compatibility (curl 8.21 re-vendor, endless reconfigure loop) (#4355)
## 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)
2026-07-27 19:19:18 -04:00

356 lines
9.8 KiB
Batchfile
Vendored
Generated

@echo off
rem ***************************************************************************
rem * _ _ ____ _
rem * Project ___| | | | _ \| |
rem * / __| | | | |_) | |
rem * | (__| |_| | _ <| |___
rem * \___|\___/|_| \_\_____|
rem *
rem * Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
rem *
rem * This software is licensed as described in the file COPYING, which
rem * you should have received as part of this distribution. The terms
rem * are also available at https://curl.se/docs/copyright.html.
rem *
rem * You may opt to use, copy, modify, merge, publish, distribute and/or sell
rem * copies of the Software, and permit persons to whom the Software is
rem * furnished to do so, under the terms of the COPYING file.
rem *
rem * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
rem * KIND, either express or implied.
rem *
rem * SPDX-License-Identifier: curl
rem *
rem ***************************************************************************
:begin
rem Check we are running on a Windows NT derived OS
if not "%OS%" == "Windows_NT" goto nodos
rem Set our variables
setlocal ENABLEEXTENSIONS
set VERSION=ALL
set MODE=GENERATE
rem Check we are not running on a network drive
if "%~d0."=="\\." goto nonetdrv
rem Switch to this batch file's directory
cd /d "%~0\.." 1>NUL 2>&1
rem Check we are running from a curl git repository
if not exist ..\..\GIT-INFO.md goto norepo
:parseArgs
if "%~1" == "" goto start
if /i "%~1" == "vc10" (
set VERSION=VC10
) else if /i "%~1" == "vc11" (
set VERSION=VC11
) else if /i "%~1" == "vc12" (
set VERSION=VC12
) else if /i "%~1" == "-clean" (
set MODE=CLEAN
) else if /i "%~1" == "-?" (
goto syntax
) else if /i "%~1" == "/?" (
goto syntax
) else if /i "%~1" == "-h" (
goto syntax
) else if /i "%~1" == "-help" (
goto syntax
) else if /i "%~1" == "--help" (
goto syntax
) else (
goto unknown
)
shift & goto parseArgs
:start
if "%VERSION%" == "VC10" goto vc10
if "%VERSION%" == "VC11" goto vc11
if "%VERSION%" == "VC12" goto vc12
:vc10
if "%MODE%" == "GENERATE" (
call :generate_proj VC10
) else (
call :clean_proj VC10
)
if not "%VERSION%" == "ALL" goto success
:vc11
if "%MODE%" == "GENERATE" (
call :generate_proj VC11
) else (
call :clean_proj VC11
)
if not "%VERSION%" == "ALL" goto success
:vc12
if "%MODE%" == "GENERATE" (
call :generate_proj VC12
) else (
call :clean_proj VC12
)
goto success
rem Main project generate function.
rem
rem %1 - version
rem %2 - Input template file
rem %3 - Output project file
rem
:generate_proj
echo.
echo Generating %1 project files
if not exist %1\lib md %1\lib
if not exist %1\src md %1\src
call :generate %1 tmpl\curl-all.sln %1\curl-all.sln
call :generate %1 tmpl\curl.sln %1\src\curl.sln
call :generate %1 tmpl\curl.vcxproj %1\src\curl.vcxproj
call :generate %1 tmpl\curl.vcxproj.filters %1\src\curl.vcxproj.filters
call :generate %1 tmpl\libcurl.sln %1\lib\libcurl.sln
call :generate %1 tmpl\libcurl.vcxproj %1\lib\libcurl.vcxproj
call :generate %1 tmpl\libcurl.vcxproj.filters %1\lib\libcurl.vcxproj.filters
exit /B
rem Main project clean function.
rem
rem %1 - version
rem
:clean_proj
echo.
echo Removing %1 project files
call :clean %1\curl-all.sln
call :clean %1\src\curl.sln
call :clean %1\src\curl.vcxproj
call :clean %1\src\curl.vcxproj.filters
call :clean %1\lib\libcurl.sln
call :clean %1\lib\libcurl.vcxproj
call :clean %1\lib\libcurl.vcxproj.filters
exit /B
rem Main file generate function.
rem
rem %1 - version
rem %2 - Input template file
rem %3 - Output project file
rem
:generate
if not exist %2 (
echo.
echo Error: Cannot open %2
exit /B
)
if exist %3 (
del %3
)
set "S01=$FORMATVER"
set "S02=$PLATFORMTOOLSET"
set "S03=$SUBDIR"
set "S04=$TOOLSVER"
if "%1" == "VC10" (
set "R01=11.00"
set "R02=v100"
set "R03=VC10"
set "R04=4.0"
) else if "%1" == "VC11" (
set "R01=12.00"
set "R02=v110"
set "R03=VC11"
set "R04=4.0"
) else if "%1" == "VC12" (
set "R01=12.00"
set "R02=v120"
set "R03=VC12"
set "R04=12.0"
)
echo * %CD%\%3
for /f "usebackq delims=" %%i in (`"findstr /n ^^ %2"`) do (
set "var=%%i"
setlocal enabledelayedexpansion
set "var=!var:%S01%=%R01%!"
set "var=!var:%S02%=%R02%!"
set "var=!var:%S03%=%R03%!"
set "var=!var:%S04%=%R04%!"
set "var=!var:*:=!"
if "!var!" == "CURL_SRC_C_FILES" (
for /f "delims=" %%c in ('dir /b ..\..\src\*.c') do (
if /i "%%c" NEQ "curlinfo.c" call :element src "%%c" %3
)
) else if "!var!" == "CURL_SRC_H_FILES" (
for /f "delims=" %%h in ('dir /b ..\..\src\*.h') do call :element src "%%h" %3
) else if "!var!" == "CURL_SRC_RC_FILES" (
for /f "delims=" %%r in ('dir /b ..\..\src\*.rc') do call :element src "%%r" %3
) else if "!var!" == "CURL_SRC_X_H_FILES" (
call :element lib "config-win32.h" %3
call :element lib "curl_setup.h" %3
) else if "!var!" == "CURL_LIB_C_FILES" (
for /f "delims=" %%c in ('dir /b ..\..\lib\*.c') do call :element lib "%%c" %3
) else if "!var!" == "CURL_LIB_H_FILES" (
for /f "delims=" %%h in ('dir /b ..\..\include\curl\*.h') do call :element include\curl "%%h" %3
for /f "delims=" %%h in ('dir /b ..\..\lib\*.h') do call :element lib "%%h" %3
) else if "!var!" == "CURL_LIB_RC_FILES" (
for /f "delims=" %%r in ('dir /b ..\..\lib\*.rc') do call :element lib "%%r" %3
) else if "!var!" == "CURL_LIB_CURLX_C_FILES" (
for /f "delims=" %%c in ('dir /b ..\..\lib\curlx\*.c') do call :element lib\curlx "%%c" %3
) else if "!var!" == "CURL_LIB_CURLX_H_FILES" (
for /f "delims=" %%h in ('dir /b ..\..\lib\curlx\*.h') do call :element lib\curlx "%%h" %3
) else if "!var!" == "CURL_LIB_VAUTH_C_FILES" (
for /f "delims=" %%c in ('dir /b ..\..\lib\vauth\*.c') do call :element lib\vauth "%%c" %3
) else if "!var!" == "CURL_LIB_VAUTH_H_FILES" (
for /f "delims=" %%h in ('dir /b ..\..\lib\vauth\*.h') do call :element lib\vauth "%%h" %3
) else if "!var!" == "CURL_LIB_VQUIC_C_FILES" (
for /f "delims=" %%c in ('dir /b ..\..\lib\vquic\*.c') do call :element lib\vquic "%%c" %3
) else if "!var!" == "CURL_LIB_VQUIC_H_FILES" (
for /f "delims=" %%h in ('dir /b ..\..\lib\vquic\*.h') do call :element lib\vquic "%%h" %3
) else if "!var!" == "CURL_LIB_VSSH_C_FILES" (
for /f "delims=" %%c in ('dir /b ..\..\lib\vssh\*.c') do call :element lib\vssh "%%c" %3
) else if "!var!" == "CURL_LIB_VSSH_H_FILES" (
for /f "delims=" %%h in ('dir /b ..\..\lib\vssh\*.h') do call :element lib\vssh "%%h" %3
) else if "!var!" == "CURL_LIB_VTLS_C_FILES" (
for /f "delims=" %%c in ('dir /b ..\..\lib\vtls\*.c') do call :element lib\vtls "%%c" %3
) else if "!var!" == "CURL_LIB_VTLS_H_FILES" (
for /f "delims=" %%h in ('dir /b ..\..\lib\vtls\*.h') do call :element lib\vtls "%%h" %3
) else if "!var!" == "CURL_SRC_TOOLX_C_FILES" (
for /f "delims=" %%c in ('dir /b ..\..\src\toolx\*.c') do call :element src\toolx "%%c" %3
) else if "!var!" == "CURL_SRC_TOOLX_H_FILES" (
for /f "delims=" %%h in ('dir /b ..\..\src\toolx\*.h') do call :element src\toolx "%%h" %3
) else (
echo.!var!>> %3
)
endlocal
)
exit /B
rem Generates a single file xml element.
rem
rem %1 - Directory (src, lib, lib\vauth, lib\vquic, lib\vssh, lib\vtls)
rem %2 - Source filename
rem %3 - Output project file
rem
:element
set "SPACES= "
call :extension %2 ext
if "%ext%" == "c" (
echo %SPACES%^<ClCompile Include=^"..\..\..\..\%1\%~2^" /^>>> %3
) else if "%ext%" == "h" (
echo %SPACES%^<ClInclude Include=^"..\..\..\..\%1\%~2^" /^>>> %3
) else if "%ext%" == "rc" (
echo %SPACES%^<ResourceCompile Include=^"..\..\..\..\%1\%~2^" /^>>> %3
)
exit /B
rem Returns the extension for a given filename.
rem
rem %1 - The filename
rem %2 - The return value
rem
:extension
set fname=%~1
set ename=
:loop1
if "%fname%"=="" (
set %2=
exit /B
)
if not "%fname:~-1%"=="." (
set ename=%fname:~-1%%ename%
set fname=%fname:~0,-1%
goto loop1
)
set %2=%ename%
exit /B
rem Removes the given project file.
rem
rem %1 - The filename
rem
:clean
echo * %CD%\%1
if exist %1 (
del %1
)
exit /B
:syntax
rem Display the help
echo.
echo Usage: generate [what] [-clean]
echo.
echo What to generate:
echo.
echo vc10 - Use Visual Studio 2010
echo vc11 - Use Visual Studio 2012
echo vc12 - Use Visual Studio 2013
echo.
echo Only legacy Visual Studio project files can be generated.
echo.
echo To generate recent versions of Visual Studio project files use cmake.
echo Refer to INSTALL-CMAKE.md in the docs directory.
echo.
echo -clean - Removes the project files
goto error
:unknown
echo.
echo Error: Unknown argument '%1'
goto error
:nodos
echo.
echo Error: Only a Windows NT based Operating System is supported
goto error
:nonetdrv
echo.
echo Error: This batch file cannot run from a network drive
goto error
:norepo
echo.
echo Error: This batch file should only be used from a curl git repository
goto error
:seterr
rem Set the caller's errorlevel.
rem %1[opt]: Errorlevel as integer.
rem If %1 is empty the errorlevel is set to 0.
rem If %1 is not empty and not an integer the errorlevel is set to 1.
setlocal
set EXITCODE=%~1
if not defined EXITCODE set EXITCODE=0
echo %EXITCODE%|findstr /r "[^0-9\-]" 1>NUL 2>&1
if %ERRORLEVEL% EQU 0 set EXITCODE=1
exit /b %EXITCODE%
:error
if "%OS%" == "Windows_NT" endlocal
exit /B 1
:success
endlocal
exit /B 0