diff --git a/Common/3dParty/boost/nc-build.py b/Common/3dParty/boost/nc-build.py index 61ecc022fb..fb4ef84f0a 100644 --- a/Common/3dParty/boost/nc-build.py +++ b/Common/3dParty/boost/nc-build.py @@ -21,13 +21,31 @@ ) modules_needed = [ "headers", "system", "filesystem", "regex", "date_time" ] -header_only_modules_needed = [ "any", "asio", "beast", "foreach", "format", "functional", "multi_index", "uuid" ] +# Only these submodules get checked out, so every boost header the project +# includes must be reachable from this list (directly or as a boostdep-resolved +# dependency of one of them). +# +# ptr_container / serialization / spirit / variant are listed explicitly because +# they are NOT dependencies of anything above: they used to be pulled in +# transitively on 1.78, but boost has been migrating away from Boost.Variant +# towards std::variant, so on current boost they are no longer dragged in and +# the headers went missing -> "fatal error C1083: Cannot open include file: +# 'boost/variant.hpp'" in libetonyek, and the same for the spirit / ptr_container +# / boost-archive includes in librevenge. +# +# NOTE: boost/archive/** (the base64 iterators librevenge uses) ships in the +# SERIALIZATION module - there is no boost module called "archive". Those +# iterators are pure templates, so headers alone are enough and serialization +# does not have to be built. +header_only_modules_needed = [ "any", "asio", "beast", "foreach", "format", "functional", + "multi_index", "ptr_container", "serialization", "spirit", + "uuid", "variant" ] def fetch_and_patch(): nc.create_workdir() print( "Clone Boost 1.78.0..." ) nc.run_command( - [ "git", "clone", "https://github.com/boostorg/boost.git", "-b", "boost-1.78.0", nc.work_dir, "--depth", "1" ], + [ "git", "clone", "https://github.com/boostorg/boost.git", nc.work_dir, "--depth", "1" ], "Clone Boost 1.78.0" ) @@ -70,6 +88,36 @@ def boost_msvc_arch() -> tuple[ str, str ]: return "arm", "Hostarm64\\arm64" nc.abort_op( f"Unsupported target arch for boost: {a!r}" ) +def jam_path( p ) -> str: + return str( Path( p ) ).replace( "\\", "\\\\" ) + +def boost_msvc_toolset_version() -> str: + """ + The 'using msvc : ' value for the compiler we actually build with. + + Boost derives the library name tag from this value - common.jam's + toolset-tag joins major+minor, so 14.0 -> vc140, 14.3 -> vc143, + 14.5 -> vc145. CMake's BoostConfig computes the same tag from the compiler + it detects and REJECTS libs whose tag differs: + + libboost_filesystem-vc140-mt-x64-1_92.lib (vc140, detected vc145) + No suitable build variant has been found. + + which is exactly what happened while this was hardcoded to 14.0 but cl.exe + came from MSVC 14.51 - the libs were built by the right compiler, only the + name lied. So derive it from the toolchain actually in use. + + MSVC's tag keeps only the FIRST digit of the minor version (14.51 -> vc145, + 14.39 -> vc143), hence 'MAJOR.' from VCToolsVersion. + """ + ver = os.environ.get( "VCToolsVersion", "" ) + parts = ver.split( "." ) + if len( parts ) < 2 or not parts[ 0 ].isdigit() or not parts[ 1 ][ :1 ].isdigit(): + nc.abort_op( + f"Cannot derive the MSVC toolset version from VCToolsVersion={ ver!r }. " + "Is the MSVC environment loaded (vcvars)?" + ) + return f"{ parts[ 0 ] }.{ parts[ 1 ][ 0 ] }" def build_and_install(): nc.create_install_dir() @@ -94,14 +142,27 @@ def build_and_install(): if nc.is_windows(): print( "Fixing project-config.jam..." ) + msvc_version = boost_msvc_toolset_version() + print( f"Using MSVC toolset { msvc_version } " + f"(libs will be tagged vc{ msvc_version.replace( '.', '' ) })" ) + # Jam treats a backslash inside a quoted string as an escape, so a raw + # Windows path silently collapses ("C:\Program Files\..." arrives as + # "C:Program Files...") and b2 then warns "Did not find command for MSVC + # toolset" and falls back to whatever cl.exe is on PATH. jam_path() + # doubles the separators, which is exactly what it exists for. + # Built outside the f-string so no backslash appears in an f-string + # expression (only allowed from Python 3.12 on). + cl_path = jam_path( + Path( os.environ[ "VCToolsInstallDir" ] ) / "bin" / host_subdir / "cl.exe" + ) content = f""" # Boost.Build Configuration # Generated by nc-build.py - + import option ; - -using msvc : 14.0 : "{ os.environ[ "VCToolsInstallDir" ] }\\bin\\{ host_subdir }\\cl.exe"; - + +using msvc : { msvc_version } : "{ cl_path }"; + option.set keep-going : false ; """ diff --git a/Common/3dParty/cryptopp/integer.cpp b/Common/3dParty/cryptopp/integer.cpp index eae3350db7..e6a05dbec5 100644 --- a/Common/3dParty/cryptopp/integer.cpp +++ b/Common/3dParty/cryptopp/integer.cpp @@ -3056,7 +3056,13 @@ Integer::Integer(const byte *encodedInteger, size_t byteCount, Signedness s, Byt else { SecByteBlock block(byteCount); -#if (_MSC_VER >= 1500) +// The stdext:: iterator extensions were removed from the MSVC STL (gone as of +// MSVC 14.51 / _MSC_VER 1951), so a pure version check picks a namespace that +// no longer exists: "error C2653: 'stdext': is not a class or namespace". +// Gate on _STDEXT_BEGIN - the macro the MSVC headers use to open that namespace - +// like the guard in zdeflate.cpp already does. The portable path below is +// equivalent; the wrapper only added debug-time bounds checking. +#if (_MSC_VER >= 1500) && defined(_STDEXT_BEGIN) std::reverse_copy(encodedInteger, encodedInteger+byteCount, stdext::make_checked_array_iterator(block.begin(), block.size())); #else diff --git a/Common/3dParty/cryptopp/zdeflate.cpp b/Common/3dParty/cryptopp/zdeflate.cpp index 44413ab5e8..e87d07fe3b 100644 --- a/Common/3dParty/cryptopp/zdeflate.cpp +++ b/Common/3dParty/cryptopp/zdeflate.cpp @@ -418,7 +418,11 @@ unsigned int Deflator::LongestMatch(unsigned int &bestMatch) const #else std::mismatch #endif -#if _MSC_VER >= 1600 +// Same _STDEXT_BEGIN feature test as the guard above: the stdext:: extensions +// are gone from the MSVC STL (as of MSVC 14.51 / _MSC_VER 1951), so a bare +// version check would name a namespace that no longer exists. The unchecked +// wrappers only suppressed iterator-debug warnings, so raw pointers are equivalent. +#if _MSC_VER >= 1600 && defined(_STDEXT_BEGIN) (stdext::make_unchecked_array_iterator(scan)+3, stdext::make_unchecked_array_iterator(scanEnd), stdext::make_unchecked_array_iterator(match)+3).first - stdext::make_unchecked_array_iterator(scan)); #else (scan+3, scanEnd, match+3).first - scan); diff --git a/Common/3dParty/icu-desktop/nc-build-cygwin.sh b/Common/3dParty/icu-desktop/nc-build-cygwin.sh index a61dc0d744..1c539a9a00 100644 --- a/Common/3dParty/icu-desktop/nc-build-cygwin.sh +++ b/Common/3dParty/icu-desktop/nc-build-cygwin.sh @@ -8,7 +8,43 @@ if [[ $install_dir == /mnt/* ]]; then fi export PATH="$PATH:/usr/bin" -export PYTHON=/usr/bin/python3 + +# Pick a working Python 3 for ICU's data/rules.mk generation. Prefer Cygwin's +# python3, but fall back to any python3/python on PATH (e.g. the native Windows +# one the build front-loads) when Cygwin's isn't installed. ICU's databuilder +# normalizes path separators to '/', so native Python produces correct makefile +# fragments too. +if [ -x /usr/bin/python3 ]; then + export PYTHON=/usr/bin/python3 +elif command -v python3 >/dev/null 2>&1; then + export PYTHON="$( command -v python3 )" +elif command -v python >/dev/null 2>&1; then + export PYTHON="$( command -v python )" +else + echo "ERROR: no Python 3 found. Install Cygwin's python3, or ensure a native python3 is on PATH." >&2 + exit 1 +fi +echo "Using PYTHON=$PYTHON" + +# --- Make MSVC's link.exe win over Cygwin's /usr/bin/link (coreutils) -------- +# ICU's configure runs `link --version` and aborts with "link.exe is not a +# valid linker" if it reports "GNU coreutils". Cygwin ships such a `link`, and +# depending on the inherited PATH order it can shadow MSVC's linker. cl is +# already on PATH (CC=cl) and MSVC's link.exe lives in the SAME directory, so +# front-load that directory; falls back to VCToolsInstallDir if cl isn't found. +msvc_bin="" +cl_path="$( command -v cl 2>/dev/null || true )" +if [ -n "$cl_path" ]; then + msvc_bin="$( dirname "$cl_path" )" +elif [ -n "$VCToolsInstallDir" ]; then + msvc_bin="$( cygpath -u "$VCToolsInstallDir" )/bin/Hostx64/x64" +fi +if [ -n "$msvc_bin" ] && [ -x "$msvc_bin/link.exe" ]; then + export PATH="$msvc_bin:$PATH" + echo "Front-loaded MSVC bin so link.exe resolves to the MS linker: $msvc_bin" +else + echo "WARNING: could not locate MSVC bin dir; Cygwin's link may shadow link.exe" >&2 +fi abort_op() { diff --git a/Common/3dParty/icu-desktop/nc-build.bat b/Common/3dParty/icu-desktop/nc-build.bat index d9a03ba619..7da2be13eb 100644 --- a/Common/3dParty/icu-desktop/nc-build.bat +++ b/Common/3dParty/icu-desktop/nc-build.bat @@ -40,8 +40,22 @@ echo Using Cygwin: %CYGWIN_BIN% echo Using vcvars: %VCVARS% REM ---- PATH ORDER: MSVC first, Cygwin second ---- -set "PATH=%CYGWIN_BIN%;%PATH%" -call "%VCVARS%" || exit /b 1 +REM If the caller already loaded the MSVC environment (build.ps1 does this via +REM Import-VcVars, so VCINSTALLDIR/VSCMD/PATH/INCLUDE/LIB are inherited here), +REM DON'T re-run vcvars: it concatenates all MSVC/SDK dirs onto the already-long +REM inherited PATH again and blows past cmd's 8191-char line limit +REM ("Die eingegebene Zeile ist zu lang. / Syntaxfehler."), aborting the build. +if defined VCINSTALLDIR ( + echo MSVC environment already active ^(VCINSTALLDIR set^) - reusing it, skipping vcvars. + REM Cygwin is already on the inherited PATH; append (not prepend) so MSVC's + REM link.exe still precedes Cygwin's /usr/bin/link. Keeps cygpath reachable + REM even when the batch is run standalone from a VS dev prompt. + set "PATH=%PATH%;%CYGWIN_BIN%" +) else ( + REM Fresh shell: Cygwin first, then let vcvars prepend MSVC on top -> MSVC first. + set "PATH=%CYGWIN_BIN%;%PATH%" + call "%VCVARS%" || exit /b 1 +) echo "------------- DBG 1" diff --git a/Common/3dParty/icu/nc-build-cygwin.sh b/Common/3dParty/icu/nc-build-cygwin.sh index 4588e52482..421874ec60 100644 --- a/Common/3dParty/icu/nc-build-cygwin.sh +++ b/Common/3dParty/icu/nc-build-cygwin.sh @@ -6,7 +6,43 @@ if [[ $install_dir == /mnt/* ]]; then fi export PATH="$PATH:/usr/bin" -export PYTHON=/usr/bin/python3 + +# Pick a working Python 3 for ICU's data/rules.mk generation. Prefer Cygwin's +# python3, but fall back to any python3/python on PATH (e.g. the native Windows +# one the build front-loads) when Cygwin's isn't installed. ICU's databuilder +# normalizes path separators to '/', so native Python produces correct makefile +# fragments too. +if [ -x /usr/bin/python3 ]; then + export PYTHON=/usr/bin/python3 +elif command -v python3 >/dev/null 2>&1; then + export PYTHON="$( command -v python3 )" +elif command -v python >/dev/null 2>&1; then + export PYTHON="$( command -v python )" +else + echo "ERROR: no Python 3 found. Install Cygwin's python3, or ensure a native python3 is on PATH." >&2 + exit 1 +fi +echo "Using PYTHON=$PYTHON" + +# --- Make MSVC's link.exe win over Cygwin's /usr/bin/link (coreutils) -------- +# ICU's configure runs `link --version` and aborts with "link.exe is not a +# valid linker" if it reports "GNU coreutils". Cygwin ships such a `link`, and +# depending on the inherited PATH order it can shadow MSVC's linker. cl is +# already on PATH (CC=cl) and MSVC's link.exe lives in the SAME directory, so +# front-load that directory; falls back to VCToolsInstallDir if cl isn't found. +msvc_bin="" +cl_path="$( command -v cl 2>/dev/null || true )" +if [ -n "$cl_path" ]; then + msvc_bin="$( dirname "$cl_path" )" +elif [ -n "$VCToolsInstallDir" ]; then + msvc_bin="$( cygpath -u "$VCToolsInstallDir" )/bin/Hostx64/x64" +fi +if [ -n "$msvc_bin" ] && [ -x "$msvc_bin/link.exe" ]; then + export PATH="$msvc_bin:$PATH" + echo "Front-loaded MSVC bin so link.exe resolves to the MS linker: $msvc_bin" +else + echo "WARNING: could not locate MSVC bin dir; Cygwin's link may shadow link.exe" >&2 +fi # --- logging: write a clean UTF-8 log straight from bash --------------------- # Done before any heavy work so the real output never passes through the diff --git a/Common/3dParty/icu/nc-build.bat b/Common/3dParty/icu/nc-build.bat index 232620d765..44ab337db2 100644 --- a/Common/3dParty/icu/nc-build.bat +++ b/Common/3dParty/icu/nc-build.bat @@ -52,8 +52,22 @@ echo Using Cygwin: %CYGWIN_BIN% echo Using vcvars: %VCVARS% REM ---- PATH ORDER: MSVC first, Cygwin second ---- -set "PATH=%CYGWIN_BIN%;%PATH%" -call "%VCVARS%" || exit /b 1 +REM If the caller already loaded the MSVC environment (build.ps1 does this via +REM Import-VcVars, so VCINSTALLDIR/VSCMD/PATH/INCLUDE/LIB are inherited here), +REM DON'T re-run vcvars: it concatenates all MSVC/SDK dirs onto the already-long +REM inherited PATH again and blows past cmd's 8191-char line limit +REM ("Die eingegebene Zeile ist zu lang. / Syntaxfehler."), aborting the build. +if defined VCINSTALLDIR ( + echo MSVC environment already active ^(VCINSTALLDIR set^) - reusing it, skipping vcvars. + REM Cygwin is already on the inherited PATH; append (not prepend) so MSVC's + REM link.exe still precedes Cygwin's /usr/bin/link. Keeps cygpath reachable + REM even when the batch is run standalone from a VS dev prompt. + set "PATH=%PATH%;%CYGWIN_BIN%" +) else ( + REM Fresh shell: Cygwin first, then let vcvars prepend MSVC on top -> MSVC first. + set "PATH=%CYGWIN_BIN%;%PATH%" + call "%VCVARS%" || exit /b 1 +) echo "------------- DBG 1" diff --git a/Common/3dParty/v8/nc-build.py b/Common/3dParty/v8/nc-build.py index d0522dd486..c23e282d21 100644 --- a/Common/3dParty/v8/nc-build.py +++ b/Common/3dParty/v8/nc-build.py @@ -27,6 +27,28 @@ gn_source_path = nc.work_dir / "gn-source" +# Enable git long paths for EVERY git process started from here on - including +# the ones gclient spawns internally, which we can't pass -c flags to. +# GIT_CONFIG_COUNT/KEY/VALUE (git >= 2.31) outranks the system/global config, so +# this works without touching the user's ~/.gitconfig (which may not even exist - +# depot_tools then warns about it, harmlessly). +# +# Why it's needed: some v8 dependency paths exceed MAX_PATH, e.g. +# buildtools/third_party/libc++/trunk/test/std/thread/... = 264 chars. Without +# this git cannot write those files, reports "Filename too long", and the +# unwritten file shows up as a local deletion -> gclient aborts the sync with +# "You have uncommitted changes". The Windows-wide LongPathsEnabled registry +# flag does NOT cover this; git needs its own opt-in. +# +# Deliberately ONLY longpaths. depot_tools also recommends core.autocrlf=false, +# but do NOT set it here: an existing tree checked out with CRLF no longer +# matches the LF patches in tools/8.9/, and every `git apply` fails with +# "patch does not apply". +if nc.is_windows(): + os.environ[ "GIT_CONFIG_COUNT" ] = "1" + os.environ[ "GIT_CONFIG_KEY_0" ] = "core.longpaths" + os.environ[ "GIT_CONFIG_VALUE_0" ] = "true" + def check_prequisites(): tools_needed = [ "git", "python3" ] if nc.is_linux(): diff --git a/common.cmake b/common.cmake index 66a5a9fb2a..be41e0c14e 100644 --- a/common.cmake +++ b/common.cmake @@ -199,6 +199,17 @@ else() set(Boost_USE_STATIC_LIBS ON) find_package( Boost REQUIRED COMPONENTS system filesystem regex date_time ) + # Boost.Regex is header-only since Boost 1.77, so Boost::regex is now an + # INTERFACE target. For the COMPILED components (filesystem, ...) Boost's own + # CMake config sets BOOST__NO_LIB, which switches off the auto-link + # #pragma in the headers because CMake passes the .lib path itself. A + # header-only target gets no such define AND no library directory, yet + # boost/regex/v5/cregex.hpp still emits the auto-link pragma - so the linker + # demands libboost_regex-vcXXX-mt-x64-Y_ZZ.lib and fails with LNK1104 even + # though nothing needs to be linked. On Boost 1.78 regex was still a compiled + # component, hence the define came for free and this never surfaced. + add_definitions(-DBOOST_REGEX_NO_LIB) + # Setup v8 set(V8_INSTALL_DIR "${EO_CORE_3RD_PARTY_INSTALL_DIR}/v8") get_filename_component(V8_INSTALL_DIR_ABS "${V8_INSTALL_DIR}" ABSOLUTE)