Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

41 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

delphi-msbuild

delphi-msbuild logo

Delphi CI GitHub Release License: MIT Ask DeepWiki Continuous Delphi

Quick-start, or enhance your Delphi build automation with a standalone, MIT-licensed, CI-ready build tool from Continuous-Delphi - designed for automating builds using MSBuild. See delphi-dccbuild for a similar tool that utilizes the DCC command line compilers.

Overview

delphi-msbuild.ps1 builds a Delphi .dproj project using MSBuild. It sources the Delphi build environment from rsvars.bat (located under the Delphi installation root) and then invokes MSBuild with the requested project file, platform, and configuration.

It is designed to be used standalone by providing the ProjectFile path and the Delphi RootDir (and optionally the Platform and Config settings.)

delphi-msbuild.ps1 `
  -ProjectFile .\src\MyApp.dproj `
  -RootDir     'C:\Program Files (x86)\Embarcadero\Studio\23.0' 

You can also pipe the output from delphi-inspect.ps1 to automatically detect the RootDir:

delphi-inspect.ps1 -DetectLatest -Platform Win32 -BuildSystem MSBuild |
      delphi-msbuild.ps1 -ProjectFile .\src\MyApp.dproj

PowerShell Compatibility

Runs on the widely available Windows PowerShell 5.1 (powershell.exe) and the newer PowerShell 7+ (pwsh).

Note: the test suite requires pwsh.

Usage

pwsh delphi-msbuild.ps1 -ProjectFile <path> [options]

Parameters

-ProjectFile

-ProjectFile <path>

Path to the .dproj project file. Required.

The path is resolved to an absolute path before being passed to MSBuild.

-RootDir

-RootDir <path>

The Delphi installation root directory (e.g. C:\Program Files (x86)\Embarcadero\Studio\23.0).

rsvars.bat is expected at <RootDir>\bin\rsvars.bat.

If omitted, -RootDir is taken from the .rootDir property of a piped delphi-inspect result object.

Note: An explicit -RootDir takes precedence over the piped value.

Note: -RootDir is required unless -SkipRsvars is used, in which case it becomes optional metadata (see -SkipRsvars).

-Platform

-Platform <platform>    (default: Win32)

The target compilation platform. Passed to MSBuild as /p:Platform=<value>.

Valid values:

  • Win32
  • Win64
  • macOS32
  • macOS64
  • macOSARM64
  • Linux64
  • iOS32
  • iOSSimulator32
  • iOS64
  • iOSSimulator64
  • Android32
  • Android64
  • WinARM64EC

-Config

-Config <value>    (default: Debug)

The build configuration name. Passed to MSBuild as /p:Config=<value>.

Note: Common values are Debug and Release.

-Target

-Target <value>    (default: Build)

The MSBuild target to execute.

Valid values: Build, Clean, Rebuild

-Verbosity

-Verbosity <value>    (default: normal)

The MSBuild verbosity level. Passed to MSBuild as /v:<value>.

Valid values: quiet, minimal, normal, detailed, diagnostic

-ExeOutputDir

-ExeOutputDir <path>

Output directory for the compiled executable or library. Passed to MSBuild as /p:DCC_ExeOutput=<path>.

When omitted, MSBuild uses the output location defined in the project's PropertyGroups. The result object's .exeOutputDir is $null when this parameter is not supplied.

-DcuOutputDir

-DcuOutputDir <path>

Output directory for compiled .dcu files. Passed to MSBuild as /p:DCC_DcuOutput=<path>.

When omitted, MSBuild uses the DCU location from the project's PropertyGroups. The result object's .dcuOutputDir is $null when this parameter is not supplied.

-UnitSearchPath

-UnitSearchPath <path[]>

Additional unit search paths appended to the project's existing unit path. Accepts an array of path strings. Multiple paths are joined with semicolons (each trailing separator trimmed) and passed via the DCC_UnitSearchPath environment variable, not as a /p: property:

$env:DCC_UnitSearchPath = "path1;path2"

An environment variable is used instead of /p:DCC_UnitSearchPath=... because a command-line /p: value becomes a global MSBuild property that overrides the project's config-scoped <DCC_UnitSearchPath>...;$(DCC_UnitSearchPath)</DCC_UnitSearchPath> assignment entirely -- the project's own paths would be lost. An environment-derived property has the lowest precedence, so the project's PropertyGroup still runs and its $(DCC_UnitSearchPath) self-reference resolves against this value, appending your paths to the project's. The env var is set only for the duration of the build and any prior value is restored.

When omitted (or an empty array), the DCC_UnitSearchPath env var is not set. The result object's .unitSearchPath is $null when no paths are supplied.

Example:

-UnitSearchPath @('C:\Libs\A', 'C:\Libs\B')

-Define

-Define <string[]>

