Modules/businessdev.ALbuild.Apps/Private/Invoke-BcCallGraphAnalysis.ps1

function Invoke-BcCallGraphAnalysis {
    <#
    .SYNOPSIS
        Runs the call graph engine over a graph (and optionally a baseline) and returns its result dictionary.
 
    .DESCRIPTION
        Metrics, findings (not reached by tests, obsolete calls, dead code), the report model and - with a
        baseline - the impact: added, modified and removed members, their transitive callers and the tests
        that reach the change. A baseline is the folder of an earlier call graph; its analysis.json supplies
        the per-member source hashes that reveal a changed body.
 
    .OUTPUTS
        System.Collections.Generic.Dictionary[string, object] (the analysis.json content).
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)] [string] $GraphPath,
        [string] $BaselinePath,
        [string] $WorkspaceRoot,
        [int] $MaxListItems = 2000
    )

    Import-BcCallGraphEngine
    $graph = Resolve-BcCallGraphPath -Path $GraphPath -Require
    $apps = @()
    if ($graph.AppsPath) {
        # Assigned first: Windows PowerShell's ConvertFrom-Json emits a JSON array as ONE pipeline object, so
        # @(... | ConvertFrom-Json) would be a one-element array holding the whole list.
        $records = Get-Content -LiteralPath $graph.AppsPath -Raw -Encoding UTF8 | ConvertFrom-Json
        $apps = @(foreach ($a in $records) {
                $info = New-Object ALbuild.CallGraph.AppInfo
                $info.Id = "$($a.id)"; $info.Name = $a.name; $info.Publisher = $a.publisher; $info.Version = $a.version
                $info.Role = $a.role; $info.HasSource = [bool]$a.hasSource
                $root = $a.PSObject.Properties['workspaceRoot']
                if (-not $WorkspaceRoot -and $a.role -eq 'own' -and $root -and $root.Value) { $WorkspaceRoot = $root.Value }
                $info
            })
    }
    else {
        Write-ALbuildLog -Level Warning "No apps.json next to '$($graph.GraphPath)'; every app in the graph header counts as own."
    }

    $baseline = $null
    if ($BaselinePath) {
        $baseline = Resolve-BcCallGraphPath -Path $BaselinePath
        if (-not $baseline) { Write-ALbuildLog -Level Warning "Baseline '$BaselinePath' has no call graph; the impact is not computed." }
        elseif (-not $baseline.AnalysisPath) { Write-ALbuildLog -Level Warning "Baseline '$BaselinePath' has no analysis.json; changed bodies are only detected through changed calls." }
    }

    $options = New-Object ALbuild.CallGraph.AnalysisOptions
    $options.WorkspaceRoot = $WorkspaceRoot
    $options.MaxListItems = $MaxListItems
    $sw = [System.Diagnostics.Stopwatch]::StartNew()
    $result = [ALbuild.CallGraph.Analyzer]::Analyze($graph.GraphPath, [ALbuild.CallGraph.AppInfo[]]$apps,
        $(if ($baseline) { $baseline.GraphPath } else { $null }), $(if ($baseline) { $baseline.AnalysisPath } else { $null }), $options)
    $result['baseline'] = if ($baseline) { $baseline.Folder } else { $null }
    Write-ALbuildLog "Call graph analysed in $([int]$sw.Elapsed.TotalSeconds) s."
    return , $result
}