Modules/businessdev.ALbuild.Apps/Public/Invoke-BcCompiler.ps1
|
function Invoke-BcCompiler { <# .SYNOPSIS Compiles an AL project into an .app package. .DESCRIPTION Compiles an AL project using either the cross-platform AL compiler (default) or a container's compiler: -Engine AlTool (default) invokes the AL compiler executable (alc) on the host. Symbols are taken from the package cache (.alpackages), which Resolve-BcDependencies populates; the AL compiler does not download dependencies itself. -Engine Container runs the compiler inside a Business Central container via the exec seam. The output file name follows the BC convention "<publisher>_<name>_<version>.app", derived from app.json. .PARAMETER ProjectFolder The AL project folder (contains app.json). .PARAMETER OutputFolder Where to write the compiled .app. Default: <ProjectFolder>/output. .PARAMETER Engine AlTool (default) or Container. .PARAMETER CompilerPath (AlTool) The AL compiler executable. Default 'alc'. .PARAMETER ContainerName (Container) The container to compile in. .PARAMETER ContainerCompilerPath (Container) The AL compiler path inside the container. Default 'alc.exe'. .PARAMETER PackageCachePath Symbol package folder(s). Default: <ProjectFolder>/.alpackages. .PARAMETER Analyzer Analyzer assembly paths (CodeCop/AppSourceCop/etc.). .PARAMETER RuleSet Ruleset file. .PARAMETER AssemblyProbingPath Additional .NET assembly probing paths. .PARAMETER LogLevel Compiler log level. .PARAMETER DockerExecutable (Container) The Docker executable to use (default 'docker'). .EXAMPLE Invoke-BcCompiler -ProjectFolder ./app .OUTPUTS PSCustomObject with Success, OutputFile, ExitCode, Output. #> [CmdletBinding()] [OutputType([PSCustomObject])] param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $ProjectFolder, [string] $OutputFolder, [ValidateSet('AlTool', 'Container')] [string] $Engine = 'AlTool', [string] $CompilerPath = 'alc', [string] $ContainerName, [string] $ContainerCompilerPath = 'alc.exe', [string[]] $PackageCachePath = @(), [string[]] $Analyzer = @(), [string] $RuleSet, [string[]] $AssemblyProbingPath = @(), [ValidateSet('', 'Error', 'Warning', 'Verbose', 'Normal')] [string] $LogLevel = '', [string] $DockerExecutable = 'docker' ) $appJsonPath = Join-Path $ProjectFolder 'app.json' if (-not (Test-Path -LiteralPath $appJsonPath)) { throw "No app.json found in project folder '$ProjectFolder'." } $appJson = Get-Content -LiteralPath $appJsonPath -Raw -Encoding UTF8 | ConvertFrom-Json if (-not $OutputFolder) { $OutputFolder = Join-Path $ProjectFolder 'output' } if (-not (Test-Path -LiteralPath $OutputFolder)) { New-Item -Path $OutputFolder -ItemType Directory -Force | Out-Null } if (-not $PackageCachePath -or $PackageCachePath.Count -eq 0) { $PackageCachePath = @(Join-Path $ProjectFolder '.alpackages') } $outputFileName = "$($appJson.publisher)_$($appJson.name)_$($appJson.version).app" $outputFile = Join-Path $OutputFolder $outputFileName $compilerArgs = Get-BcCompilerArguments -ProjectFolder $ProjectFolder -OutputFile $outputFile ` -PackageCachePath $PackageCachePath -Analyzer $Analyzer -RuleSet $RuleSet ` -AssemblyProbingPath $AssemblyProbingPath -LogLevel $LogLevel Write-ALbuildLog "Compiling '$($appJson.name)' $($appJson.version) ($Engine engine)..." if ($Engine -eq 'AlTool') { # Locate the AL Tool CLI: the requested -CompilerPath ('alc'), then the 'al'/'altool' command # from the cross-platform AL Tool dotnet global tool # (dotnet tool install --global Microsoft.Dynamics.BusinessCentral.Development.Tools). The 'al' # tool wraps alc.exe behind a 'compile' subcommand; a raw alc takes the arguments directly. $compiler = Get-Command -Name $CompilerPath -ErrorAction SilentlyContinue | Select-Object -First 1 # Fall back to the AL Tool only when relying on the default name; an explicit -CompilerPath that # is not found is an error (don't silently substitute a different compiler). if (-not $compiler -and -not $PSBoundParameters.ContainsKey('CompilerPath')) { $compiler = @('al', 'altool') | ForEach-Object { Get-Command -Name $_ -ErrorAction SilentlyContinue } | Select-Object -First 1 } if (-not $compiler) { throw "The AL Tool was not found (tried '$CompilerPath' and the 'al' command). Install it with: dotnet tool install --global Microsoft.Dynamics.BusinessCentral.Development.Tools (or pass -CompilerPath)." } $compilerBase = [System.IO.Path]::GetFileNameWithoutExtension($compiler.Source) $invokeArgs = if ($compilerBase -in @('al', 'altool')) { @('compile') + $compilerArgs } else { $compilerArgs } $result = Invoke-ALbuildProcess -FilePath $compiler.Source -Arguments $invokeArgs -PassThru } else { if (-not $ContainerName) { throw "-ContainerName is required when -Engine is Container." } $output = Invoke-BcContainerCommand -ContainerName $ContainerName -DockerExecutable $DockerExecutable -Variables @{ Compiler = $ContainerCompilerPath CompilerArgs = $compilerArgs } -ScriptBlock { & $Compiler @CompilerArgs [PSCustomObject]@{ ExitCode = $LASTEXITCODE } | ConvertTo-Json } $exit = 0 try { $exit = ($output | ConvertFrom-Json).ExitCode } catch { Write-Verbose "Could not parse container compiler exit code: $($_.Exception.Message)" } $result = [PSCustomObject]@{ Success = ($exit -eq 0); ExitCode = $exit; StdOut = $output; StdErr = '' } } $success = $result.Success -and (Test-Path -LiteralPath $outputFile) if ($result.StdOut) { Write-ALbuildLog $result.StdOut.TrimEnd() } if (-not $success) { Write-ALbuildLog -Level Error "Compilation of '$($appJson.name)' failed (exit $($result.ExitCode))." } else { Write-ALbuildLog -Level Success "Compiled '$outputFile'." } return [PSCustomObject]@{ Success = $success OutputFile = if ($success) { $outputFile } else { $null } ExitCode = $result.ExitCode Output = $result.StdOut } } |