Build, Check and Publish Mono API Docs with mdoc

mdoc turns a .NET assembly into a documentation tree you can edit, validate and export. This guide walks the whole loop once so you never have to guess the order again: the commands here match the installed monodoc-base package version 6.8.0.105+dfsg-3.6ubuntu2, whose mdoc executable reports 5.7.4.9.

1. Check the tool and make a private workspace

Confirm the executable, then create a directory separate from your source tree. mdoc writes a directory of XML files, so keeping generated material in a disposable workspace makes review easier.

command -v mdoc
mdoc --version
mkdir -p "$PWD/mdoc-work/docs/en"

On this installation, the version command prints mdoc 5.7.4.9. If mdoc --version is not available on another release, use dpkg-query -W -f='${Version}\n' monodoc-base on Debian or Ubuntu systems. No elevated privileges are needed here.

Checkpoint: You should have an empty mdoc-work/docs/en directory and a working mdoc command.

2. Generate or update the XML stubs

Run mdoc update with the assembly and an output directory. It creates new stubs, or updates existing ones while preserving documentation you have already written. It does not normally pull documentation from source comments; the --import switch is the explicit route for a C# or ECMA XML documentation file.

mdoc update \
  --out "$PWD/mdoc-work/docs/en" \
  /path/to/ProjectName.dll

The generated tree normally contains index.xml, namespace files such as ns-ProjectName.xml, and one XML file per type below namespace directories. Check the result without changing it:

find mdoc-work/docs/en -type f -name '*.xml' -print | sort

Those filters are for investigation, not for generating documentation: a partial run is not a complete documentation set.

3. Edit only writer-owned fields

Open the generated XML and write the children of <Docs>: usually <summary>, <remarks>, <param>, <returns>, <example>, and <exception>. Type and member signatures, assembly information, interfaces, parameter metadata and member names are maintained by mdoc; leave them alone, or a later update can overwrite or conflict with your hand edits.

A method's documentation, for example, can carry a short summary and a parameter description:

<Docs>
  <summary>Reads the next record from the input stream.</summary>
  <param name="buffer">The buffer that receives the record.</param>
  <returns>The number of bytes read.</returns>
</Docs>

XML syntax matters here. Escape a literal ampersand as &amp;, and use &lt; and &gt; for angle brackets in code or prose. Use <see cref="..." /> for links to API members: the cref value takes a prefix such as T: for a type, M: for a method, or P: for a property. Generic names use the documented backtick and parameter-count form, for example T:System.Collections.Generic.List`1.

Tip: do not hand-edit index.xml to add a type or namespace. mdoc maintains its assembly and type index from the assembly itself; put explanatory text in the relevant Docs element instead.

4. Validate before generating output

Validation checks the whole ECMA-format directory, including index.xml, namespace files and type files. Run it after every update and after meaningful XML edits:

mdoc validate "$PWD/mdoc-work/docs/en"

A successful run returns to the shell without a validation error. If it reports malformed XML or schema problems, fix the named file and repeat the command. Do not skip this checkpoint: export and assembly can produce output that is syntactically usable but still incomplete or inconsistent.

The XML layout is defined by mdoc(5). In brief, index.xml records assemblies, namespaces and types; ns-*.xml holds namespace text; and Namespace/Type.xml holds type and member text. Nested types get their own separate type files, not an XML <Type> element nested inside another type.

5. Choose an output path

For a plain directory of HTML files, export the validated XML to a separate destination:

mkdir -p "$PWD/mdoc-work/html"
mdoc export-html \
  --out "$PWD/mdoc-work/html" \
  "$PWD/mdoc-work/docs/en"
find mdoc-work/html -type f -name '*.html' -print | sort

By default, mdoc only regenerates an HTML file when its XML source is newer. Add --force-update when deliberately rebuilding every file. To restrict output, use one or more --with-version=VERSION or --with-profile=PROFILE options; without those filters, every version represented in the documentation is included.

For the monodoc browser, assemble a prefix instead of an HTML directory:

mdoc assemble \
  --out "$PWD/mdoc-work/ProjectName" \
  "$PWD/mdoc-work/docs/en"
ls -l mdoc-work/ProjectName.tree mdoc-work/ProjectName.zip

The command creates ProjectName.tree and ProjectName.zip. A matching ProjectName.source description is also needed to place the source in the browser tree; the assemble man page documents its XML shape and installation. The --format=ecma default is right for the XML that mdoc update produces.

6. Install a source only when you mean to

Installing assembled files changes the system's Mono documentation catalogue and needs elevated privileges. Review the files first. The man page's installation pattern copies the source, tree and zip files to the directory returned by pkg-config:

pkg-config monodoc --variable=sourcesdir

Warning: the copy itself is an administrative action. Do not run it against a production machine until the documentation has been reviewed and the destination confirmed. Keep a backup of any existing files with the same prefix so undoing the installation is a straightforward restore. If you only need a web preview, use export-html and avoid system changes altogether.

Common failure modes

Done means