Skip to main content

Target Definitions

Inside a Build class, you can define your build steps as Target properties. The implementation for a build step is provided as a lambda function through the Executes method:

Build.cs
class Build : NukeBuild
{
public static int Main() => Execute<Build>();

Target MyTarget => _ => _
.Executes(() =>
{
Console.WriteLine("Hello!");
});
}

Dependencies

Specifying dependencies is essential to let targets run in a meaningful and predictable order. There are 3 different types of dependencies, each of them can be defined from both directions.

Define that target A must run before target B unless A is skipped:

Build.cs
class Build : NukeBuild
{
Target A => _ => _
.DependentFor(B) // Choose this...
.Executes(() => { });

Target B => _ => _
.DependsOn(A) // ...or this!
.Executes(() => { });
}
tip

When choosing a direction, you should ask yourself which target should know about the existence of the other. For instance, should a Release target trigger a Tweet target? Or should a Tweet target be triggered by a Release target?

caution

Dependencies between targets are solely defined between the individual targets and not through the position they take in a dependency call. The following examples illustrates the difference between the partial and total order of targets:

Build.cs
class Build : NukeBuild
{
Target A => _ => _
.Executes(() => { });

Target B => _ => _
.Executes(() => { });

Target C => _ => _
.DependsOn(A, B)
.Executes(() => { });
}

The execution is nondeterministic between A->B->C and B->A->C. This isn't necessarily problematic, but something to be aware of. In particular, it allows different targets to run in parallel (currently only in compatible CI/CD environments).

Conditional Execution

Apart from skipping targets manually, you can also programmatically decide whether a target should be skipped. Depending on the use-case, you can choose between dynamic and static conditions.

Define a condition that is checked right before target B executes:

class Build : NukeBuild
{
readonly List<string> Data = new();

Target A => _ => _
.Executes(() => { /* Populate Data */ });

Target B => _ => _
.DependsOn(A)
.OnlyWhenDynamic(() => Data.Any())
.Execute(() => { });
}
tip

When a condition is not met, the exception message is created from the boolean expression. For more complex conditions, you can extract the logic into a separate method or property to make the message more readable.

Requirements

You can define target requirements that are checked right at the beginning of the build execution before any other targets are executed:

class Build : NukeBuild
{
Target A => _ => _
.Requires(() => IsServerBuild)
.Executes(() => { });
}
note

Target requirements are an important aspect to achieve a fail-fast behavior. Preceding targets won't waste any execution time only to discover that a condition that was known right from the beginning was not met.

tip

When a requirement is not met, the exception message is created from the boolean expression. For more complex requirements, you can extract the logic into a separate method or property to make the message more readable.

Failure Handling

Not every failing target should immediately stop the build. Targets that are not essential could allow to continue the execution, while other targets are important to run even if another target has failed. For these use-cases, you can configure the failure handling.

Define that execution continues after target A throws:

class Build : NukeBuild
{
Target A => _ => _
.ProceedAfterFailure()
.Executes(() =>
{
Assert.Fail("error");
});

Target B => _ => _
.DependsOn(A)
.Execute(() => { });
}

Unlisting Targets

It is good practice to follow the single-responsibility principle when implementing targets. However, you may not want to expose every target through the build help text. For cases like this, you can un-list a target:

class Build : NukeBuild
{
Target A => _ => _
.Unlisted()
.Executes(() => { });
}