Skip to content

Latest commit

 

History

History
128 lines (83 loc) · 13.5 KB

PowerForge.PowerShellCompilation.Development.md

File metadata and controls

128 lines (83 loc) · 13.5 KB

Develop compiled PowerShell projects

powerforge powershell project run builds and runs one executable target from source. watch repeats that workflow after edits. The CLI uses the same compiler and project owners as the PowerShell module.

Use the .NET 10 SDK for building the CLI and generated executables. The Windows examples below select win-x64; a declared target must match the operating system and architecture of the process running PowerForge. Additional platform qualification is tracked separately in M30.

Build the development CLI

From a repository checkout:

dotnet build ./PowerForge.Cli/PowerForge.Cli.csproj -c Release -f net10.0
$powerforge = (Resolve-Path ./PowerForge.Cli/bin/Release/net10.0/PowerForge.Cli.dll).Path

Keep $powerforge as an absolute path so you can work from your own project directory. The following commands use that locally built CLI and do not require installing a global tool.

Keep the project directory and final executable path compact on Windows. Restore acquires dependencies in an isolated temporary workspace and retains its verified lock and environment under the project. This permits deep internal package paths without changing machine long-path policy; it does not remove Windows process-launch limits on the final executable or working directory. On Windows Server 2025 with long-path policy disabled, the qualification workflow uses a 249-character project path, a 238-character executable path, and internal package paths up to 397 characters. See the distribution evidence for the tested scope.

Installation adds a content-addressed directory beneath the project root. Leave room for that directory even when the build output path is short: on Windows, the executable and its working-directory paths must each remain below 260 characters. The installer checks the final paths before extracting or reusing an installation and reports their lengths if the project root needs shortening. Test, run and watch apply the same launch check. Binary-module validation checks its PowerShell host and working directory; library metadata inspection does not launch a process. These are Windows process-launch constraints, separate from filesystem long-path support.

Create and run an executable

Create a project directory and save main.ps1 there:

return 7

From that directory:

dotnet $powerforge powershell project init ./main.ps1 --name Sample --kind exe --mode Strict --framework net10.0 --rid win-x64
dotnet $powerforge powershell project lock ./powerforge.psproject.json
dotnet $powerforge powershell project restore ./powerforge.psproject.json
dotnet $powerforge powershell project run ./powerforge.psproject.json
dotnet $powerforge powershell project watch ./powerforge.psproject.json

Change return 7 to return 9. Watch stops the preceding attempt, waits for stable edits, rebuilds, verifies the new artifact, and runs it. A parse or build failure leaves watch waiting for the next edit and launches nothing. A successful application exit leaves watch active. Press Ctrl+C to stop watch or a foreground run; the CLI returns exit code 130 and terminates its owned application process tree. Process termination does not guarantee execution of the application's finally blocks.

For scripts that need the PowerShell runtime, choose --mode Package when creating the project. Strict mode requires the complete executable workflow to be admitted by the compiler. Use project explain to inspect unsupported source. run and watch accept one executable target; use --target <name> when the manifest declares several targets.

Use the project diagnostics guide to inspect grouped source, dependency, shaping, and artifact-integrity causes with project explain and project diagnose.

Arguments and streams

Everything after the CLI's -- is passed to the executable as an argument vector without shell parsing:

dotnet $powerforge powershell project run ./powerforge.psproject.json -- -Name 'two words'

The executable retains its own argument-binding rules. A Package executable accepts named PowerShell parameters; its own -- switches to positional-only input. To pass a literal dash-prefixed value into a script's $args, use both delimiters:

dotnet $powerforge powershell project run ./powerforge.psproject.json -- -- --help --verbose

The first delimiter belongs to the project CLI, and the second belongs to the generated executable. Neither --help nor --verbose is then interpreted by PowerForge's global options.

The application runs from the project directory and inherits stdin, stdout and stderr, including redirected OS handles. A successful run adds no status text to those streams and returns the application's exit code. Build/validation failures write a diagnostic to stderr. Watch writes lifecycle messages to stderr; stdout remains the application's stream. Run/watch do not support --output json, which would conflict with application output.

What an edit can change

Run/watch permits content edits to already-locked local scripts and ordinary resources. It keeps the reviewed lock file unchanged and records the exact current source graph in the generated artifact. It does not automatically approve changed dependency declarations, newly introduced graph nodes, module manifests, binaries, provider packages, semantic profiles, or target settings. Review those changes, then run project lock and project restore explicitly.

Watch fingerprints source files, selected resource/dependency inputs, provider packages, the project manifest, and lock/environment receipts. It excludes declared artifact directories and build/cache state. Content hashes detect same-size edits even when a timestamp is retained. Rapid edits cancel the superseded attempt and wait for stable input before the next build. Generated build output is reused only through the compiler's verified content-addressed cache in .powerforge/cache; analysis, integrity checks and locked restore still run.

After the initial acquisition, builds use the isolated package root with offline locked restore. You can verify its availability explicitly:

dotnet $powerforge powershell project restore ./powerforge.psproject.json --offline

Only one run/watch session can own a project at a time. Stop it before starting another development session. An input change during a build prevents that superseded attempt from launching; watch then rebuilds the latest stable input. Immediately before launch, run checks that the artifact files still match the inventory authenticated for that attempt. If another producer replaces those files, run fails instead of accepting its replacement receipt.

