dotnet build

Build a project or solution with the right configuration, verbosity and warnings policy, and diagnose a failing build with a binary log.

Signature
dotnet build [<project|solution>] [-c|--configuration <cfg>] [-f <tfm>] [-r <rid>] [--no-restore] [--no-incremental] [-p:<Name>=<Value>] [-v <level>] [-bl] [-warnaserror]
  • Added in .NET .NET Core 1.0

Parameters

ParameterTypeDescription
$project|solutionoptionalpath= the single project or solution in the current folderProject file, solution file or folder that contains exactly one of them.
$-c, --configurationoptionalstring= DebugBuild configuration. Controls DEBUG/RELEASE symbols, optimisation and the bin/<config> folder.
$-f, --frameworkoptionalTFM= all target frameworksBuild only one target framework of a multi-targeted project.
$--no-restoreoptionalflag= falseSkip the implicit restore. Use after an explicit dotnet restore in CI.
$--no-incrementaloptionalflag= falseIgnore up-to-date checks and rebuild everything.
$-p, --propertyoptionalName=ValueSet an MSBuild property for this build, for example -p:ContinuousIntegrationBuild=true.
$-v, --verbosityoptionalq|m|n|d|diag= minimalLogging detail.
$-bl, --binaryLoggeroptionalflag or fileWrite a complete structured log (msbuild.binlog) of the build.
$-o, --outputoptionalpath= bin/<config>/<tfm>/Output folder for a single project. Not supported for a solution.

Return value

process exit code0 when every project built. 1 when any project failed; errors are printed with file, line and code (CS8618, NU1903...).

Errors

Returns exit code 1 on any compile, restore or MSBuild error. With -warnaserror, warnings such as nullable or NuGet audit warnings fail the build too.

Changelog

  • PHP .NET Core 2.0Restore became implicit in build, run, test and publish.
  • PHP .NET 8UseArtifactsOutput puts all build output under one artifacts/ folder.
  • PHP .NET 9The terminal logger became the default in interactive terminals.

dotnet build runs MSBuild's Build target: an implicit restore, then the C# compiler for each project in dependency order, then copying dependencies to the output folder. The output is bin/Debug/net10.0/ by default, intermediate files go to obj/, and MSBuild skips any project whose inputs have not changed since the last build.

Configuration is the decision that matters

The default configuration is Debug: no optimisation, the DEBUG symbol defined. dotnet publish defaults to Release but build and test do not, so a CI script must say -c Release everywhere or nowhere. The classic broken pipeline is dotnet build -c Release followed by dotnet test --no-build: the test step looks in bin/Debug, finds nothing and fails, or worse, runs a stale Debug build left in a cache.

Options for CI

OptionWhy in CI
--no-restoreRestore once with dotnet restore --locked-mode, then never touch the network again
-warnaserrorFails on every warning, including MSBuild and NuGet audit warnings, not only compiler ones
-p:ContinuousIntegrationBuild=trueDeterministic paths in PDBs, so builds are reproducible
-blWrites msbuild.binlog; upload it as an artifact when the build fails
-tl:offPlain line-by-line output when a log viewer mangles the terminal logger

A binary log records every property, item, target and task, so "why is this reference missing" or "which Directory.Build.props won" is answered by opening it in the MSBuild Structured Log Viewer instead of re-running with -v diag. It contains environment variables, so treat it as sensitive.

Solutions, output and artifacts

Building a .slnx builds every project in it, in parallel where references allow. -o is not supported for a solution because every project would overwrite the others' files in one folder. To collect output in one place, set <UseArtifactsOutput>true</UseArtifactsOutput> in Directory.Build.props; output then goes to artifacts/bin/Orders.Api/debug/ and similar.

