Generating API files

edit

Generate the files in memory

PublicApi.Generate returns the API files of an assembly, without writing anything:

var files = PublicApi.Generate(
    assemblyPath: typeof(PublicApi).Assembly.Location,
    options: new PublicApiOptions
    {
        FileLayout = PublicApiFileLayout.OneFilePerNamespace,
        IncludeAutoGeneratedComment = false,
    });

// The assembly declares a single namespace
var file = Assert.Single(files);
Assert.Equal("Meziantou.Framework.PublicApiGenerator.g.cs", file.RelativePath);
Assert.Contains("public static class PublicApi", file.Content);

Each PublicApiFile has a RelativePath, which depends on the file layout, and a Content. Use them to write the files where you want, or to compare them with existing files.

Write the files to a directory

PublicApi.GenerateToDirectory writes the files to a directory, and creates it when needed:

PublicApi.GenerateToDirectory(
    assemblyPath: typeof(PublicApi).Assembly.Location,
    outputDirectory: outputDirectory,
    options: new PublicApiOptions { FileLayout = PublicApiFileLayout.OneFilePerType });

Assert.True(File.Exists(Path.Combine(outputDirectory, "Meziantou.Framework.PublicApiGenerator.PublicApi.g.cs")));

Existing files are overwritten, and files that the generator does not write are left in place: delete the directory first to remove the files of types that no longer exist.

Read a file or a loaded assembly

Both methods accept the path of a .dll or .exe, or an Assembly that is already loaded. A path is read with its metadata only: the assembly is never loaded, and none of its code runs. Both give the same files:

var fromMetadata = PublicApi.Generate(typeof(PublicApi).Assembly.Location); // Reads the file, never loads it
var fromReflection = PublicApi.Generate(typeof(PublicApi).Assembly);        // Uses the loaded assembly

Assert.Equal(fromMetadata, fromReflection);

Multi-targeted libraries

Pass the builds of all the target frameworks to merge them into one set of files, with #if blocks for the declarations that differ (see Multi-targeted libraries):

var files = PublicApi.Generate(
    assemblySources:
    [
        "/path/to/MyLibrary.netstandard2.0.dll",
        "/path/to/MyLibrary.net10.0.dll",
    ]);

The items are AssemblySource values: a path or an Assembly converts to one implicitly. The target framework of each build is read from its TargetFrameworkAttribute. To set it explicitly, create the AssemblySource with a target framework moniker:

var files = PublicApi.Generate(
    assemblySources:
    [
        new AssemblySource("/path/to/MyLibrary.netstandard2.0.dll", ".NETStandard,Version=v2.0"),
        new AssemblySource("/path/to/MyLibrary.net10.0.dll", ".NETCoreApp,Version=v10.0"),
    ]);

When the moniker is null or empty, it is inferred from the assembly. Generate throws an ArgumentException when the list is empty, when two builds have the same target framework, or when a target framework cannot be converted to a preprocessor symbol.

Options

PublicApiOptions has two properties:

PropertyDefaultDescription
FileLayoutPublicApiFileLayout.SingleFileSingleFile, OneFilePerNamespace or OneFilePerType. See File layouts.
IncludeAutoGeneratedCommenttrueStarts each file with a // <auto-generated/> comment.

When two files would have the same name, ignoring case, Generate throws an InvalidOperationException instead of losing part of the API.