<# .SYNOPSIS j3270 Terminal Emulator - Setup and Build Script (PowerShell) Detects Java 11+ JDK and builds j3270 standalone executable JAR. .DESCRIPTION Searches for a compatible Java Development Kit (JDK 11+ / LTS 11, 17, 21), verifies compiler (javac) and archiver (jar) toolchains, compiles the lib3270j protocol core and j3270 Swing desktop application, and packages the standalone executable JAR file (build/j3270.jar). .PARAMETER Run Launch j3270 immediately after successful build. .PARAMETER Test Run unit test suite after successful build. .PARAMETER Clean Remove previous build artifacts before compiling. .PARAMETER Help Display usage help message. .EXAMPLE .\setup.ps1 Builds the standalone j3270.jar. .EXAMPLE .\setup.ps1 -Clean -Run Cleans previous build, builds j3270, and immediately launches the application. .EXAMPLE .\setup.ps1 -Run -- mainframe.example.com 23 4 Builds and connects to specified host, port, and terminal model. #> [CmdletBinding()] param ( [switch]$Run, [switch]$Test, [switch]$Clean, [switch]$Help, [Parameter(ValueFromRemainingArguments = $true)] [string[]]$RunArgs ) $ErrorActionPreference = "Stop" # Resolve script root directory $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path if (-not $ScriptDir) { $ScriptDir = Get-Location } Set-Location $ScriptDir # Helper functions for colored terminal output function Write-LogInfo($msg) { Write-Host "[INFO] $msg" -ForegroundColor Cyan } function Write-LogSuccess($msg) { Write-Host "[OK] $msg" -ForegroundColor Green } function Write-LogWarn($msg) { Write-Host "[WARN] $msg" -ForegroundColor Yellow } function Write-LogError($msg) { Write-Host "[ERROR] $msg" -ForegroundColor Red } if ($Help) { Write-Host @" j3270 Terminal Emulator - Setup & Build Script (PowerShell) Usage: .\setup.ps1 [-Run] [-Test] [-Clean] [-Help] [-- ] Options: -Run Build and immediately launch j3270 -Test Run automated unit tests after build -Clean Remove previous build artifacts before compiling -Help Show this help message Examples: .\setup.ps1 .\setup.ps1 -Clean .\setup.ps1 -Run .\setup.ps1 -Run -- mainframe.example.com 23 4 .\setup.ps1 -Test "@ exit 0 } Write-Host "`n=== j3270 Setup & Build (PowerShell) ===`n" -ForegroundColor White # Determine platform $isWindows = if (Test-Path variable:IsWindows) { $IsWindows } else { $env:OS -eq "Windows_NT" } $exeSuffix = if ($isWindows) { ".exe" } else { "" } # ------------------------------------------------------------------------------ # 1. Helper: Parse Major Java Version # ------------------------------------------------------------------------------ function Get-JavaMajorVersion([string]$verStr) { if (-not $verStr) { return 0 } if ($verStr -match 'javac\s+([0-9][0-9._]*)' -or $verStr -match 'version\s+"?([0-9][0-9._]*)"?' -or $verStr -match '([0-9]+(\.[0-9]+)*)') { $token = $matches[1] if ($token -match '^1\.(\d+)') { return [int]$matches[1] } if ($token -match '^(\d+)') { return [int]$matches[1] } } return 0 } # ------------------------------------------------------------------------------ # 2. Helper: Test JDK Candidate Directory # ------------------------------------------------------------------------------ function Test-JdkCandidate([string]$candHome) { if (-not $candHome -or -not (Test-Path $candHome)) { return $null } $javacPath = Join-Path $candHome "bin\javac$exeSuffix" $jarPath = Join-Path $candHome "bin\jar$exeSuffix" $javaPath = Join-Path $candHome "bin\java$exeSuffix" if (-not (Test-Path $javacPath) -or -not (Test-Path $jarPath) -or -not (Test-Path $javaPath)) { return $null } try { $pInfo = New-Object System.Diagnostics.ProcessStartInfo $pInfo.FileName = $javacPath $pInfo.Arguments = "-version" $pInfo.RedirectStandardOutput = $true $pInfo.RedirectStandardError = $true $pInfo.UseShellExecute = $false $pInfo.CreateNoWindow = $true $proc = [System.Diagnostics.Process]::Start($pInfo) $stdout = $proc.StandardOutput.ReadToEnd() $stderr = $proc.StandardError.ReadToEnd() $proc.WaitForExit() if ($proc.ExitCode -eq 0) { $verOutput = ("$stdout $stderr").Trim() $majorVer = Get-JavaMajorVersion $verOutput if ($majorVer -ge 11) { return [PSCustomObject]@{ Home = $candHome Javac = $javacPath Jar = $jarPath Java = $javaPath Version = $verOutput Major = $majorVer } } } } catch { # Process execution failed or access denied } return $null } # ------------------------------------------------------------------------------ # 3. Discover JDK 11+ # ------------------------------------------------------------------------------ Write-LogInfo "Searching for a compatible Java Development Kit (JDK 11+)..." $detectedJdk = $null # Strategy A: Check JAVA_HOME environment variable if ($env:JAVA_HOME) { $detectedJdk = Test-JdkCandidate $env:JAVA_HOME if ($detectedJdk) { Write-LogInfo "Found valid JDK in `$env:JAVA_HOME: $($detectedJdk.Home)" } else { Write-LogWarn "`$env:JAVA_HOME is set ($($env:JAVA_HOME)) but is not a valid JDK 11+." } } # Strategy B: Check current system PATH if (-not $detectedJdk) { $pathJavac = Get-Command "javac$exeSuffix" -ErrorAction SilentlyContinue if ($pathJavac) { try { $realPath = $pathJavac.Source $candBin = Split-Path -Parent $realPath $candHome = Split-Path -Parent $candBin $detectedJdk = Test-JdkCandidate $candHome if ($detectedJdk) { Write-LogInfo "Found valid JDK on PATH: $($detectedJdk.Home)" } } catch {} } } # Strategy C: Check Windows Registry (if running on Windows) if (-not $detectedJdk -and $isWindows) { $regPaths = @( "HKLM:\SOFTWARE\JavaSoft\JDK", "HKLM:\SOFTWARE\JavaSoft\Java Development Kit", "HKLM:\SOFTWARE\Eclipse Adoptium\JDK", "HKLM:\SOFTWARE\AdoptOpenJDK\JDK", "HKLM:\SOFTWARE\Microsoft\JDK", "HKLM:\SOFTWARE\BellSoft\Liberica JDK", "HKLM:\SOFTWARE\Zulu\zulu-jdk", "HKLM:\SOFTWARE\WOW6432Node\JavaSoft\JDK", "HKLM:\SOFTWARE\WOW6432Node\JavaSoft\Java Development Kit", "HKLM:\SOFTWARE\WOW6432Node\Eclipse Adoptium\JDK", "HKLM:\SOFTWARE\WOW6432Node\Microsoft\JDK" ) foreach ($regBase in $regPaths) { if (Test-Path $regBase) { $subKeys = Get-ChildItem -Path $regBase -ErrorAction SilentlyContinue foreach ($key in $subKeys) { $item = Get-ItemProperty -Path $key.PSPath -ErrorAction SilentlyContinue if ($item.JavaHome) { $detectedJdk = Test-JdkCandidate $item.JavaHome if ($detectedJdk) { break } } if ($item.Path) { $detectedJdk = Test-JdkCandidate $item.Path if ($detectedJdk) { break } } # Check hotspot subkey used by Adoptium/Microsoft MSI installers $hotspotKey = Join-Path $key.PSPath "hotspot\MSI" if (Test-Path $hotspotKey) { $hs = Get-ItemProperty -Path $hotspotKey -ErrorAction SilentlyContinue if ($hs.Path) { $detectedJdk = Test-JdkCandidate $hs.Path if ($detectedJdk) { break } } } } if ($detectedJdk) { Write-LogInfo "Found valid JDK via Windows Registry: $($detectedJdk.Home)" break } } } } # Strategy D: Check Standard Filesystem Locations if (-not $detectedJdk) { $fsCandidates = @() if ($isWindows) { $searchRoots = @( "$env:ProgramFiles\Eclipse Adoptium\*", "$env:ProgramFiles\Java\*", "$env:ProgramFiles\Microsoft\*", "$env:ProgramFiles\Amazon Corretto\*", "$env:ProgramFiles\Zulu\*", "$env:ProgramFiles\BellSoft\*", "$env:ProgramFiles\Semeru\*", "$env:ProgramFiles\RedHat\*", "${env:ProgramFiles(x86)}\Java\*", "$env:LOCALAPPDATA\Programs\Eclipse Adoptium\*", "$env:LOCALAPPDATA\Programs\Common\Oracle\Java\*", "$env:USERPROFILE\.jdks\*", "$env:USERPROFILE\.sdkman\candidates\java\*", "$env:USERPROFILE\scoop\apps\openjdk\current", "$env:USERPROFILE\scoop\apps\oraclejdk\current", "$env:USERPROFILE\scoop\apps\temurin\current", "$env:USERPROFILE\scoop\apps\zulu\current", "C:\tools\jdk*", "C:\Java\*" ) foreach ($pattern in $searchRoots) { Resolve-Path -Path $pattern -ErrorAction SilentlyContinue | ForEach-Object { $fsCandidates += $_.Path } } } else { # POSIX / macOS search roots for pwsh $searchRoots = @( "/opt/homebrew/opt/openjdk/libexec/openjdk.jdk/Contents/Home", "/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home", "/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home", "/opt/homebrew/opt/openjdk@11/libexec/openjdk.jdk/Contents/Home", "/opt/homebrew/opt/openjdk*", "/usr/local/opt/openjdk/libexec/openjdk.jdk/Contents/Home", "/Library/Java/JavaVirtualMachines/*/Contents/Home", "/usr/lib/jvm/default-java", "/usr/lib/jvm/java-21-openjdk*", "/usr/lib/jvm/java-17-openjdk*", "/usr/lib/jvm/java-11-openjdk*", "/usr/lib/jvm/*", "$env:HOME/.sdkman/candidates/java/current", "$env:HOME/.sdkman/candidates/java/*", "$env:HOME/.asdf/installs/java/*" ) foreach ($pattern in $searchRoots) { Resolve-Path -Path $pattern -ErrorAction SilentlyContinue | ForEach-Object { $fsCandidates += $_.Path } } } foreach ($cand in $fsCandidates) { $detectedJdk = Test-JdkCandidate $cand if ($detectedJdk) { Write-LogInfo "Found valid JDK at: $($detectedJdk.Home)" break } } } # If no compatible JDK is found, report error with helpful instructions if (-not $detectedJdk) { Write-LogError "No compatible Java Development Kit (JDK 11+) was found on your system." Write-Host @" j3270 requires a Java Development Kit (JDK) version 11 or higher (LTS 11, 17, or 21). Note: A JRE (runtime only) is not sufficient; the compiler (javac) and archiver (jar) are required. To install JDK 17 on Windows: • Using Windows Package Manager (winget): winget install EclipseAdoptium.Temurin.17.JDK • Using Chocolatey: choco install openjdk17 • Using Scoop: scoop install openjdk17 • Or download Eclipse Temurin (Adoptium MSI installer): https://adoptium.net After installing, restart PowerShell and re-run .\setup.ps1 "@ -ForegroundColor Yellow exit 1 } $env:JAVA_HOME = $detectedJdk.Home Write-LogSuccess "JDK verified: $($detectedJdk.Version)" Write-LogInfo "Java compiler: $($detectedJdk.Javac)" Write-LogInfo "JAR packager: $($detectedJdk.Jar)" Write-LogInfo "Java runtime: $($detectedJdk.Java)" Write-LogInfo "JAVA_HOME: $($detectedJdk.Home)" Write-Host "" # ------------------------------------------------------------------------------ # 4. Preparation & Clean # ------------------------------------------------------------------------------ $BuildDir = Join-Path $ScriptDir "build" $LibBuildDir = Join-Path $BuildDir "lib3270j" $AppBuildDir = Join-Path $BuildDir "j3270" if ($Clean) { Write-LogInfo "Cleaning build directory ($BuildDir)..." Remove-Item -Path $LibBuildDir, $AppBuildDir -Recurse -Force -ErrorAction SilentlyContinue Remove-Item -Path (Join-Path $BuildDir "MANIFEST.MF") -Force -ErrorAction SilentlyContinue Remove-Item -Path (Join-Path $BuildDir "j3270.jar") -Force -ErrorAction SilentlyContinue } $null = New-Item -ItemType Directory -Path $LibBuildDir -Force $null = New-Item -ItemType Directory -Path $AppBuildDir -Force # ------------------------------------------------------------------------------ # 5. Compile Core Protocol Engine (lib3270j) # ------------------------------------------------------------------------------ Write-LogInfo "Compiling lib3270j core engine..." $libSrcDir = Join-Path $ScriptDir "lib3270j\src\main\java" $libSources = Get-ChildItem -Path $libSrcDir -Filter *.java -Recurse | Select-Object -ExpandProperty FullName if (-not $libSources -or $libSources.Count -eq 0) { Write-LogError "No source files found in $libSrcDir" exit 1 } $libSourcesFile = Join-Path $BuildDir "lib_sources.txt" $libSources | ForEach-Object { "`"$_`"" } | Set-Content -Path $libSourcesFile -Encoding UTF8 & $detectedJdk.Javac -d $LibBuildDir "@$libSourcesFile" if ($LASTEXITCODE -ne 0) { Write-LogError "Compilation of lib3270j failed with exit code $LASTEXITCODE." exit $LASTEXITCODE } Remove-Item -Path $libSourcesFile -Force -ErrorAction SilentlyContinue Write-LogSuccess "lib3270j compiled successfully." # ------------------------------------------------------------------------------ # 6. Compile Desktop Application (j3270) # ------------------------------------------------------------------------------ Write-LogInfo "Compiling j3270 terminal emulator..." $appSrcDir = Join-Path $ScriptDir "j3270\src\main\java" $appSources = Get-ChildItem -Path $appSrcDir -Filter *.java -Recurse | Select-Object -ExpandProperty FullName if (-not $appSources -or $appSources.Count -eq 0) { Write-LogError "No source files found in $appSrcDir" exit 1 } $appSourcesFile = Join-Path $BuildDir "app_sources.txt" $appSources | ForEach-Object { "`"$_`"" } | Set-Content -Path $appSourcesFile -Encoding UTF8 & $detectedJdk.Javac -cp $LibBuildDir -d $AppBuildDir "@$appSourcesFile" if ($LASTEXITCODE -ne 0) { Write-LogError "Compilation of j3270 failed with exit code $LASTEXITCODE." exit $LASTEXITCODE } Remove-Item -Path $appSourcesFile -Force -ErrorAction SilentlyContinue Write-LogSuccess "j3270 application compiled successfully." # Copy any resources if present $appResDir = Join-Path $ScriptDir "j3270\src\main\resources" if (Test-Path $appResDir) { Copy-Item -Path (Join-Path $appResDir "*") -Destination $AppBuildDir -Recurse -Force -ErrorAction SilentlyContinue } $libResDir = Join-Path $ScriptDir "lib3270j\src\main\resources" if (Test-Path $libResDir) { Copy-Item -Path (Join-Path $libResDir "*") -Destination $LibBuildDir -Recurse -Force -ErrorAction SilentlyContinue } # ------------------------------------------------------------------------------ # 7. Package Standalone Executable JAR # ------------------------------------------------------------------------------ Write-LogInfo "Packaging standalone executable JAR..." $manifestFile = Join-Path $BuildDir "MANIFEST.MF" $manifestText = "Manifest-Version: 1.0`r`nMain-Class: haus.nightmare.j3270.J3270App`r`nImplementation-Title: j3270`r`nImplementation-Version: 0.1.0`r`nCreated-By: j3270 setup.ps1`r`n`r`n" [System.IO.File]::WriteAllText($manifestFile, $manifestText) $jarFile = Join-Path $BuildDir "j3270.jar" & $detectedJdk.Jar cvfm $jarFile $manifestFile -C $LibBuildDir . -C $AppBuildDir . | Out-Null if (-not (Test-Path $jarFile)) { Write-LogError "Failed to create executable JAR at $jarFile" exit 1 } $jarItem = Get-Item $jarFile $jarSizeKB = [math]::Round($jarItem.Length / 1KB, 1) Write-Host "`n=== Build Successful ===`n" -ForegroundColor Green Write-LogSuccess "Executable JAR created: $jarFile (${jarSizeKB} KB)" Write-Host @" To run j3270: java -jar "$jarFile" # Or with arguments: java -jar "$jarFile" mainframe.example.com 23 4 "@ -ForegroundColor White # ------------------------------------------------------------------------------ # 8. Run Tests (if requested) # ------------------------------------------------------------------------------ if ($Test) { Write-Host "`n=== Running Unit Tests ===`n" -ForegroundColor White $testScript = Join-Path $ScriptDir "test_all.sh" if (Test-Path $testScript) { & sh $testScript } else { Write-LogWarn "No test runner script found at $testScript" } } # ------------------------------------------------------------------------------ # 9. Launch Application (if requested) # ------------------------------------------------------------------------------ if ($Run) { Write-LogInfo "Launching j3270..." if ($isWindows) { Start-Process -FilePath $detectedJdk.Java -ArgumentList (@("-jar", "`"$jarFile`"") + $RunArgs) } else { & $detectedJdk.Java -jar $jarFile @RunArgs } }