Getting started
editGenerate the API file on build
Add the Meziantou.Framework.PublicApiGenerator.MSBuild package to the project of your library, and set the path of the API file in PublicApiGeneratorOutputPath:
<ItemGroup>
<PackageReference Include="Meziantou.Framework.PublicApiGenerator.MSBuild" Version="x.y.z" PrivateAssets="all" />
</ItemGroup>
<PropertyGroup>
<PublicApiGeneratorOutputPath>PublicApi/PublicApi.g.cs</PublicApiGeneratorOutputPath>
</PropertyGroup>
<ItemGroup>
<!-- The API file describes the library: it is not part of it -->
<Compile Remove="PublicApi/**" />
</ItemGroup>PrivateAssets="all"keeps the package out of the dependencies of your own package.- The path is relative to the project directory. The project compiles every
.csfile of its directory, so exclude the API file withCompile Remove, or write it outside of the project directory. Otherwise the next build compiles the stubs with your code, and fails because every type is declared twice.
Build the project:
dotnet buildAfter the build, PublicApi/PublicApi.g.cs lists the public API of the assembly. For this code:
namespace Demo;
public enum Unit
{
Celsius,
Fahrenheit,
Kelvin = 10,
}
public sealed class Thermometer : IComparable<Thermometer>
{
private double _value;
public Thermometer(string name) => Name = name;
public string Name { get; private set; }
public string? Location { get; set; }
public event EventHandler? Changed;
public double Read(Unit unit = Unit.Celsius) => _value;
public void Rename(string name) { Name = name; Changed?.Invoke(this, EventArgs.Empty); }
internal void Calibrate(double value) => _value = value; // Not visible outside the assembly
public int CompareTo(Thermometer? other) => 0;
}The API file is:
// Target Frameworks: net10.0
#nullable enable
namespace Demo
{
public sealed class Thermometer : System.IComparable<global::Demo.Thermometer>
{
public string? Location { get => throw null; set { } }
public string Name { get => throw null; }
public event System.EventHandler? Changed;
public Thermometer(string name) { }
public int CompareTo(global::Demo.Thermometer? other) => throw null;
public double Read(global::Demo.Unit unit = global::Demo.Unit.Celsius) => throw null;
public void Rename(string name) { }
}
public enum Unit
{
Celsius = 0,
Fahrenheit = 1,
Kelvin = 10,
}
}The private field, the private setter and the internal method are not part of the public API, so they are not in the file. Method bodies are replaced by stubs, and the declarations are sorted. The API files describes the content of the files.
The file starts with a // <auto-generated/> comment, omitted in this example. The options remove it and split the API into several files.
Commit the API file
Commit the API file with the code. From then on, a pull request that changes the public API also changes this file, and its diff shows the change of the API, whatever the size of the change of the code.
Check the API file in CI
The file is written by the build, so a contributor who did not build before committing can forget to update it. In CI, check it instead of writing it:
dotnet build -p:PublicApiGeneratorVerifyNoChangeOnBuild=trueIn this mode, the build writes nothing, and fails when the API file is out of date. To fix it, build locally and commit the new file. See Check the API files in CI.
Next steps
- Generating API files in the build: all the MSBuild properties, multi-targeted projects.
- Command-line tool: generate the API files of any assembly without changing its project.
- Library: generate the files, or read the API model, from your own code.