-
-
Notifications
You must be signed in to change notification settings - Fork 60
Build Scripts Guidelines
Here are some guidelines based on Invoke-Build scripts here and there. They are not rules, scripts may work fine even if they do not follow.
Scripts often use some initialization code invoked before tasks. If it is just setting a few script scope variables then it is fine right in the script body.
But for heavier jobs like loading modules or changing things outside consider
using Enter-Build
, the special block invoked before tasks. It is invoked in
the script scope, as if its code is there.
Alternatively, check the build variable $WhatIf
. If it is false then invoke
your initialization code.
Why? Because build scripts are not always invoked for running tasks. They may be invoked for getting task information or code completion.
Example with Enter-Build
Enter-Build {
'Prepare for tasks...'
}
task Task1 {
'Doing Task1...'
}
Example with $WhatIf
if (!$WhatIf) {
'Prepare for tasks...'
}
task Task1 {
'Doing Task1...'
}
For getting the build directory, use the predefined variable $BuildRoot
. The
engine sets the current location to $BuildRoot
before invoking task jobs and
other build blocks.
By default $BuildRoot
is the build script directory. You may change it in the
beginning of your script. Then all tasks may rely on the new custom build root
maintained current by the engine.
On the other hand, if you need the directory of the current script regardless
of $BuildRoot
then use the PowerShell variable $PSScriptRoot
.
Use the predefined variable $OriginalLocation
in order to get the location
which was current on invoking the build script. Note that this location is
restored as current after the build.
Consider using Set-Location
instead of Push-Location
and Pop-Location
.
The engine takes care of restoring the location to $BuildRoot
for tasks.
Push-and-pop makes sense only if the original location is needed in the same
task later. But the original location of a task is always $BuildRoot
, so
that Set-Location $BuildRoot
will do the same (but safer, see later).
DO
task MyTask {
Set-Location ...
...
}
DO NOT
task MyTask {
Push-Location ...
...
Pop-Location
}
Besides, the above code is not safe. If it fails before Pop-Location
then the location stack remains not clean. The safe code should be:
task MyTask {
Push-Location ...
try {
...
}
finally {
Pop-Location
}
}
Consider using throw
instead of Write-Error
in task code. throw
results
in error information pointing to the error line. This is useful for analysis
and jumping to the source, e.g. from VSCode output with Ctrl+Click.
In contrast, Write-Error
points to the caller code. In tasks it is some build
engine line, not useful for error analysis.
DO
task MyTask {
if (...) {
throw 'Something is wrong'
}
}
DO NOT
task MyTask {
if (...) {
Write-Error 'Something is wrong'
}
}
CAVEAT
In functions called from tasks, Write-Error
is fine and even preferable if
errors should point to the calling task.
Avoid Write-Host
unless you deliberately target some messages for console
only and do not need them in the redirected output, e.g. in build log files.
Use Write-Build
for colored console output which is not lost on redirection.
For not colored messages from tasks just output text directly or write by
Write-Output
.
DO
task MyTask {
'Message 1'
Write-Output 'Message 2'
Write-Build Green 'Green message'
}
DO NOT
task MyTask {
Write-Host 'Message 1'
Write-Host 'Message 2'
Write-Host 'Green message' -ForegroundColor Green
}
CAVEAT
Only tasks and Enter/Exit-*
blocks may output log-like messages. Functions
with their own returned data cannot. This is not about Invoke-Build, this is
PowerShell. Thus, Write-Host
in functions may be reasonable.
Unless there are reasons, avoid using -Before
and -After
in order to set
task relations. The normal way is to list task relations in the parameter
-Jobs
(parameter name often omitted). -Before
and -After
exist for
special cases when tasks cannot be modified directly.
DO
task MyTask1 {
...
}
task MyTask2 Task1, {
...
}
DO NOT
task MyTask1 -Before MyTask2 {
...
}
task MyTask2 {
...
}
- Concepts
- Script Tutorial
- Incremental Tasks
- Partial Incremental Tasks
- How Build Works
- Special Variables
- Build Failures
- Build Analysis
- Parallel Builds
- Persistent Builds
- Portable Build Scripts
- Using for Test Automation
- Debugging Tips
- VSCode Tips
Helpers
- Invoke Task from VSCode
- Generate VSCode Tasks
- Invoke Task from ISE
- Resolve MSBuild
- Show Build Trees
- Show Build Graph
- Argument Completers
- Invoke-Build.template
Appendix