Modules/businessdev.ALbuild.RuntimePackages/Public/New-BcRuntimePackageFromSource.ps1

function New-BcRuntimePackageFromSource {
    <#
    .SYNOPSIS
        Builds a runtime package (or a normal .app) straight from AL source - no container, no server.
 
    .DESCRIPTION
        A runtime package normally comes from a running BC server (Get-NAVAppRuntimePackage), which is
        why New-BcRuntimePackage needs a container. It does not have to. Every BC artifact ships the
        complete AL compiler, and that compiler can emit the runtime package itself.
 
        Verified against a real server, not assumed: the package produced here is part-for-part
        identical to one produced by Get-NAVAppRuntimePackage in a container (29 parts each, no
        difference) and is accepted by a BC 28 server via Publish-NAVApp.
 
        What this removes from the runtime factory is the container round trip per product: no
        provisioning, no publishing the dependency chain just so the server can resolve it. Dependencies
        are still needed - but only as SYMBOLS, which is what -SymbolFolder takes and what the factory
        already builds.
 
        The compiler must match the platform the package targets: a BC 26 runtime package is emitted by
        BC 26's compiler. Pass -PlatformPath (the platform part of the Get-BcArtifact result the factory
        already holds) and the right one is used.
 
    .PARAMETER ProjectFolder
        The folder holding app.json.
 
    .PARAMETER OutputFolder
        Where to write the package.
 
    .PARAMETER SymbolFolder
        Additional folders holding .app symbol packages. '<ProjectFolder>\.alpackages' is always used.
 
    .PARAMETER PlatformPath
        PlatformPath of a Get-BcArtifact result - selects the compiler for that platform version.
 
    .PARAMETER ServiceTier
        An explicit service tier folder, instead of resolving one.
 
    .PARAMETER BcVersion
        Version or prefix ('28', '28.4') to pick from the artifact caches when no path is given.
 
    .PARAMETER Kind
        'Runtime' (default) emits a runtime package; 'App' emits a normal .app.
 
    .PARAMETER IncludeAlCode
        Keep the AL source inside the runtime package. Off by default - leaving the source out is what a
        runtime package is for.
 
    .PARAMETER FileName
        Override the output file name. Default is BC's own: Publisher_Name_Version[.runtime].app.
 
    .OUTPUTS
        System.String - the path to the package.
 
    .EXAMPLE
        $artifact = Get-BcArtifact -ArtifactUrl $url
        New-BcRuntimePackageFromSource -ProjectFolder .\app -OutputFolder .\out `
            -SymbolFolder (Get-BcArtifactSymbolFolder -Artifact $artifact) -PlatformPath $artifact.PlatformPath
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([string])]
    param(
        [Parameter(Mandatory)] [string] $ProjectFolder,
        [Parameter(Mandatory)] [string] $OutputFolder,
        [string[]] $SymbolFolder = @(),
        [string] $PlatformPath,
        [string] $ServiceTier,
        [string] $BcVersion,
        [ValidateSet('Runtime', 'App')] [string] $Kind = 'Runtime',
        [switch] $IncludeAlCode,
        [string] $FileName
    )

    $manifestPath = Join-Path $ProjectFolder 'app.json'
    if (-not (Test-Path -LiteralPath $manifestPath)) {
        throw "'$ProjectFolder' contains no app.json. Point -ProjectFolder at the folder holding app.json - for a repository with app/ and test/ that is one of those, not the repository root."
    }

    $tierArgs = @{}
    if ($ServiceTier) { $tierArgs['Path'] = $ServiceTier }
    elseif ($PlatformPath) { $tierArgs['PlatformPath'] = $PlatformPath }
    if ($BcVersion) { $tierArgs['Version'] = $BcVersion }

    $tier = Get-BcCompilerServiceTier @tierArgs
    if (-not $tier) {
        throw "No Business Central artifact with an AL compiler was found$(if ($BcVersion) { " for version '$BcVersion'" }). Fetch one with Get-BcArtifact, or pass -ServiceTier / -PlatformPath."
    }

    $target = if ($FileName) { $FileName } else { $null }
    if (-not $PSCmdlet.ShouldProcess($ProjectFolder, "Emit $Kind package with BC $($tier.Version) compiler")) { return }

    Write-ALbuildLog "Compiling '$ProjectFolder' with the BC $($tier.Version) compiler ($($tier.TargetFramework), no container)."
    $compiler = Import-BcAlCompiler -Tier $tier
    try {
        $result = Invoke-BcSourceEmit -Compiler $compiler -ProjectFolder $ProjectFolder -OutputFolder $OutputFolder `
            -SymbolFolder $SymbolFolder -Kind $Kind -IncludeAlCode:$IncludeAlCode -FileName $target
    }
    finally {
        # Collectible, so a long-running factory can drive several platform versions in one process
        # without the previous compiler staying resident.
        try { $compiler.Context.Unload() } catch { }
    }

    Write-ALbuildLog -Level Success "Created $($Kind.ToLowerInvariant()) package '$($result.Path)' ($('{0:N0}' -f $result.Bytes) bytes, $($result.Objects) object(s))."
    return $result.Path
}