One or more additional compiler defines. When at least one value is supplied, the defines are joined with semicolons and passed via the DCC_Define environment variable, not as a /p: property:

$env:DCC_Define = "DEFINE1;DEFINE2"

An environment variable is used instead of /p:DCC_Define=... because a command-line /p: value becomes a global MSBuild property that overrides the project's config-scoped <DCC_Define>DEBUG;$(DCC_Define)</DCC_Define> assignment entirely -- the project's own defines (DEBUG, RELEASE) would be dropped and the $(DCC_Define) self-reference would collapse to a stray token. An environment-derived property has the lowest MSBuild precedence, so the project's config PropertyGroup still runs and its $(DCC_Define) self-reference resolves against this value, appending your defines to the project's. The env var is set only for the duration of the build and any prior value is restored.

When no -Define values are supplied (the default), the DCC_Define env var is not set.

Examples:

# Single define
delphi-msbuild.ps1 -ProjectFile .\src\MyApp.dproj -RootDir $root -Define CI

# Multiple defines
delphi-msbuild.ps1 -ProjectFile .\src\MyApp.dproj -RootDir $root `
    -Define MYFLAG, USE_JEDI_JCL

# Via pipeline with defines
delphi-inspect.ps1 -DetectLatest -Platform Win32 -BuildSystem MSBuild |
    delphi-msbuild.ps1 -ProjectFile .\src\MyApp.dproj -Define CI, MYFLAG

-BuildAllUnits (switch)

-BuildAllUnits

When set, appends /p:DCC_BuildAllUnits=true, forcing the compiler to build every unit reachable from the project rather than only those out of date. This is a first-class shortcut for a property the older batch build path always set.

Because it is emitted before -Property, a -Property @{ DCC_BuildAllUnits = ... } entry still overrides it.

delphi-msbuild.ps1 -ProjectFile .\src\MyApp.dproj -RootDir $root -BuildAllUnits

-EnvLibraryPath

-EnvLibraryPath <string>

Sets /p:_EnvLibraryPath="..." (the value is always quoted). Used by the older batch build path's Win32-only sub-path. A first-class shortcut that remains overridable via -Property.

delphi-msbuild.ps1 -ProjectFile .\src\MyApp.dproj -RootDir $root `
    -EnvLibraryPath 'C:\Program Files\Lib'

-Property

-Property <hashtable>

Generic pass-through for arbitrary MSBuild properties. Each entry is appended to the MSBuild command line as /p:Key=Value. This is the escape hatch for any property the script does not expose as a first-class parameter -- for example DCC_BuildAllUnits, _EnvLibraryPath, DCC_ResourcePath, DCC_UsePackage, or DCC_Namespace.

-Property entries are appended after the built-in properties (Config, Platform, and the DCC_* output/search-path properties). Because MSBuild honours the last /p: occurrence on the command line, an entry supplied via -Property overrides a built-in property of the same name.

Values containing whitespace or semicolons are quoted automatically; clean values are passed unquoted. Keys are emitted in sorted order for deterministic command lines.

Examples:

# Build all units and set a resource path
delphi-msbuild.ps1 -ProjectFile .\src\MyApp.dproj -RootDir $root `
    -Property @{ DCC_BuildAllUnits = 'true'; DCC_ResourcePath = 'C:\res' }

# Value with spaces is quoted automatically -> /p:_EnvLibraryPath="C:\Program Files\Lib"
delphi-msbuild.ps1 -ProjectFile .\src\MyApp.dproj -RootDir $root `
    -Property @{ _EnvLibraryPath = 'C:\Program Files\Lib' }

-ShowOutput (switch)

-ShowOutput

When set (meant for non-object pipeline usage)

  • MSBuild output streams to the console in real time.
  • The result object's .output property still contains the full captured text.
  • On build failure, a Write-Error message is emitted to stderr.

When not set (default):

  • MSBuild output (stdout and stderr combined) is captured.
  • The result object's .output property contains the full captured text.
  • On build failure, no additional stderr message is emitted (the captured output already contains the compiler diagnostics).

-SkipRsvars (switch)

-SkipRsvars

By default the script requires <RootDir>\bin\rsvars.bat, sources it, and builds in the resulting environment. When -SkipRsvars is set:

  • rsvars.bat is not required or sourced.
  • The build runs in the caller's current process environment -- set BDS, PATH, FrameworkDir, etc. yourself before invoking (the approach _SetDelphiBuildPaths.bat takes for trimmed, non-installed toolchains).
  • -RootDir becomes optional metadata; it is not validated, and its absence no longer produces exit code 3. (The project-file check, exit 4, still runs.)
# Caller sets the Delphi environment, then builds without rsvars
$env:BDS = 'G:\radprogrammer\rad-buildfiles-d12'
delphi-msbuild.ps1 -ProjectFile .\src\MyApp.dproj -SkipRsvars