Failure modes

  • Stale or locked output after switching branches: the compiler server and MSBuild nodes keep running between builds. dotnet build-server shutdown stops them; --no-incremental forces a full rebuild.
  • A warning that is an error on CI but not locally: put TreatWarningsAsErrors in Directory.Build.props so both behave the same. It promotes NuGet audit warnings as well, so exempt the severities you only want reported with WarningsNotAsErrors.
  • A NuGet audit warning (NU1902 to NU1904) suddenly failing a -warnaserror build with no code change: a new advisory was published. That is the gate working; upgrade the package.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Fail a pull request build when any warning, including a new NuGet vulnerability advisory, appears
  • Stamp the git commit into AssemblyInformationalVersion so the running orders API can report exactly which build it is
  • Upload msbuild.binlog from a failed CI build to find which Directory.Build.props set a property

Frequently asked questions

Why does dotnet build -o fail or warn for my solution?

All projects would write into one folder and overwrite each other's files. Build the solution without -o and use UseArtifactsOutput, or publish each deployable project with its own -o.

Is -warnaserror the same as TreatWarningsAsErrors?

Close but not equal. TreatWarningsAsErrors promotes compiler warnings and NuGet warnings, NuGet audit NU1901 to NU1904 included, for the projects that set it; -warnaserror (or MSBuildTreatWarningsAsErrors) also promotes MSBuild engine warnings (MSBxxxx). With the property on, list low-severity audit codes in WarningsNotAsErrors or every new low advisory breaks every developer's restore.

Do I need dotnet restore before dotnet build?

No, build restores implicitly. In CI, restore separately (with --locked-mode) and then build with --no-restore so network problems and lock-file drift fail in their own step.

More examples

A CI build that fails for the right reasons

<code>--locked-mode</code> makes restore fail when <code>packages.lock.json</code> is out of date instead of silently resolving new versions. The binary log goes to a known path so the pipeline can upload it when the build step fails.

Example · bash
set -euo pipefail

dotnet restore Shop.slnx --locked-mode
dotnet build Shop.slnx -c Release --no-restore \
  -warnaserror \
  -p:ContinuousIntegrationBuild=true \
  -p:Version=1.4.2 -p:SourceRevisionId="$(git rev-parse --short HEAD)" \
  -bl:artifacts/logs/build.binlog

# Same configuration in every later step, or --no-build looks in bin/Debug.
dotnet test Shop.slnx -c Release --no-build

Build policy in Directory.Build.props

Every project below this folder imports the file, so laptops and CI share one warnings policy. <code>TreatWarningsAsErrors</code> also applies to NuGet restore, so without the <code>NU1901;NU1902</code> exemption a new low-severity advisory would fail every build; high and critical (NU1903, NU1904) still fail. <code>UseArtifactsOutput</code> moves output to <code>artifacts/bin/&lt;project&gt;/&lt;config&gt;</code>, which also removes the need for <code>-o</code> on solution builds.

Example · xml
<Project>
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
    <!-- NuGet honours TreatWarningsAsErrors too: keep obsolete-API warnings and
         low/moderate audit advisories visible without blocking every restore. -->
    <WarningsNotAsErrors>CS0618;NU1901;NU1902</WarningsNotAsErrors>
    <AnalysisLevel>latest-recommended</AnalysisLevel>
    <UseArtifactsOutput>true</UseArtifactsOutput>
    <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
  </PropertyGroup>
</Project>

Diagnosing a build that works on one machine only

Most works-on-my-machine builds are a different SDK band, a different <code>Directory.Build.props</code> being imported, or leftover state from build servers. The binary log answers the first two; <code>build-server shutdown</code> rules out the third.

Example · bash
# Which SDK, which global.json, which MSBuild?
dotnet --info

# Full structured log of one project, then open msbuild.binlog in the
# MSBuild Structured Log Viewer and search for the property or reference.
dotnet build src/Orders.Api -bl -v minimal

# Stale compiler server or MSBuild node holding old state or file locks
dotnet build-server shutdown
dotnet build src/Orders.Api --no-incremental

# Plain output when a CI log viewer garbles the live terminal display
dotnet build -tl:off

Discussion

  • Be the first to comment on this lesson.