build.ps1 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. Param(
  2. [Parameter(Mandatory, HelpMessage="Please provide a name for the app")][string] $AppName,
  3. [Parameter(Mandatory, HelpMessage="Please provide the glob pattern to identify the main jar")][string] $MainJarGlob,
  4. [Parameter(Mandatory, HelpMessage="Please provide the module- and main class path to start the app")][string] $ModuleAndMainClass,
  5. [Parameter(Mandatory, HelpMessage="Please provide the windows upgrade uuid for the installer")][string] $UpgradeUUID,
  6. [Parameter(Mandatory, HelpMessage="Please provide the name of the vendor")][string] $Vendor,
  7. [Parameter(Mandatory, HelpMessage="Please provide the starting year for the copyright notice")][int] $CopyrightStartYear,
  8. [Parameter(Mandatory, HelpMessage="Please provide a help url")][string] $HelpUrl,
  9. [Parameter(Mandatory, HelpMessage="Please provide an update url")][string] $UpdateUrl,
  10. [Parameter(Mandatory, HelpMessage="Please provide an about url")][string] $AboutUrl,
  11. [Parameter(Mandatory, HelpMessage="Please provide an alias for localhost")][string] $LoopbackAlias,
  12. [bool] $clean = $false # if true, cleans up previous build artifacts
  13. )
  14. # ============================
  15. # Function Definitions Section
  16. # ============================
  17. function Main {
  18. [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
  19. $ProgressPreference = 'SilentlyContinue' # disables Invoke-WebRequest's progress bar, which slows down downloads to a few bytes/s
  20. # check preconditions
  21. if ((Get-Command "git" -ErrorAction SilentlyContinue) -eq $null)
  22. {
  23. Write-Error "Unable to find git.exe in your PATH (try: choco install git)"
  24. exit 1
  25. }
  26. if ((Get-Command "mvn" -ErrorAction SilentlyContinue) -eq $null)
  27. {
  28. Write-Error "Unable to find mvn.cmd in your PATH (try: choco install maven)"
  29. exit 1
  30. }
  31. if ((Get-Command 'wix' -ErrorAction SilentlyContinue) -eq $null)
  32. {
  33. Write-Error 'Unable to find wix in your PATH (try: dotnet tool install --global wix --version 6.0.0)'
  34. exit 1
  35. }
  36. $wixExtensions = & wix.exe extension list --global | Out-String
  37. if ($wixExtensions -notmatch 'WixToolset.UI.wixext') {
  38. Write-Error 'Wix UI extension missing. Please install it with: wix.exe extension add WixToolset.UI.wixext/6.0.0 --global)'
  39. exit 1
  40. }
  41. if ($wixExtensions -notmatch 'WixToolset.Util.wixext') {
  42. Write-Error 'Wix Util extension missing. Please install it with: wix.exe extension add WixToolset.Util.wixext/6.0.0 --global)'
  43. exit 1
  44. }
  45. if ($wixExtensions -notmatch 'WixToolset.BootstrapperApplications.wixext') {
  46. Write-Error 'Wix Bootstrapper extension missing. Please install it with: wix.exe extension add WixToolset.BootstrapperApplications.wixext/6.0.0 --global)'
  47. exit 1
  48. }
  49. $buildDir = Split-Path -Parent $PSCommandPath
  50. $version = $(mvn -f $buildDir/../../pom.xml help:evaluate -Dexpression="project.version" -q -DforceStdout)
  51. $semVerNo = $version -replace '(\d+\.\d+\.\d+).*','$1'
  52. $revisionNo = $(git rev-list --count HEAD)
  53. Write-Host "`$version=$version"
  54. Write-Host "`$semVerNo=$semVerNo"
  55. Write-Host "`$revisionNo=$revisionNo"
  56. Write-Host "`$buildDir=$buildDir"
  57. Write-Host "`$Env:JAVA_HOME=$Env:JAVA_HOME"
  58. $copyright = "(C) $CopyrightStartYear - $((Get-Date).Year) $Vendor"
  59. # compile
  60. &mvn -B -f $buildDir/../../pom.xml clean package -DskipTests -Pwin "-Djavafx.platform=win"
  61. Copy-Item "$buildDir\..\..\target\$MainJarGlob.jar" -Destination "$buildDir\..\..\target\mods"
  62. # add runtime
  63. $runtimeImagePath = '.\runtime'
  64. if ($clean -and (Test-Path -Path $runtimeImagePath)) {
  65. Remove-Item -Path $runtimeImagePath -Force -Recurse
  66. }
  67. ## download jfx jmods for X64, while they are part of the Arm64 JDK
  68. $archCode = (Get-CimInstance Win32_Processor).Architecture
  69. $archName = switch ($archCode) {
  70. 9 { "x64" }
  71. 12 { "ARM64" }
  72. default { "WMI Win32_Processor.Architecture code ($archCode)" }
  73. }
  74. switch ($archName) {
  75. 'ARM64' {
  76. $javafxBaseJmod = Join-Path $Env:JAVA_HOME "jmods\javafx.base.jmod"
  77. if (!(Test-Path $javafxBaseJmod)) {
  78. Write-Error "JavaFX module not found in JDK. Please ensure full JDK (including jmods) is installed."
  79. exit 1
  80. }
  81. $jmodPaths = "$Env:JAVA_HOME/jmods"
  82. }
  83. 'x64' {
  84. $javaFxVersion='24.0.1'
  85. $javaFxJmodsUrl = "https://download2.gluonhq.com/openjfx/${javaFxVersion}/openjfx-${javaFxVersion}_windows-x64_bin-jmods.zip"
  86. $javaFxJmodsSHA256 = 'f13d17c7caf88654fc835f1b4e75a9b0f34a888eb8abef381796c0002e63b03f'
  87. $javaFxJmods = '.\resources\jfxJmods.zip'
  88. if( !(Test-Path -Path $javaFxJmods) ) {
  89. Write-Host "Downloading ${javaFxJmodsUrl}..."
  90. Invoke-WebRequest $javaFxJmodsUrl -OutFile $javaFxJmods # redirects are followed by default
  91. }
  92. $jmodsChecksumActual = $(Get-FileHash -Path $javaFxJmods -Algorithm SHA256).Hash.ToLower()
  93. if( $jmodsChecksumActual -ne $javaFxJmodsSHA256 ) {
  94. Write-Error "Checksum mismatch for jfxJmods.zip. Expected: $javaFxJmodsSHA256
  95. , actual: $jmodsChecksumActual"
  96. exit 1;
  97. }
  98. Expand-Archive -Path $javaFxJmods -Force -DestinationPath ".\resources\"
  99. Remove-Item -Recurse -Force -Path ".\resources\javafx-jmods" -ErrorAction Ignore
  100. Move-Item -Force -Path ".\resources\javafx-jmods-*" -Destination ".\resources\javafx-jmods" -ErrorAction Stop
  101. $jmodPaths="$buildDir/resources/javafx-jmods";
  102. }
  103. default {
  104. Write-Error "Unsupported architecture: $arch"
  105. exit 1
  106. }
  107. }
  108. ## create custom runtime
  109. ### check for JEP 493
  110. if ((& "$Env:JAVA_HOME\bin\jlink" --help | Select-String -Pattern "Linking from run-time image enabled" -SimpleMatch | Measure-Object).Count -eq 0 ) {
  111. $jmodPaths="$Env:JAVA_HOME/jmods;" + $jmodPaths;
  112. }
  113. ### create runtime
  114. & "$Env:JAVA_HOME\bin\jlink" `
  115. --verbose `
  116. --output runtime `
  117. --module-path $jmodPaths `
  118. --add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.accessibility,jdk.management.jfr,jdk.crypto.mscapi,java.compiler,javafx.base,javafx.graphics,javafx.controls,javafx.fxml `
  119. --strip-native-commands `
  120. --no-header-files `
  121. --no-man-pages `
  122. --strip-debug `
  123. --compress "zip-0" #do not compress and use msi compression
  124. $appPath = ".\$AppName"
  125. if ($clean -and (Test-Path -Path $appPath)) {
  126. Remove-Item -Path $appPath -Force -Recurse
  127. }
  128. $javaOptions = @(
  129. "--java-options", "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.win,org.cryptomator.integrations.win"
  130. "--java-options", "-Xss5m"
  131. "--java-options", "-Xmx256m"
  132. "--java-options", "-Dcryptomator.appVersion=`"$semVerNo`""
  133. "--java-options", "-Dfile.encoding=`"utf-8`""
  134. "--java-options", "-Djava.net.useSystemProxies=true"
  135. "--java-options", "-Dcryptomator.logDir=`"@{localappdata}/$AppName`""
  136. "--java-options", "-Dcryptomator.pluginDir=`"@{appdata}/$AppName/Plugins`""
  137. "--java-options", "-Dcryptomator.settingsPath=`"@{appdata}/$AppName/settings.json;@{userhome}/AppData/Roaming/$AppName/settings.json`""
  138. "--java-options", "-Dcryptomator.ipcSocketPath=`"@{localappdata}/$AppName/ipc.socket`""
  139. "--java-options", "-Dcryptomator.p12Path=`"@{appdata}/$AppName/key.p12;@{userhome}/AppData/Roaming/$AppName/key.p12`""
  140. "--java-options", "-Dcryptomator.mountPointsDir=`"@{userhome}/$AppName`""
  141. "--java-options", "-Dcryptomator.loopbackAlias=`"$LoopbackAlias`""
  142. "--java-options", "-Dcryptomator.integrationsWin.autoStartShellLinkName=`"$AppName`""
  143. "--java-options", "-Dcryptomator.integrationsWin.keychainPaths=`"@{appdata}/$AppName/keychain.json;@{userhome}/AppData/Roaming/$AppName/keychain.json`""
  144. "--java-options", "-Dcryptomator.integrationsWin.windowsHelloKeychainPaths=`"@{appdata}/$AppName/windowsHelloKeychain.json`""
  145. "--java-options", "-Dcryptomator.showTrayIcon=true"
  146. "--java-options", "-Dcryptomator.buildNumber=`"msi-$revisionNo`""
  147. )
  148. # create app dir
  149. & "$Env:JAVA_HOME\bin\jpackage" `
  150. --verbose `
  151. --type app-image `
  152. --runtime-image runtime `
  153. --input ../../target/libs `
  154. --module-path ../../target/mods `
  155. --module $ModuleAndMainClass `
  156. --dest . `
  157. --name $AppName `
  158. --vendor $Vendor `
  159. --copyright $copyright `
  160. --app-version "$semVerNo.$revisionNo" `
  161. --resource-dir resources `
  162. --icon resources/$AppName.ico `
  163. --add-launcher "${AppName} (Debug)=$buildDir\debug-launcher.properties" `
  164. @javaOptions
  165. if ($LASTEXITCODE -ne 0) {
  166. Write-Error "jpackage Appimage failed with exit code $LASTEXITCODE"
  167. return 1;
  168. }
  169. #Create RTF license for msi
  170. &mvn -B -f $buildDir/../../pom.xml license:add-third-party "-Djavafx.platform=win" `
  171. "-Dlicense.thirdPartyFilename=license.rtf" `
  172. "-Dlicense.fileTemplate=$buildDir\resources\licenseTemplate.ftl" `
  173. "-Dlicense.outputDirectory=$buildDir\resources\" `
  174. "-Dlicense.includedScopes=compile" `
  175. "-Dlicense.excludedGroups=^org\.cryptomator" `
  176. "-Dlicense.failOnMissing=true" `
  177. "-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges"
  178. # patch app dir
  179. Copy-Item "contrib\*" -Destination "$AppName"
  180. attrib -r "$AppName\$AppName.exe"
  181. attrib -r "$AppName\${AppName} (Debug).exe"
  182. # patch batch script to set hostfile
  183. $webDAVPatcher = "$AppName\patchWebDAV.bat"
  184. try {
  185. (Get-Content $webDAVPatcher ) -replace '::REPLACE ME', "SET LOOPBACK_ALIAS=`"$LoopbackAlias`"" | Set-Content $webDAVPatcher
  186. } catch {
  187. Write-Host "Failed to set LOOPBACK_ALIAS for patchWebDAV.bat"
  188. exit 1
  189. }
  190. # create .msi
  191. $Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources"
  192. $Env:JP_WIXHELPER_DIR = "."
  193. & "$Env:JAVA_HOME\bin\jpackage" `
  194. --verbose `
  195. --type msi `
  196. --win-upgrade-uuid $UpgradeUUID `
  197. --app-image $AppName `
  198. --dest installer `
  199. --name $AppName `
  200. --vendor $Vendor `
  201. --copyright $copyright `
  202. --app-version "$semVerNo.$revisionNo" `
  203. --win-menu `
  204. --win-dir-chooser `
  205. --win-shortcut-prompt `
  206. --win-menu-group $AppName `
  207. --resource-dir resources `
  208. --license-file resources/license.rtf `
  209. --win-update-url $UpdateUrl `
  210. --about-url $AboutUrl `
  211. --file-associations resources/FAvaultFile.properties
  212. if ($LASTEXITCODE -ne 0) {
  213. Write-Error "jpackage MSI failed with exit code $LASTEXITCODE"
  214. return 1;
  215. }
  216. #Create RTF license for bundle
  217. &mvn -B -f $buildDir/../../pom.xml license:add-third-party "-Djavafx.platform=win" `
  218. "-Dlicense.thirdPartyFilename=license.rtf" `
  219. "-Dlicense.fileTemplate=$buildDir\bundle\resources\licenseTemplate.ftl" `
  220. "-Dlicense.outputDirectory=$buildDir\bundle\resources\" `
  221. "-Dlicense.includedScopes=compile" `
  222. "-Dlicense.excludedGroups=^org\.cryptomator" `
  223. "-Dlicense.failOnMissing=true" `
  224. "-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges"
  225. # download Winfsp
  226. $winfspMsiUrl= 'https://github.com/winfsp/winfsp/releases/download/v2.1/winfsp-2.1.25156.msi'
  227. $winfspMsiHash = '073A70E00F77423E34BED98B86E600DEF93393BA5822204FAC57A29324DB9F7A'
  228. Write-Host "Downloading ${winfspMsiUrl}..."
  229. Invoke-WebRequest $winfspMsiUrl -OutFile ".\bundle\resources\winfsp.msi" # redirects are followed by default
  230. $computedHash = $(Get-FileHash -Path '.\bundle\resources\winfsp.msi' -Algorithm SHA256).Hash
  231. if (! $computedHash.Equals($winfspMsiHash)) {
  232. Write-Error -Category InvalidData -CategoryActivity "Data integrity check failed" -Message @"
  233. Downloaded Winfsp Installer does not match stored SHA256 checksum.
  234. Expected: $winfspMsiHash
  235. Actual: $computedHash
  236. "@
  237. exit 1
  238. }
  239. # download legacy-winfsp uninstaller
  240. $winfspUninstaller= 'https://github.com/cryptomator/winfsp-uninstaller/releases/latest/download/winfsp-uninstaller.exe'
  241. Write-Host "Downloading ${winfspUninstaller}..."
  242. Invoke-WebRequest $winfspUninstaller -OutFile ".\bundle\resources\winfsp-uninstaller.exe" # redirects are followed by default
  243. # copy MSI to bundle resources
  244. Copy-Item ".\installer\$AppName-*.msi" -Destination ".\bundle\resources\$AppName.msi" -Force
  245. # create bundle including winfsp
  246. & wix build `
  247. -define BundleName="$AppName" `
  248. -define BundleVersion="$semVerNo.$revisionNo" `
  249. -define BundleVendor="$Vendor" `
  250. -define BundleCopyright="$copyright" `
  251. -define AboutUrl="$AboutUrl" `
  252. -define HelpUrl="$HelpUrl" `
  253. -define UpdateUrl="$UpdateUrl" `
  254. -ext "WixToolset.Util.wixext" `
  255. -ext "WixToolset.BootstrapperApplications.wixext" `
  256. .\bundle\bundleWithWinfsp.wxs `
  257. -out "installer\$AppName-Installer.exe"
  258. Write-Host "Created EXE installer .\installer\$AppName-Installer.exe"
  259. return 0;
  260. }
  261. # ============================
  262. # Script Execution Starts Here
  263. # ============================
  264. if ($clean) {
  265. Write-Host "Cleaning up previous build artifacts..."
  266. Remove-Item -Path ".\runtime" -Force -Recurse -ErrorAction Ignore
  267. Remove-Item -Path ".\$AppName" -Force -Recurse -ErrorAction Ignore
  268. Remove-Item -Path ".\installer" -Force -Recurse -ErrorAction Ignore
  269. }
  270. return Main