-MsbuildPath

-MsbuildPath <string>

Invoke a specific msbuild.exe instead of resolving msbuild.exe from PATH. This allows explicit per-era .NET Framework MSBuild selection (v2.0.50727, v3.5, v4.0.30319), which matters for older (2007-2010-era) Delphi builds where the batch path chose the framework version explicitly.

If the supplied path does not exist, the script exits with code 2 before invoking any build.

delphi-msbuild.ps1 -ProjectFile .\src\MyApp.dproj -SkipRsvars `
    -MsbuildPath 'C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe'

-DelphiInstallation (pipeline input)

[psobject] (ValueFromPipeline)

Accepts a pscustomobject from delphi-inspect.ps1 -DetectLatest. The .rootDir property is used as the Delphi installation root when -RootDir is not supplied explicitly.

Note: Any object with a .rootDir string property is accepted; it does not have to originate from delphi-inspect.ps1.


Result Object

On success or build failure (exit codes 0 and 5), a single pscustomobject is written to the pipeline before the script exits. This allows downstream pipeline steps to consume the build result.

Property Type Description
output string Captured MSBuild output
scriptVersion string Version of delphi-msbuild.ps1
projectFile string Absolute path to the project file
platform string Platform value used (e.g. Win32)
config string Config value used (e.g. Debug)
target string Target used (e.g. Build)
define string[] Values passed via -Define; empty array when not supplied
buildAllUnits bool $true when -BuildAllUnits was supplied
envLibraryPath string Value of -EnvLibraryPath; $null when not supplied
rootDir string Resolved Delphi installation root; $null with -SkipRsvars and no -RootDir
rsvarsPath string Derived path to rsvars.bat; $null with -SkipRsvars when no -RootDir given
skipRsvars bool $true when -SkipRsvars was supplied
msbuildPath string Value of -MsbuildPath; $null when not supplied
exeOutputDir string Value of -ExeOutputDir; $null when not supplied
dcuOutputDir string Value of -DcuOutputDir; $null when not supplied
unitSearchPath string[] Value of -UnitSearchPath; $null when not supplied
property hashtable Properties passed via -Property; $null when not supplied
exitCode int MSBuild process exit code
success bool $true when exitCode is 0
warnings int Warning count parsed from MSBuild summary
errors int Error count parsed from MSBuild summary

Note: On fatal errors before MSBuild is invoked (exit codes 2, 3, 4) no result object is emitted.


Exit Codes

Code Meaning
0 Build succeeded
1 Unexpected internal error (unhandled exception)
2 Invalid arguments: -ProjectFile missing, or -MsbuildPath does not exist
3 rootDir missing/empty, directory not found, or rsvars.bat absent (not applicable with -SkipRsvars)
4 Project file not found on disk
5 MSBuild completed but returned a non-zero exit code

Example 1) Normal -- pipe from delphi-inspect and build

Discover the latest ready MSBuild installation and pipe it into a build:

$result = delphi-inspect.ps1 -DetectLatest -Platform Win32 -BuildSystem MSBuild |
              delphi-msbuild.ps1 -ProjectFile .\src\MyApp.dproj

$result.success    # $true
$result.exitCode   # 0
$result.platform   # Win32
$result.config     # Debug
$result.rootDir    # C:\Program Files (x86)\Embarcadero\Studio\23.0
$result.rsvarsPath # C:\Program Files (x86)\Embarcadero\Studio\23.0\bin\rsvars.bat

The captured MSBuild output is available in $result.output for post-processing or logging.

Example 2) Normal -- explicit root dir, Release config

Build without inspect, targeting a specific installation:

$result = delphi-msbuild.ps1 `
              -ProjectFile  .\src\MyApp.dproj `
              -RootDir      'C:\Program Files (x86)\Embarcadero\Studio\23.0' `
              -Platform     Win64 `
              -Config       Release `
              -Verbosity    minimal

Example 3) Normal -- stream output to console

Use -ShowOutput when you want build output visible in real time (e.g. in a terminal session rather than a CI log capture):

delphi-inspect.ps1 -DetectLatest -Platform Win32 -BuildSystem MSBuild |
  delphi-msbuild.ps1 -ProjectFile .\src\MyApp.dproj -ShowOutput

MSBuild output appears in the console as it runs. $result.output is still populated in this mode.

Example 4) Normal -- feed result into a downstream step

Because the result object is always written to the pipeline before exit, you can chain a test runner or other step:

$buildResult = delphi-inspect.ps1 -DetectLatest -Platform Win32 -BuildSystem MSBuild |
                   delphi-msbuild.ps1 -ProjectFile .\src\MyApp.dproj

if ($buildResult.success) {
    # run tests, package, etc.
}

