Includes/PwSh.Fw.Build.Macos.psm1
|
# @var ConvertIsInstalled # @brief $true if 'convert' from Image Magick package is installed $Script:ConvertIsInstalled = Get-Command -Name "convert" -ErrorAction SilentlyContinue $Script:ConvertNotInstalledMessage = "'convert' command not found. Automatic icon handling is disabled. Please read the FAQ if you need it." $Script:Png2icnsIsInstalled = Get-Command -Name "png2icns" -ErrorAction SilentlyContinue $Script:Png2icnsNotInstalledMessage = "'png2icns' command not found. Automatic icon handling is disabled. Please read the FAQ if you need it." <# .SYNOPSIS Write Info.plist .DESCRIPTION Output a fully-formated Info.plist file based on build configuration .PARAMETER build build object read from build.rc .PARAMETER Destination destination folder .EXAMPLE $project | Out-InfoPlist -Destination /tmp/project.build/Contents .NOTES #> function Out-InfoPlist { [CmdletBinding()] [OutputType([String])] Param ( [Parameter(Mandatory = $true,ValueFromPipeLine = $true)][hashtable]$project, [Parameter(Mandatory = $true,ValueFromPipeLine = $false)][string]$Destination ) Begin { Write-EnterFunction if (!(dirExist($Destination))) { New-Item $($Destination) -Force -ItemType Directory } } Process { @" <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> <key>CFBundleName</key> <string>$($project.Name)</string> <key>CFBundleDisplayName</key> <string>$($project.DisplayName)</string> <key>CFBundleExecutable</key> <string>run-$($project.Name)-app.sh</string> <key>CFBundleIdentifier</key> <string>$($project.productId)</string> <key>CFBundleIconFile</key> <string>$($project.Name).icns</string> <key>CFBundleShortVersionString</key> <string>$($project.version)</string> <key>CFBundleVersion</key> <string>$($project.fqVersion)</string> <key>CFBundleGetInfoString</key> <string>$($project.Description)</string> </dict> </plist> "@ | Set-Content "$Destination/Info.plist" return "$Destination/Info.plist" } End { Write-LeaveFunction } } <# .SYNOPSIS Write Distribution.xml .DESCRIPTION Output a fully-formated Distribution.xml file based on build configuration .PARAMETER build build object read from build.rc .PARAMETER Destination destination folder .OUTPUTS Full path to Distribution.xml file .EXAMPLE $project | Out-DistributionXML -Destination /tmp/project.build .NOTES #> function Out-DistributionXML { [CmdletBinding()] [OutputType([String])] Param ( [Parameter(Mandatory = $true,ValueFromPipeLine = $true)][hashtable]$project, [Parameter(Mandatory = $true,ValueFromPipeLine = $false)][string]$Destination ) Begin { Write-EnterFunction if (!(dirExist($Destination))) { New-Item $($Destination) -Force -ItemType Directory } } Process { @" <?xml version="1.0" encoding="utf-8" standalone="no"?> <installer-gui-script minSpecVersion="1"> <title>$($project.DisplayName)</title> <organization>$($project.orgId)</organization> <domains enable_localSystem="true"/> <options customize="never" require-scripts="true" rootVolumeOnly="true" /> <!-- Define documents displayed at various steps --> <welcome file="welcome.html" mime-type="text/html" /> <license file="LICENSE" mime-type="text/plain" /> <conclusion file="conclusion.html" mime-type="text/html" /> <!-- List all component packages --> <pkg-ref id="$($project.productId)" version="0" auth="root">/tmp/$($project.Name).pkg</pkg-ref> <!-- List them again here. They can now be organized as a hierarchy if you want. --> <choices-outline> <line choice="$($project.productId)"/> </choices-outline> <!-- Define each choice above --> <choice id="$($project.productId)" visible="false" title="$($project.Name) daemon" description="$($project.Name) daemon" start_selected="true"> <pkg-ref id="$($project.productId)"/> </choice> </installer-gui-script> "@ | Set-Content "$Destination/Distribution.xml" return "$Destination/Distribution.xml" } End { Write-LeaveFunction } } <# .SYNOPSIS Convert an image to icns Apple format .DESCRIPTION Convert an image to icns format to be used on macOS system. .PARAMETER Image Full path to an image file .PARAMETER Destination Destination folder .PARAMETER Filename Optional. New filename of the image .EXAMPLE ConvertTo-LinuxIcons -Image /path/to/favicon.png .NOTES This function do not convert image size. It just convert format. #> function ConvertTo-MacOSIcons { [CmdletBinding()] [OutputType([string], [Boolean])] Param ( [Parameter(Mandatory = $true, ValueFromPipeLine = $true)][string]$Image, [Parameter(Mandatory = $false, ValueFromPipeLine = $false)][string]$Destination, [Parameter(Mandatory = $false, ValueFromPipeLine = $false)][string]$Filename ) Begin { Write-EnterFunction } Process { if (!(Test-FileExist $Image)) { eerror "Image '$Image' not found." $rc = $false } $basename = (Get-Item $Image).BaseName # $ext = (Get-Item $Image).Extension if ([string]::IsNullOrEmpty($Filename)) { $Filename = $basename } if ([string]::IsNullOrEmpty($Destination)) { $Destination = (Get-Item $Image).DirectoryName } if ($Script:Png2icnsIsInstalled) { $null = Execute-Command -exe "png2icns" -args "$Destination/$Filename.icns $Image" $rc = "$Destination/$Filename" } else { Write-Warning $Script:Png2icnsNotInstalledMessage $rc = $Image } return $rc } End { Write-LeaveFunction } } <# .SYNOPSIS Build the project to a macos package .DESCRIPTION Build the project to a full featured MacOS package. .EXAMPLE New-MacOSBuild -Project (Get-Project) .NOTES #> function New-MacOSBuild { [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Low', DefaultParameterSetName = 'PROJECT')] [OutputType([Boolean], [String])] Param ( # The project object as returned by Get-Project [Parameter(Mandatory = $true, ValueFromPipeLine = $true)][hashtable]$Project, # An optional MACOS folder where macos package scripts are stored. @see @url https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html#//apple_ref/doc/uid/10000123i-CH101-SW1 [Alias('Configuration')] [Parameter(Mandatory = $false, ValueFromPipeLine = $false)][string]$MacOSFolder = "./build/macos", # The source folder of your project. Files and directory structure will be kept as-is [Parameter(Mandatory = $false, ValueFromPipeLine = $false)][string]$Source = "./src", # Destination folder to create resulting package [Parameter(Mandatory = $false, ValueFromPipeLine = $false)][string]$Destination = "./releases", # Override output package filename. # It defaults to projectName-Version-Arch.deb [Parameter(Mandatory = $false, ValueFromPipeLine = $false)][string]$OutputFileName, # Build type. It can be 'app', 'pkg' or 'dmg'. Default to 'pkg' [Parameter(Mandatory = $false, ValueFromPipeLine = $false)] [ValidateSet("app", "pkg", "dmg")][string]$BuildType = "pkg", # Force/override/overwrite things [switch]$Force ) Begin { Write-EnterFunction if (!(Test-DirExist $Destination)) { $null = New-Item -Path $Destination -ItemType Directory -Confirm:$false -Force:$Force } $MacOSFolder = ($MacOSFolder | Resolve-Path).path } Process { if (Test-DirExist $MacOSFolder) { # copy MACOS folder taking care of mustache template files Copy-Item $MacOSFolder/* -Recurse $Source -Force:$Force -Exclude "*.mustache" Get-ChildItem $MacOSFolder -Recurse -Filter "*.mustache" | ForEach-Object { $item = $_ $destFilePath = $item.DirectoryName -replace "$MacOSFolder", "$Source" $destFileName = "$destFilePath/$($item.Basename)" ConvertFrom-MustacheTemplate -Template (Get-Content -Raw $item.fullname) -Values $Project | Out-File $destFileName # these are executable scripts (postinst / postrm) so ensure they are executable (Out-File create only regular files) $mode = (stat $item.fullname --printf %a) $null = Execute-Command -exe chmod -args "$mode $destFileName" } } # $project.size = (Get-ChildItem $Source -Recurse | Measure-Object -Property Length -sum).Sum | Convert-Size -From bytes -To KB -ValueOnly # $ControlFields = $project | Out-MacOSCONTROLFile -Destination $Source/MACOS -PassThru # copy icon to proper location # Copy-Item "$($Project.Root)/$($Project.IconFile)" -Destination $Source -Force -Confirm:$false if ($Project.IconFile) { $null = ConvertTo-MacOSIcons -Image $Project.IconFile -Destination "$Source/Content/Resources" -Filename $Project.Name } if ([string]::IsNullOrEmpty($OutputFileName)) { $Filename = "$($project.name)-$($project.fqVersion).$BuildType" } else { $Filename = $OutputFileName } if ($PSCmdlet.ShouldProcess("$Destination/$Filename", "Create macos package")) { switch ($BuildType) { "app" { $rc = Invoke-MacOSAppBuildCommand -Project $Project -Source $Source -Destination $Destination -OutputFileName $Filename } "pkg" { $rc = Invoke-MacOSPkgBuildCommand -Project $Project -Source $Source -Destination $Destination -OutputFileName $Filename } "dmg" { $rc = Invoke-MacOSDmgBuildCommand -Project $Project -Source $Source -Destination $Destination -OutputFileName $Filename } } if (!(Get-Command -Name fakeroot)) { Write-Fatal "fakeroot command not found in system. Try to install the fakeroot package." } $rc = Execute-Command -exe fakeroot -args "dpkg -b $Source $Destination/$Filename" -AsBool Write-Devel "rc = $rc" if (Test-FileExist "$Destination/$Filename") { $value = (Resolve-Path "$Destination/$Filename").Path } else { $value = $false } } else { $value = "$Destination/$Filename" } Write-Devel "value = $value" return $value } End { Write-LeaveFunction } } function Invoke-MacOSPkgBuildCommand { [CmdletBinding()] [OutputType([Boolean], [String])] Param ( # The project object as returned by Get-Project [Parameter(Mandatory = $true, ValueFromPipeLine = $true)][hashtable]$Project, # The source folder of your project. Files and directory structure will be kept as-is [Parameter(Mandatory = $false, ValueFromPipeLine = $false)][string]$Source = "./src", # Destination folder to create resulting package [Parameter(Mandatory = $false, ValueFromPipeLine = $false)][string]$Destination = "./releases", # Install location on target system. Default to /Applications [Parameter(Mandatory = $false, ValueFromPipeLine = $false)][string]$InstallLocation = "/Applications", # Override output package filename. # It defaults to projectName-Version-Arch.deb [Parameter(Mandatory = $false, ValueFromPipeLine = $false)][string]$OutputFileName ) Begin { Write-EnterFunction } Process { $rc = Execute-Command -exe "pkgbuild" -args "--root $Source --identifier $($Project.productId) --version $($Project.fqVersion) --install-location $InstallLocation --ownership recommended --scripts $Source/macos/Contents/Scripts $Destination/$OutputFileName" -AsBool Write-Devel "rc = $rc" if (!$rc) { Write-Error "Failed to create pkg package." return $false } return $OutputFileName } End { Write-LeaveFunction } } |