272 lines
7.7 KiB
Bash
272 lines
7.7 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage:
|
|
HYPERTWIST_REMOTE_WINDOWS_PASSWORD=... \
|
|
scripts/run-hypertwist-remote-unreal-build.sh
|
|
|
|
Options:
|
|
--target <name>
|
|
Optional Unreal build target. Defaults to UnrealHyperTwistEditor.
|
|
|
|
--platform <name>
|
|
Optional Unreal platform. Defaults to Win64.
|
|
|
|
--configuration <name>
|
|
Optional Unreal configuration. Defaults to Development.
|
|
|
|
--worktree-root <path>
|
|
Optional Windows worktree root. Defaults to C:\HyperTwist_worktrees\phase10validate.
|
|
|
|
--project-path <path>
|
|
Optional Windows .uproject path. Defaults beneath the worktree root.
|
|
|
|
--build-batch-path <path>
|
|
Optional Build.bat path. Defaults to the UE 5.7 installed-engine path.
|
|
|
|
--max-parallel-actions <count>
|
|
Optional MaxParallelActions override. Defaults to 2 for the remote lane.
|
|
|
|
--allow-uba
|
|
Allow UBA instead of forcing the verified remote-lane -NoUBA posture.
|
|
|
|
--source-lock-retries <count>
|
|
Retry the remote build when UnrealBuildTool hits a transient Windows
|
|
source-file lock. Defaults to 1.
|
|
|
|
--source-lock-retry-delay-seconds <count>
|
|
Delay between transient source-lock retries. Defaults to 3 seconds.
|
|
|
|
--extra-arg <value>
|
|
Append an extra Build.bat argument. May be repeated.
|
|
|
|
--dry-run
|
|
Print the generated remote PowerShell payload instead of executing it.
|
|
|
|
Environment:
|
|
HYPERTWIST_REMOTE_WINDOWS_PASSWORD Required by the underlying tunnel wrapper.
|
|
HYPERTWIST_REMOTE_WINDOWS_WORKTREE_ROOT Optional default for --worktree-root.
|
|
HYPERTWIST_REMOTE_WINDOWS_PROJECT_PATH Optional default for --project-path.
|
|
HYPERTWIST_REMOTE_WINDOWS_BUILD_BAT Optional default for --build-batch-path.
|
|
HYPERTWIST_REMOTE_WINDOWS_MAX_PARALLEL_ACTIONS
|
|
Optional default for --max-parallel-actions.
|
|
HYPERTWIST_REMOTE_WINDOWS_SOURCE_LOCK_RETRIES
|
|
Optional default for --source-lock-retries.
|
|
HYPERTWIST_REMOTE_WINDOWS_SOURCE_LOCK_RETRY_DELAY_SECONDS
|
|
Optional default for --source-lock-retry-delay-seconds.
|
|
EOF
|
|
}
|
|
|
|
target_name="UnrealHyperTwistEditor"
|
|
platform_name="Win64"
|
|
configuration_name="Development"
|
|
worktree_root="${HYPERTWIST_REMOTE_WINDOWS_WORKTREE_ROOT:-C:\\HyperTwist_worktrees\\phase10validate}"
|
|
project_path=""
|
|
build_batch_path="${HYPERTWIST_REMOTE_WINDOWS_BUILD_BAT:-C:\\Program Files\\Epic Games\\UE_5.7\\Engine\\Build\\BatchFiles\\Build.bat}"
|
|
max_parallel_actions="${HYPERTWIST_REMOTE_WINDOWS_MAX_PARALLEL_ACTIONS:-2}"
|
|
source_lock_retries="${HYPERTWIST_REMOTE_WINDOWS_SOURCE_LOCK_RETRIES:-1}"
|
|
source_lock_retry_delay_seconds="${HYPERTWIST_REMOTE_WINDOWS_SOURCE_LOCK_RETRY_DELAY_SECONDS:-3}"
|
|
allow_uba="false"
|
|
dry_run="false"
|
|
extra_args=()
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--target)
|
|
target_name="${2:-}"
|
|
shift 2
|
|
;;
|
|
--platform)
|
|
platform_name="${2:-}"
|
|
shift 2
|
|
;;
|
|
--configuration)
|
|
configuration_name="${2:-}"
|
|
shift 2
|
|
;;
|
|
--worktree-root)
|
|
worktree_root="${2:-}"
|
|
shift 2
|
|
;;
|
|
--project-path)
|
|
project_path="${2:-}"
|
|
shift 2
|
|
;;
|
|
--build-batch-path)
|
|
build_batch_path="${2:-}"
|
|
shift 2
|
|
;;
|
|
--max-parallel-actions)
|
|
max_parallel_actions="${2:-}"
|
|
shift 2
|
|
;;
|
|
--allow-uba)
|
|
allow_uba="true"
|
|
shift
|
|
;;
|
|
--source-lock-retries)
|
|
source_lock_retries="${2:-}"
|
|
shift 2
|
|
;;
|
|
--source-lock-retry-delay-seconds)
|
|
source_lock_retry_delay_seconds="${2:-}"
|
|
shift 2
|
|
;;
|
|
--extra-arg)
|
|
extra_args+=("${2:-}")
|
|
shift 2
|
|
;;
|
|
--dry-run)
|
|
dry_run="true"
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
usage >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ -z "$project_path" ]]; then
|
|
project_path="${HYPERTWIST_REMOTE_WINDOWS_PROJECT_PATH:-${worktree_root}\\UnrealHyperTwist\\UnrealHyperTwist.uproject}"
|
|
fi
|
|
|
|
if [[ -z "$target_name" || -z "$platform_name" || -z "$configuration_name" || -z "$project_path" || -z "$build_batch_path" ]]; then
|
|
echo "Target, platform, configuration, project path, and Build.bat path must all be populated." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -n "$max_parallel_actions" && ! "$max_parallel_actions" =~ ^[0-9]+$ ]]; then
|
|
echo "--max-parallel-actions must be numeric." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -n "$source_lock_retries" && ! "$source_lock_retries" =~ ^[0-9]+$ ]]; then
|
|
echo "--source-lock-retries must be numeric." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -n "$source_lock_retry_delay_seconds" && ! "$source_lock_retry_delay_seconds" =~ ^[0-9]+$ ]]; then
|
|
echo "--source-lock-retry-delay-seconds must be numeric." >&2
|
|
exit 1
|
|
fi
|
|
|
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
remote_ps_wrapper="${script_dir}/run-hypertwist-remote-windows-powershell.sh"
|
|
|
|
if [[ ! -x "$remote_ps_wrapper" ]]; then
|
|
echo "Expected executable wrapper at $remote_ps_wrapper" >&2
|
|
exit 1
|
|
fi
|
|
|
|
powershell_payload="$(
|
|
python3 - <<'PY' \
|
|
"$build_batch_path" \
|
|
"$target_name" \
|
|
"$platform_name" \
|
|
"$configuration_name" \
|
|
"$project_path" \
|
|
"$max_parallel_actions" \
|
|
"$allow_uba" \
|
|
"${extra_args[@]}"
|
|
import sys
|
|
|
|
build_batch_path, target_name, platform_name, configuration_name, project_path, max_parallel_actions, allow_uba, *extra_args = sys.argv[1:]
|
|
|
|
def ps_single_quote(value: str) -> str:
|
|
return "'" + value.replace("'", "''") + "'"
|
|
|
|
lines = [
|
|
f"$BuildBatchPath = {ps_single_quote(build_batch_path)}",
|
|
f"$TargetName = {ps_single_quote(target_name)}",
|
|
f"$PlatformName = {ps_single_quote(platform_name)}",
|
|
f"$ConfigurationName = {ps_single_quote(configuration_name)}",
|
|
f"$ProjectPath = {ps_single_quote(project_path)}",
|
|
f"$MaxParallelActions = {max_parallel_actions}",
|
|
f"$AllowUba = ${'true' if allow_uba == 'true' else 'false'}",
|
|
"$ExtraArgs = @(",
|
|
]
|
|
|
|
for value in extra_args:
|
|
lines.append(f" {ps_single_quote(value)}")
|
|
|
|
lines.extend([
|
|
")",
|
|
"",
|
|
"if (-not (Test-Path -LiteralPath $BuildBatchPath)) {",
|
|
" throw \"Build.bat was not found at '$BuildBatchPath'.\"",
|
|
"}",
|
|
"",
|
|
"if (-not (Test-Path -LiteralPath $ProjectPath)) {",
|
|
" throw \"Project file was not found at '$ProjectPath'.\"",
|
|
"}",
|
|
"",
|
|
"$BuildArgs = @(",
|
|
" $TargetName",
|
|
" $PlatformName",
|
|
" $ConfigurationName",
|
|
" $ProjectPath",
|
|
" '-WaitMutex'",
|
|
" '-NoHotReloadFromIDE'",
|
|
")",
|
|
"",
|
|
"if (-not $AllowUba) {",
|
|
" $BuildArgs += '-NoUBA'",
|
|
"}",
|
|
"",
|
|
"if ($MaxParallelActions -gt 0) {",
|
|
" $BuildArgs += \"-MaxParallelActions=$MaxParallelActions\"",
|
|
"}",
|
|
"",
|
|
"if ($ExtraArgs.Count -gt 0) {",
|
|
" $BuildArgs += $ExtraArgs",
|
|
"}",
|
|
"",
|
|
"& $BuildBatchPath @BuildArgs",
|
|
"exit $LASTEXITCODE",
|
|
])
|
|
|
|
print("\n".join(lines))
|
|
PY
|
|
)"
|
|
|
|
if [[ "$dry_run" == "true" ]]; then
|
|
printf '%s\n' "$powershell_payload"
|
|
exit 0
|
|
fi
|
|
|
|
source_lock_marker="because it is being used by another process"
|
|
attempt=1
|
|
max_attempts=$((source_lock_retries + 1))
|
|
build_output_path="$(mktemp)"
|
|
trap 'rm -f "$build_output_path"' EXIT
|
|
|
|
while :; do
|
|
set +e
|
|
"$remote_ps_wrapper" --command "$powershell_payload" 2>&1 | tee "$build_output_path"
|
|
build_exit="${PIPESTATUS[0]}"
|
|
set -e
|
|
|
|
if [[ "$build_exit" -eq 0 ]]; then
|
|
exit 0
|
|
fi
|
|
|
|
if ! grep -Fq "$source_lock_marker" "$build_output_path"; then
|
|
exit "$build_exit"
|
|
fi
|
|
|
|
if (( attempt >= max_attempts )); then
|
|
echo "Remote Unreal build exhausted ${max_attempts} attempt(s) after transient source-file lock detection." >&2
|
|
exit "$build_exit"
|
|
fi
|
|
|
|
attempt=$((attempt + 1))
|
|
echo "Retrying remote Unreal build after transient source-file lock (${attempt}/${max_attempts}) in ${source_lock_retry_delay_seconds}s..." >&2
|
|
sleep "$source_lock_retry_delay_seconds"
|
|
done
|