Example 5) Normal -- build with defines and check warnings

Pass additional compiler defines and inspect the build summary counts:

$result = delphi-inspect.ps1 -DetectLatest -Platform Win32 -BuildSystem MSBuild |
              delphi-msbuild.ps1 -ProjectFile .\src\MyApp.dproj `
                  -Config Release -Define CI, PRODUCTION

if ($result.success -and $result.warnings -gt 0) {
    Write-Warning "Build succeeded with $($result.warnings) warning(s)"
}

Example 6) Error -- no Delphi installation supplied (exit 3)

Running without a piped object or -RootDir:

delphi-msbuild.ps1 -ProjectFile .\src\MyApp.dproj

# stderr: No Delphi root dir supplied. Provide -RootDir or pipe a
#         delphi-inspect result object.
# exit code: 3
# no result object emitted

The same exit code (3) is returned if -RootDir is supplied but the directory does not exist on disk, or if rsvars.bat is absent under <RootDir>\bin\:

delphi-msbuild.ps1 -ProjectFile .\src\MyApp.dproj -RootDir C:\Missing\Path

# stderr: Delphi root dir not found on disk: C:\Missing\Path
# exit code: 3

delphi-msbuild.ps1 -ProjectFile .\src\MyApp.dproj -RootDir C:\SomeDir

# stderr: rsvars.bat not found: C:\SomeDir\bin\rsvars.bat
# exit code: 3

Example 7) Error -- project file not found (exit 4)

delphi-inspect.ps1 -DetectLatest -Platform Win32 -BuildSystem MSBuild |
    delphi-msbuild.ps1 -ProjectFile .\src\Typo.dproj

# stderr: Project file not found: C:\Work\src\Typo.dproj
# exit code: 4
# no result object emitted

Example 8) Error -- MSBuild build failure (exit 5)

When MSBuild itself runs but returns a non-zero exit code (compilation errors, missing components, etc.), the result object is emitted with success = $false before the script exits:

$result = delphi-inspect.ps1 -DetectLatest -Platform Win32 -BuildSystem MSBuild |
              delphi-msbuild.ps1 -ProjectFile .\src\MyApp.dproj

# exit code: 5
$result.success    # $false
$result.exitCode   # 1 (or whatever MSBuild returned)
$result.output     # captured MSBuild output containing error diagnostics

Inspect $result.output to see the MSBuild error lines. When -ShowOutput is used, the output has already streamed to the console and is still available in $result.output; a Write-Error message is emitted to stderr as well.


Notes on "rsvars.bat" and Environment Variables

rsvars.bat typically sets the following environment variables in the Delphi installation's own cmd.exe session:

  • BDS -- Delphi installation root
  • BDSBIN -- Delphi bin directory
  • BDSCOMMONDIR -- shared Delphi data directory
  • FrameworkDir -- .NET Framework directory
  • FrameworkVersion -- .NET Framework version string
  • PATH -- prepended with MSBuild and Delphi bin paths

PowerShell cannot inherit these from a cmd.exe side-effect directly. delphi-msbuild.ps1 works around this by sourcing rsvars.bat via:

cmd.exe /c "call rsvars.bat > nul 2>&1 && set"

Then each KEY=VALUE line from set is applied to the current PowerShell process environment via [Environment]::SetEnvironmentVariable. This makes msbuild.exe available on PATH for the remainder of the session.

Comparison with delphi-dccbuild.ps1

Aspect delphi-dccbuild.ps1 delphi-msbuild.ps1
Project file type .dpr .dproj
Build system dcc*.exe msbuild.exe
Environment setup Sources rsvars.bat Sources rsvars.bat
Config parameter Added as define (-DDEBUG) Passed as /p:Config=Debug
Target: Rebuild -B flag /t:Rebuild
Target: Clean Not available /t:Clean
Verbosity options quiet, normal quiet through diagnostic
Result rsvarsPath Present Present
Result compilerPath Present Not present
Inspect -BuildSystem DCC MSBuild

Both scripts use the same -RootDir parameter and accept the same pipeline object shape (.rootDir property), so the same delphi-inspect.ps1 -DetectLatest result object works with either.

continuous-delphi logo

Part of the Continuous Delphi Organization

This repository follows the Continuous Delphi organization taxonomy. See cd-meta-org for navigation and governance.

  • docs/org-taxonomy.md -- naming and tagging conventions
  • docs/versioning-policy.md -- release and versioning rules
  • docs/repo-lifecycle.md -- lifecycle states and graduation criteria

About

[PowerShell] Simple scriptable command-line builds for Delphi projects. Pair with delphi-inspect to discover the full list of installed Delphi toolchains (or pick the latest) to set build settings.

Topics

Resources

Stars

Watchers

Forks

Releases

Contributors

Languages