Skip to content

Build Failures

Chad McCaffery edited this page Oct 17, 2017 · 10 revisions

Build and task failures are exceptions and terminating errors. Non terminating errors are ignored. By default all errors are terminating because the engine sets $ErrorActionPreference = 'Stop' before invoking build scripts.

Invoke-Build invokes build scripts and their tasks, catches errors, and mostly re-throws them after logging and doing some extra checks for tasks (failed tasks do not stop builds if their references are safe (job Task -Safe)).

If a build is invoked with the switch Safe then the engine stores an error as the property Error of the result object and returns quietly. Exceptions are still possible, they are not build failures but fatal errors like invalid arguments, missing build scripts, and etc.


How to fail

There are several ways to cause failures with more or less important differences in errors.


assert and equals

Use assert (Assert-Build) in order to check for a condition and fail with the default or a custom message if a condition is not evaluated to true.

    task Example {
        assert (...) "Something is wrong."
    }

Error messages from assert and equals are distinctive, they emphasize failures caused by unexpected conditions. Error information points to the line of code where assert or equals fails.

Consider to use equals X Y instead of assert (X -eq Y). It is easier to type, it avoids subtle PowerShell conversions, and its error message shows object values and types.


throw

Use standard PowerShell throw in order to throw an error from task code

    task Example {
        throw "Something is wrong."
    }

Error source information points to the code line where an error is thrown.


Write-Error

Do not use Write-Error in tasks. It works but error source information is not useful, it points to some engine code that calls a task, not the error source.

On the other hand, Write-Error can be useful in a function shared between tasks if such errors should point to a task which calls a function and causes its failure.


Build Failure Detection


PowerShell script

If Invoke-Build is called from PowerShell and a caller is about to process failures then try..catch should be used.

    try {
        Invoke-Build ...
    }
    catch {
        # build failed, $_ is the error
    }

Alternatively, invoke a build with the switch Safe and the parameter Result and then check the result property Error.

    Invoke-Build -Safe -Result Result ...
    if ($Result.Error) {
        # build failed, $Result.Error is the error
    }
    else {
        # build succeeded
    }

If none of the above is used then at least consider to set

    $ErrorActionPreference = 'Stop'

before calling Invoke-Build from a script with some other commands after it. Otherwise, on some build failures script may just write errors and continue.


Batch script

If Invoke-Build is called by other applications with PowerShell.exe then a caller analyses the exit code. It is 0 if a build succeeds. Other exit codes are for failures.

    PowerShell.exe -NoProfile -Command "Invoke-Build ..."
    if ERRORLEVEL 1 goto fail

    echo Build succeeded.
    exit /b 0

    :fail
    echo Build FAILED.
    exit /b %ERRORLEVEL%

MSBuild script

One use case is invoking Invoke-Build from MSBuild using the task Exec. Nothing special is needed for failure detection, this task fails when Invoke-Build fails (due to not zero exit code).

    <Exec Command='PowerShell.exe -NoProfile "Invoke-Build ..."'/>

Another MSBuild use case is PostBuildEvent in Visual Studio projects (their are also MSBuild scripts). Nothing special is needed for failure detection, too. The post build event fails if Invoke-Build fails.

    <PropertyGroup>
        <PostBuildEvent>PowerShell.exe -NoProfile "Invoke-Build ..."</PostBuildEvent>
        <RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
    </PropertyGroup>