makepad/tools/run/entry/makepad.bat

204 lines
12 KiB
Batchfile

@echo off
rem Makepad for Windows: gets Rust, compiles Makepad's loader (makepad run) from its source on
rem this computer and starts it. Read it before you run it; it is this one file. The same steps as
rem makepad.sh (macOS, Linux):
rem
rem 1. Rust 1.98.0 for x86_64-pc-windows-gnu (rustc, cargo, rust-std, rust-mingw: its own linker,
rem no Visual Studio) from static.rust-lang.org, checked against the sizes and SHA-256 below.
rem 2. The loader's source: the release service's licence-free loader release, a ZIP whose size
rem and SHA-256 the service names (checked before unpacking).
rem 3. `cargo build --profile host --offline -p makepad-run --bin makepad` for this computer's
rem processor (the ZIP holds every crate; a new loader release replaces only the files that
rem changed, so only their crates compile again), then `makepad` with no arguments: the runner,
rem from its launch record (apps\runner.launch, the release service's latest `runner` release),
rem which downloads the rest. With arguments, the loader runs with them instead.
rem
rem In its folder (this file's own when it holds nothing else, else %USERPROFILE%\makepad-runner) it
rem creates rust\, cache\, apps\ and runner\, the same folders as makepad.sh.
rem Run it again at any time: nothing is downloaded or compiled twice. It uses only Windows' own
rem curl.exe, tar.exe, certutil.exe and PowerShell (to read the service's JSON answers).
setlocal EnableExtensions EnableDelayedExpansion
rem The folder: this file's own when it is an installation already or holds nothing else (a folder
rem made for it); else %USERPROFILE%\makepad-runner (a download among other files), where it copies itself.
set "ROOT=%~dp0"
set "ROOT=%ROOT:~0,-1%"
if not exist "%ROOT%\cache" (
for /f "delims=" %%F in ('dir /b /a "%ROOT%" 2^>nul') do if /i not "%%F"=="%~nx0" if /i not "%%F"=="desktop.ini" set "ROOT=%USERPROFILE%\makepad-runner"
)
if defined MAKEPAD_LOADER_ROOT set "ROOT=%MAKEPAD_LOADER_ROOT%"
if not exist "%ROOT%" mkdir "%ROOT%" || (echo Cannot create the folder %ROOT%. & exit /b 1)
if /i not "%~dp0"=="%ROOT%\" copy /y "%~f0" "%ROOT%\makepad.bat" >nul
set "RUST_VERSION=1.98.0"
set "TRIPLE=x86_64-pc-windows-gnu"
set "DIST=https://static.rust-lang.org/dist"
set "RUST=%ROOT%\rust"
set "CACHE=%ROOT%\cache"
set "DOWNLOADS=%CACHE%\downloads"
set "LOGS=%CACHE%\logs"
set "PARTS=rustc cargo rust-std rust-mingw"
set "SIZE_rustc=158249903"
set "SIZE_cargo=18429051"
set "SIZE_rust-std=43334608"
set "SIZE_rust-mingw=9594885"
set "SHA_rustc=c97a98af7a8c84cee611a9a8a5bba6a804f82b09899a2e451a33b5934d684542"
set "SHA_cargo=79da7e6bb0c7ac1583b380c702477da298f2dace9ba22f41150698c516533406"
set "SHA_rust-std=47bceabaceabc4de97e20f7470d9973fb20e844986da01b3a31cf28e24596660"
set "SHA_rust-mingw=14fc86abb02d7d0575eb38a738161dcd87aa4032e091cce3424e78321b145e5e"
set "SERVICE=https://makepad.nl/api/loader"
if defined MAKEPAD_LOADER_SERVICE set "SERVICE=%MAKEPAD_LOADER_SERVICE%"
title Makepad
chcp 65001 >nul
where curl.exe >nul 2>nul || (echo Makepad needs curl.exe: Windows 10 version 1803 or later. & exit /b 1)
where tar.exe >nul 2>nul || (echo Makepad needs tar.exe: Windows 10 version 1803 or later. & exit /b 1)
for %%D in ("%DOWNLOADS%" "%LOGS%" "%CACHE%\loader" "%CACHE%\runner" "%ROOT%\apps") do if not exist "%%~D" mkdir "%%~D" || exit /b 1
set "MAKEPAD_RUN_HOME=%ROOT%"
rem --- 1. Rust ------------------------------------------------------------------------------------
set "NEED_RUST="
if not exist "%RUST%\.toolchain-version" set "NEED_RUST=1"
if not exist "%RUST%\bin\cargo.exe" set "NEED_RUST=1"
if not defined NEED_RUST goto :have_rust
echo Rust %RUST_VERSION%: downloading, once, into this folder
set "CFG=%DOWNLOADS%\rust.curl"
del "%CFG%" 2>nul
for %%P in (%PARTS%) do (
set "FILE=%DOWNLOADS%\%%P-%RUST_VERSION%-%TRIPLE%.tar.gz"
set "HAVE=0"
if exist "!FILE!" for %%S in ("!FILE!") do set "HAVE=%%~zS"
if not "!HAVE!"=="!SIZE_%%P!" (
del "!FILE!" 2>nul
>>"%CFG%" echo url = "%DIST%/%%P-%RUST_VERSION%-%TRIPLE%.tar.gz"
>>"%CFG%" echo output = "!FILE:\=/!"
>>"%CFG%" echo proto = =https
>>"%CFG%" echo fail
>>"%CFG%" echo retry = 3
)
)
if exist "%CFG%" (
curl.exe -sS -Z --parallel-immediate -K "%CFG%" 2>"%LOGS%\download.log" || (echo The download stopped. Run makepad.bat again to continue it. & exit /b 1)
del "%CFG%"
)
for %%P in (%PARTS%) do (
call :verify "%DOWNLOADS%\%%P-%RUST_VERSION%-%TRIPLE%.tar.gz" "!SHA_%%P!" || (del "%DOWNLOADS%\%%P-%RUST_VERSION%-%TRIPLE%.tar.gz" & echo Checksum mismatch for %%P: downloaded again on the next run. & exit /b 1)
)
set "STAGE=%RUST%.unpack"
if exist "%STAGE%" rmdir /s /q "%STAGE%"
mkdir "%STAGE%\ready" || exit /b 1
for %%P in (%PARTS%) do (
mkdir "%STAGE%\%%P" || exit /b 1
tar.exe -xzf "%DOWNLOADS%\%%P-%RUST_VERSION%-%TRIPLE%.tar.gz" --strip-components=2 -C "%STAGE%\%%P" || (echo Unpacking %%P failed. & exit /b 1)
robocopy "%STAGE%\%%P" "%STAGE%\ready" /e /move /njh /njs /nfl /ndl /np >nul
if errorlevel 8 (echo Unpacking %%P failed. & exit /b 1)
)
<nul set /p "=%RUST_VERSION% %TRIPLE%" > "%STAGE%\ready\.toolchain-version"
if exist "%RUST%" rmdir /s /q "%RUST%"
move "%STAGE%\ready" "%RUST%" >nul || exit /b 1
rmdir /s /q "%STAGE%"
for %%P in (%PARTS%) do del "%DOWNLOADS%\%%P-%RUST_VERSION%-%TRIPLE%.tar.gz" 2>nul
:have_rust
rem --- 2. The loader's source: the service's latest loader release (offline: the one built before)
set "LATEST=%CACHE%\loader-latest.json"
del "%LATEST%" 2>nul
curl.exe -fsS --proto =https -m 60 "%SERVICE%/public/app/relax/latest" -o "%LATEST%" 2>>"%LOGS%\download.log"
set "VERSION=" & set "ZIPNAME=" & set "ZIPBYTES=" & set "ZIPSHA="
if exist "%LATEST%" for /f "tokens=1-4" %%A in ('powershell -NoProfile -Command "$j = Get-Content -Raw '%LATEST%' | ConvertFrom-Json; '{0} {1} {2} {3}' -f $j.release, $j.archive.name, $j.archive.bytes, $j.archive.sha256"') do (
set "VERSION=%%A" & set "ZIPNAME=%%B" & set "ZIPBYTES=%%C" & set "ZIPSHA=%%D"
)
rem One source folder for every version (target\ inside it: makepad finds its std two levels up).
set "SRC=%CACHE%\loader\src"
if not defined VERSION if exist "%SRC%\.version" set /p VERSION=<"%SRC%\.version"
if not defined VERSION (echo Could not reach the release service ^(%SERVICE%^) and no loader is here yet. Check the connection, then run this again. & exit /b 1)
set "MAKEPAD=%SRC%\target\host\makepad.exe"
set "BUILT=%VERSION% %RUST_VERSION% %TRIPLE%"
set "HAVE_BUILT="
if exist "%SRC%\.built" set /p HAVE_BUILT=<"%SRC%\.built"
if exist "%MAKEPAD%" if "%HAVE_BUILT%"=="%BUILT%" goto :have_loader
set "HAVE_VERSION="
if exist "%SRC%\.version" set /p HAVE_VERSION=<"%SRC%\.version"
if not "%HAVE_VERSION%"=="%VERSION%" (
if not defined ZIPNAME (echo The release service did not answer; the loader's source is not here yet. Check the connection, then run this again. & exit /b 1)
echo Makepad's loader: downloading its source
curl.exe -fsS --proto =https --retry 3 "%SERVICE%/public/app/relax/%VERSION%/%ZIPNAME%" -o "%DOWNLOADS%\%ZIPNAME%" 2>>"%LOGS%\download.log" || (echo The download stopped. & exit /b 1)
for %%S in ("%DOWNLOADS%\%ZIPNAME%") do if not "%%~zS"=="%ZIPBYTES%" (del "%DOWNLOADS%\%ZIPNAME%" & echo The loader's source has the wrong size. & exit /b 1)
call :verify "%DOWNLOADS%\%ZIPNAME%" "%ZIPSHA%" || (del "%DOWNLOADS%\%ZIPNAME%" & echo Checksum mismatch for %ZIPNAME%. & exit /b 1)
if exist "%CACHE%\loader\.unpack" rmdir /s /q "%CACHE%\loader\.unpack"
mkdir "%CACHE%\loader\.unpack" || exit /b 1
tar.exe -xf "%DOWNLOADS%\%ZIPNAME%" -C "%CACHE%\loader\.unpack" || (echo Unpacking the loader's source failed. Run this again. & exit /b 1)
if not exist "%SRC%\Cargo.toml" (
if exist "%SRC%" rmdir /s /q "%SRC%"
move "%CACHE%\loader\.unpack\%VERSION%" "%SRC%" >nul || exit /b 1
) else (
rem an update: changed files replaced (with the time of now, so cargo sees them as changed),
rem files the new version no longer has removed, the rest (and target\) left as they are
echo Makepad's loader: updating its source to %VERSION%
powershell -NoProfile -Command ^
"$new = '%CACHE%\loader\.unpack\%VERSION%'; $src = '%SRC%'; $now = Get-Date; $keep = @{};" ^
"foreach ($f in Get-ChildItem -LiteralPath $new -Recurse -File) { $rel = $f.FullName.Substring($new.Length + 1); $keep[$rel] = 1; $dst = Join-Path $src $rel;" ^
" if (-not (Test-Path -LiteralPath $dst) -or (Get-FileHash -LiteralPath $f.FullName).Hash -ne (Get-FileHash -LiteralPath $dst).Hash) {" ^
" New-Item -ItemType Directory -Force -Path (Split-Path $dst) | Out-Null; Move-Item -Force -LiteralPath $f.FullName -Destination $dst; (Get-Item -LiteralPath $dst).LastWriteTime = $now } };" ^
"foreach ($f in Get-ChildItem -LiteralPath $src -Recurse -File) { $rel = $f.FullName.Substring($src.Length + 1);" ^
" if (-not $rel.StartsWith('target\') -and $rel -ne '.version' -and $rel -ne '.built' -and -not $keep.ContainsKey($rel)) { Remove-Item -Force -LiteralPath $f.FullName } }" ^
|| (echo Could not update the loader's source in %SRC%. & exit /b 1)
)
>"%SRC%\.version" echo %VERSION%
rmdir /s /q "%CACHE%\loader\.unpack"
del "%DOWNLOADS%\%ZIPNAME%"
)
rem --- 3. Compile the loader: no Rust settings from elsewhere on this computer; output in cache\
echo Makepad's loader: compiling for this computer, once per version
set "PATH=%RUST%\bin;%SystemRoot%\System32;%SystemRoot%;%SystemRoot%\System32\WindowsPowerShell\v1.0"
set "CARGO_HOME=%CACHE%\cargo-home"
set "CARGO_INCREMENTAL=0"
for %%V in (RUSTUP_TOOLCHAIN RUSTFLAGS CARGO_ENCODED_RUSTFLAGS CARGO_BUILD_TARGET CARGO_BUILD_RUSTFLAGS CARGO_TARGET_DIR RUSTC_WRAPPER RUSTDOC) do set "%%V="
rem [profile.host]: thin LTO, 16 codegen units; target-cpu=native for this computer's processor
set "RUSTFLAGS=-Ctarget-cpu=native"
set "RUSTC=%RUST%\bin\rustc.exe"
pushd "%SRC%"
cargo.exe build --profile host --offline -p makepad-run --bin makepad >"%LOGS%\build.log" 2>&1
set "BUILD=%errorlevel%"
popd
set "RUSTFLAGS="
if not "%BUILD%"=="0" (echo Makepad's loader did not compile. Run this again; if it stops the same way, the log says why: %LOGS%\build.log & exit /b 1)
>"%SRC%\.built" echo %BUILT%
:have_loader
set "PATH=%RUST%\bin;%PATH%"
set "MAKEPAD_LOADER_ROOT=%ROOT%"
set "MAKEPAD_LOADER_SERVICE=%SERVICE%"
if not "%~1"=="" (
"%MAKEPAD%" %*
exit /b !errorlevel!
)
rem --- 4. The runner's launch record, then `makepad` alone runs it (makepad launch.rs) ------------
rem The service's latest licence-free `runner` release: its repositories on the service's read-only
rem git face, the one at `makepad` the workspace, the others mounted at their folders.
set "RUNNER=%CACHE%\runner\latest.json"
del "%RUNNER%" 2>nul
curl.exe -fsS --proto =https -m 60 "%SERVICE%/public/app/runner/latest" -o "%RUNNER%" 2>>"%LOGS%\download.log"
if exist "%RUNNER%" (
powershell -NoProfile -Command ^
"$j = Get-Content -Raw '%RUNNER%' | ConvertFrom-Json; $g = '%SERVICE%/public/git/runner'; $r = $j.release;" ^
"$l = @('title`tMakepad', 'arg`trun', 'arg`t--quiet', 'arg`t--cached'); $m = @();" ^
"foreach ($p in $j.repositories) { $u = '{0}/{1}.git#{2}@{3}' -f $g, $p.name, $r, $p.commit;" ^
" if ($p.path -eq 'makepad') { $l += ('arg`t' + $u) } elseif ($p.path.StartsWith('makepad/')) { $m += 'arg`t--mount'; $m += ('arg`t' + $p.path.Substring(8) + '=' + $u) } };" ^
"$l += $m; $l += 'arg`tmakepad-runner';" ^
"[IO.File]::WriteAllText('%ROOT%\apps\runner.launch.part', (($l | ForEach-Object { $_ -replace '`t', [char]9 }) -join [char]10) + [char]10)" ^
|| (echo The runner release could not be read. & exit /b 1)
move /y "%ROOT%\apps\runner.launch.part" "%ROOT%\apps\runner.launch" >nul || exit /b 1
)
if not exist "%ROOT%\apps\runner.launch" (echo Could not reach the release service ^(%SERVICE%^) and the runner was never set up. & exit /b 1)
echo Makepad: starting
"%MAKEPAD%"
exit /b %errorlevel%
rem :verify FILE SHA256 checks a file's SHA-256 (certutil's answer, spaces removed, any case).
:verify
set "GOT="
for /f "skip=1 tokens=*" %%L in ('certutil.exe -hashfile "%~1" SHA256') do if not defined GOT set "GOT=%%L"
set "GOT=%GOT: =%"
if /i "%GOT%"=="%~2" exit /b 0
exit /b 1