Nullean.Make 0.25.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Nullean.Make --version 0.25.0
                    
NuGet\Install-Package Nullean.Make -Version 0.25.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Nullean.Make" Version="0.25.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Nullean.Make" Version="0.25.0" />
                    
Directory.Packages.props
<PackageReference Include="Nullean.Make" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Nullean.Make --version 0.25.0
                    
#r "nuget: Nullean.Make, 0.25.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Nullean.Make@0.25.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Nullean.Make&version=0.25.0
                    
Install as a Cake Addin
#tool nuget:?package=Nullean.Make&version=0.25.0
                    
Install as a Cake Tool

Nullean.Make

Typed build targets for .NET, with Argh-quality help. Your targets are the cases of a C# union; each one runs commands, and dependencies run first.

#!/usr/bin/env -S dotnet run --
#:package Nullean.Make@0.24.0
#:property GenerateDocumentationFile=true
#:property NoWarn=CS1591
using Nullean.Make;
using static Nullean.Make.Shell;

var app = new MakeApp<Target>("build");
var config = app.Option<string>("--config", "Build configuration", defaultValue: "Release");

app.Bind(t => t.Value switch
{
    Clean     => app.Target(() => Exec("dotnet clean")),
    Build     => app.Target([new Clean()], () => Exec($"dotnet build -c {config.Value}")),
    Test test => app.Target([new Build()], () =>
    {
        if (test.Filter is { } filter) Exec($"dotnet test -c {config.Value} --filter:{filter}");
        else Exec($"dotnet test -c {config.Value}");
    }),
    Pack      => app.Target(() => Exec("dotnet pack -c Release -o build/output")),
    Release   => app.Command([new Test()], [new Pack()]),
    _ => throw new InvalidOperationException($"unmapped target {t.Value}")
});

return await app.RunAsync(args);

/// <summary>Clean known output locations</summary>
record Clean;
/// <summary>Run build</summary>
record Build;
/// <summary>Run build then tests</summary>
record Test(string? Filter = null);
/// <summary>Pack the NuGet packages</summary>
record Pack;
/// <summary>Test, then pack</summary>
record Release;

union Target(Clean, Build, Test, Pack, Release);
./build.cs --help
./build.cs test --filter Unit --config Debug
./build.cs release
./build.cs release -s        # skip the tests, run only pack
  Commands:  (pipeline entry points — compose and sequence targets)
  release    Test, then pack

  Targets:  (atomic steps — can also be run directly)
  clean    Clean known output locations
  build    Run build  (depends on: clean)
  test     Run build then tests  (depends on: build)
  pack     Pack the NuGet packages

Targets

app.Target(body) and app.Target(dependsOn, body) take a plain lambda, so a body can branch, loop and read the case's fields. A Func<Task> body works too. Dependencies run first, once each, and -s / --single-target skips them.

A case record's primary-constructor parameters become the target's flags: Test(string? Filter = null) takes --filter.

Bind runs twice per case: once when the app reads the graph, with each parameter at its default (default when it has none, such as Deploy(string Environment)), and once when the target runs, with the parsed flags. Keep side effects inside the target body.

The package's source generator finds each new MakeApp<TUnion>(...) and records the union's cases at build time, so the app reads no union by reflection. A MakeApp<T> created only through a type parameter has no case table and exits 1 with an error.

Commands

app.Command(requires, composes) sequences other targets. requires are gates, such as tests, and -s skips them. composes always run, in order. An optional third argument is a body that runs last.

Running commands

using static Nullean.Make.Shell; brings in Exec:

  • Exec("dotnet build -c Release") splits on whitespace. Double quotes group an argument that contains spaces ("My App.csproj"), and \" is a literal quote.
  • Exec("dotnet", "build", "-c", config) passes each argument as is. Use it for arguments built in code.

A command shares the console. A non-zero exit code fails the target, and RunAsync returns 1.

Descriptions

A target's description is the <summary> of its case type's XML doc comment. The comment is read from the XML documentation file, so set GenerateDocumentationFile=true: #:property GenerateDocumentationFile=true in a file-based app, or <GenerateDocumentationFile> in a project. .Description("...") overrides it.

Global options

app.Flag(...) and app.Option<T>(...) declare options that every target can read through .Value. A value that does not parse, or a missing value, exits 2 before any target runs.

Namespaces

A case that is itself a union becomes a namespace: union Db(Migrate, Seed) inside union Target(..., Db) gives db migrate and db seed, and db --help lists them. Case type names must be unique across the whole union, including nested unions.

Class-based targets

MakeApp.Execute<TBuild>(args) discovers targets from a MakeBuild class instead: Target properties, [Flag] and [GlobalOption] properties, and nested Namespace classes. build.cs in this repository is a complete example.

For F#, see Nullean.Make.Fs.

Product Compatible and additional computed target framework versions.
.NET net11.0 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Nullean.Make:

Package Downloads
Nullean.Make.Fs

F# DU/match binding over Nullean.Make — exhaustive, typed, namespaced build targets.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.30.0 97 11/12/2026
0.29.0 148 11/12/2026
0.28.0 441 11/12/2026
0.27.2 164 11/11/2026
0.27.1 171 11/11/2026
0.25.0 664 11/11/2026
0.24.0 50 11/11/2026
0.23.0 46 11/11/2026
0.22.1 50 11/11/2026