Test and package a reviewed revision

Development execution does not replace the exact-source review required by project test/pack. Once the source is ready, refresh the lock and restore, then follow the existing qualification path:

dotnet $powerforge powershell project lock ./powerforge.psproject.json
dotnet $powerforge powershell project restore ./powerforge.psproject.json --offline
dotnet $powerforge powershell project build ./powerforge.psproject.json
dotnet $powerforge powershell project test ./powerforge.psproject.json
dotnet $powerforge powershell project diagnose ./powerforge.psproject.json
dotnet $powerforge powershell project pack ./powerforge.psproject.json

Project pack creates the existing qualified ZIP. For a tested Strict CLR library, use project pack --format nuget and the library NuGet quickstart. Interactive source debugging remains a deferred tooling gate; portable PDB output alone is not debugger qualification.

On Unix, qualification includes each payload file's read/write/execute permissions. ZIP packing and project install preserve those permissions, so installed executable targets remain runnable. Changed permissions invalidate the corresponding test, package, or installation evidence; privileged modes and symbolic-link ZIP entries are rejected. For Unix evidence created without permission metadata, rerun project test and project pack before installation.

After building a typed executable or binary module, inspect its authenticated debugger inputs with dotnet $powerforge powershell project debug-plan ./powerforge.psproject.json --output json. Select a target with --target <name> when the project has more than one. The result supplies the artifact, portable PDB, working directory, and sourceFileMap from stable PDB document names to current authored PowerShell files. The command rejects a stale source, changed artifact, missing PDB, or source checksum mismatch; rebuild before using a rejected plan. For a VS Code C# debugger configuration, use sourceFileMap as returned, enable exact source matching, launch the executable artifact or attach to a module host, and put breakpoints in the authored .ps1/.psm1. The direct breakpoint, step, locals, stack, and exception gate remains open; treat this as debugger setup evidence, not an observed debugger guarantee.

For a module, follow the module consumer quickstart from a .psd1 or .psm1 with --kind dll --mode Hybrid. For a CLR library, use --kind library --mode Strict and the NuGet consumer guide. Both use the same lock → restore → build → test → diagnose → pack sequence. Module test performs a clean import, while library test validates CLR metadata. Invoke exported commands or use an ordinary .NET consumer to verify the behavior you intend to distribute. Run/watch is the executable development path, not a substitute for those consumers.

Qualify whole workflows

Build/Invoke-PowerShellWholeWorkflowGate.ps1 runs the reviewed public powershell-yaml 0.4.12 and platyPS 0.14.2 module packets on both PowerShell 7/net10 and Windows PowerShell 5.1/net472, followed by the complete sitemap comparison application. It reuses the corpus download, archive and qualification owners and requires original, built and installed scenario parity through the canonical project lifecycle. The module packages are pinned comparison inputs; this gate does not install them globally or add their binaries to PowerForge.

Use the built source CLI in a Windows x64 PowerShell 7.6 child process. Choose $workspace on the configured scratch volume with an absolute path of at most 25 characters, leaving room for installed Desktop module payload paths. Choose a separate empty $evidence directory. Keep DOTNET_CLI_HOME and TEMP private to the child, retain the ordinary NuGet cache and shared NUGET_SCRATCH, and set DOTNET_GENERATE_ASPNET_CERTIFICATE and DOTNET_ADD_GLOBAL_TOOLS_TO_PATH to false.

./Build/Invoke-PowerShellWholeWorkflowGate.ps1 `
    -CliAssemblyPath $powerforge -WorkspacePath $workspace -EvidencePath $evidence

The gate seals compiler and packet identities, workflow receipts, observations and source-withdrawal evidence. The required authored bodies must be emitted, and the declared scenarios must match with the staged compiler inputs unavailable during artifact observation. Hybrid still uses the PowerShell SDK and supported hosted command regions; passing these packets does not establish exhaustive branch coverage or execution without PowerShell. The existing Windows product CI job runs this gate alongside the focused compiler contracts.

For a compiler test graph built with the SDK's --artifacts-path, pass that same root to Build/Invoke-PowerShellCompilerGate.ps1 -ArtifactsPath $artifacts -NoBuild -EvidencePath $evidence. The gate uses that graph's net10 CLI for process tests and the strict corpus. Direct test runners can select the same prebuilt CLI with the test-only POWERFORGE_TEST_CLI_ASSEMBLY_PATH environment variable; an invalid configured path fails instead of selecting another build.

Diagnose a failed attempt

Symptom Next action
Source is rejected Run project explain and inspect the target's evidence under .powerforge/explain.
Dependencies or declarations changed Review project lock, then run project restore; watch never approves these automatically.
Environment belongs to another manifest revision Restore the updated project and target selection.
Missing or altered package/closure Restore the reviewed closure; use online restore if the isolated package root needs reacquisition.
Another development session owns the project Stop the preceding run/watch process.
Target belongs to another OS or architecture Select a target matching the actual host.
Inputs changed during build Retry run, or let watch build the next stable revision.
Artifact output changed before launch Stop the competing output writer, then retry run. Do not build into the active development output from another process.
Test/diagnose rejects a development artifact after source edits Refresh the exact source lock and restore, then build and test the reviewed revision.

The compilation guide describes artifact contracts. The next milestones and assessment record remaining work and qualification evidence.