-
-
Notifications
You must be signed in to change notification settings - Fork 59
Script Tutorial
task Hello {
'Hello, World!'
}
If the script directory is the current PowerShell location and the script name
is .build.ps1
(or *.build.ps1
and it is the only like this there) then it
is the default script which can be invoked simply as
Invoke-Build
If there is no task specified then the default task is invoked which is the "." task if it is defined, otherwise the first added task, Hello in this script.
Build output looks like
Build Hello ...\.build.ps1
Task /Hello
Hello, World!
Done /Hello 00:00:00.01
Build succeeded. 1 tasks, 0 errors, 0 warnings 00:00:00.02
This script defines three tasks: Build builds a C# project, Clean removes temporary data, and the task "." invokes Build and Clean:
use 4.0 MSBuild
# Synopsis: Build the project.
task Build {
exec { MSBuild Project.csproj /t:Build /p:Configuration=Release }
}
# Synopsis: Remove temp files.
task Clean {
Remove-Item bin, obj -Recurse -Force -ErrorAction 0
}
# Synopsis: Build and clean.
task . Build, Clean
Now the command Invoke-Build
without parameters invokes the default task "."
because it is defined. It builds the project and cleans after that.
Points of interest:
-
use
(alias ofUse-BuildAlias
) is used in order to simplify calls to external tools like MSBuild; -
exec
(alias ofInvoke-BuildExec
) is used to call an executable and check its exit code; - Tasks Build and Clean consist of one script job and do not reference other tasks;
- The task "." references two other tasks and does not have its own script jobs.
- The task "." is invoked by default.
- Help comments
# Synopsis: ...
Any combination of defined tasks can be invoked. For example, there is no such a task as RebuildAll in the script. But the equivalent action can be invoked:
Invoke-Build Clean, Build
Help comments are used for task help shown by the special task ?
PS> Invoke-Build ?
Name Jobs Synopsis
---- ---- --------
Build {} Build the project.
Clean {} Remove temp files.
. Build, Clean Build and clean.
Task jobs are defined by the parameter Jobs
with its name often omitted. Jobs
are references to other tasks and script block actions. For instance, the task
"." in the previous example may have its own actions in addition to task
references:
task . {
# do some job before the Build
},
Build, {
# do some job after the Build
},
Clean, {
# do something after all
}
Note that other task references do not have to precede own scripts. The order is up to an author. Thus, a task may not only reference tasks that it depends on but also specify tasks to be invoked after. The term "depends on" is not quite applicable, such references are rather "continuations".
Build script parameters are standard PowerShell script parameters. They are
available for the script code for reading and writing as $var
and for all
tasks for reading as $var
and for writing as $script:var
.
Note: script parameters are usual script variables (see later). The only difference is that their values can be specified on invoking a script.
In the previous example the task Build builds the Release configuration. This slightly modified script makes this configurable:
param(
$Configuration = 'Release'
)
use 4.0 MSBuild
task Build {
exec { MSBuild Project.csproj /t:Build /p:Configuration=$Configuration }
}
The command Invoke-Build Build
still builds the Release configuration due
to the default script parameter value. But it is now possible to specify and
build Debug as well:
Invoke-Build Build -Configuration Debug
Yes, this is so simple. If script parameters do not conflict with parameters of
Invoke-Build
then they are simply specified for Invoke-Build
, thanks to the
magic of PowerShell dynamic parameters.
Conflicting parameters or parameter sets created by a program can be passed in
a build script using the hashtable Parameters
. For example if a script has a
parameter Checkpoint
(conflicting) then this syntax is used:
Invoke-Build Build -Parameters @{ Checkpoint = ... }
Build script variables are standard PowerShell variables in the script scope.
Variables are available for the script code for reading and writing as $var
and for all tasks for reading as $var
and for writing as $script:var
.
Example: The variable $Tests
is defined in the script scope and available
for all tasks (even defined before because tasks are invoked after the script):
$Tests = 'SmokeTests.build.ps1', 'MoreTests.build.ps1'
task Test {
foreach($_ in $Tests) {
Invoke-Build * $_
}
}
Note: The special task *
invokes all tasks starting from roots. It is
normally used on testing with tests defined as build tasks. Tests are often
invoked all together.
Nested scripts invoked by Invoke-Build have their own script scopes with their
own parameters and variables. Parent variables can be accessed, normally for
reading. For example, the variable $Tests
is available in the parent scope
for the nested scripts SmokeTests.build.ps1 and MoreTests.build.ps1.
Tasks may use existing script variables or create new. New script variables are normally created for use in other tasks.
Example: The task Version gets the file version and stores it in the
script variable as $script:Version = ...
. Tasks Zip and NuGet reference
this task before their own scripts. Thus, the scripts assume existence of the
script variable $Version
and use it for package names:
task Version {
$script:Version = (Get-Item Project.dll).VersionInfo.FileVersion
}
task Zip Version, {
exec { & 7z a Project.$Version.zip Project.dll }
}
task NuGet Version, {
exec { NuGet pack Project.nuspec -Version $Version }
}
task PackAll Zip, NuGet
This is the main rule of build flows. A task can be referenced by other tasks many times. But as soon as it is invoked, its contribution to a build is over.
When the task PackAll is invoked in the previous example then Version is referenced twice by the tasks scheduled for the build, at first by Zip and then by NuGet. But in fact it is invoked only when Zip calls it.
Invoke-Build "properties" are usual PowerShell script variables and parameters, like MSBuild properties defined in XML scripts (variables) and properties that come from command lines (parameters), or environment variables.
MSBuild deals with environment variables using the same syntax. In contrast,
Invoke-Build scripts do not use environment variables in the same way. They
should be referenced explicitly as $env:var
or obtained by property
. It
gets a value of session or environment variable or the default. Nulls are
treated as not defined, so that property
never gets nulls, it fails.
Example: $DevModuleDir
or $Configuration
may come to the script below
in three ways: as script parameters, as variables defined in a parent scope,
and as existing environment variables. If nothing is the case then (property DevModuleDir)
throws an exception because there is no default value specified.
But (property Configuration Release)
does not fail, it uses the default value
Release.
param(
$DevModuleDir = (property DevModuleDir),
$Configuration = (property Configuration Release)
)
task Install {
Copy-Item Bin/$Configuration/MyModule.dll $DevModuleDir/MyModule
}
Caution
Build properties should be used sparingly with carefully chosen names which
unlikely can exist and be not related to the build. Consider to avoid them in
persistent builds because properties rely on external data. On resuming these
data may be missing or different. Custom Export-Build
and Import-Build
may
be designed to take this into account but it may be easier to avoid properties.
The task parameter If
specifies a condition, either a value evaluated on
creation or a script block evaluated on invocation. If it is present and
evaluated to false then the task is not invoked.
In the following example the task MakeHelp is invoked only if the current configuration is Release:
param(
$Configuration = 'Release'
)
task MakeHelp -If ($Configuration -eq 'Release') {
...
}
task Build {
...
},
MakeHelp
Note that the task MakeHelp is still defined even if its condition is not true. Thus, other tasks may refer to it, like the task Build does.
If a condition is a script block and a task is called more than once then it is possible that it is skipped at first due to its condition evaluated to false but still invoked later when its condition gets true.
If a task throws an exception or writes a terminating error then the whole
build fails unless the task is referenced as safe (job Task -Safe)
by the
calling task and all other tasks having a chance to be invoked in this build.
The helper error
(alias of Get-BuildError
) is used in order to get errors
of tasks called safe. Note that error
is useless with unsafe references
because the whole build stops on failures.
In this example Task2 calls Task1 safe and then analyses its errors:
task Task1 {
# code with potential failures
...
}
task Task2 (job Task1 -Safe), {
if (error Task1) {
# Task1 failed
...
}
else {
# Task1 succeeded
...
}
}
The task parameters After
and Before
are used in order to alter build task
jobs in special cases, for example if a build script is imported (dot-sourced)
and its direct changes are not suitable.
task Build {
# original Build code
}
task BeforeBuild -Before Build {
# when Build is called this code is invoked before it
}
task AfterBuild -After Build {
# when Build is called this code is invoked after it
}
When the build engine preprocesses tasks the task Build is transformed into a task which is equivalent to this:
task Build BeforeBuild, <original task jobs>, AfterBuild
Other task parameters Inputs
, Outputs
, and the switch Partial
are used in
order to define incremental and partial incremental tasks. These techniques are
described in here:
- 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