Mono's al command bolts an assembly manifest onto a bare .netmodule, turning a compiler's leftover into a library the runtime will load. The examples use al from mono-devel version 6.8.0.105+dfsg-3.6ubuntu2. Allow about fifteen minutes if Mono and a compiler are already installed.
This is a developer tool, so the normal commands do not need sudo. You need a shell, al, and one or more module files. The linker combines modules and resources into an assembly manifest; it is not monolinker, which trims assemblies down to the code they actually use.
Confirm the command and package are actually the ones you expect. Read-only, so fire away:
$ command -v al
/usr/bin/al
$ dpkg-query -W -f='${Package} ${Version}\n' mono-devel
mono-devel 6.8.0.105+dfsg-3.6ubuntu2
$ al --help | head -3
Mono Assembly Linker (al.exe) version 6.8.0.105
Usage: al [options] [sources]
Options: ('/out' must be specified)
Checkpoint: the installed help uses slash-prefixed spellings such as /out, while the local manpage documents the equivalent hyphen form such as -out. Both forms worked in the installed version, a small leftover of al's Windows ancestry. Pick one style and use it consistently in scripts.
Already have a module? Skip to step 3. Otherwise, compile this harmless class as one. Everything the compiler writes stays under a temporary directory:
$ work=$(mktemp -d /tmp/al-guide.XXXXXX)
$ printf '%s\n' 'public class Greeter { public static string Message() { return "linked"; } }' > "$work/Greeter.cs"
$ mcs -target:module -out:"$work/Greeter.netmodule" "$work/Greeter.cs"
$ file "$work/Greeter.netmodule"
/tmp/al-guide.XXXXXX/Greeter.netmodule: PE32 executable (DLL) (console) Intel 80386 Mono/.Net assembly, for MS Windows, 2 sections
The random directory name and exact file description will vary. What matters is that the compiler exits successfully and the .netmodule exists. A module carries metadata but no assembly manifest, and that manifest is exactly the part al bolts on next.
Give al an output name, a target kind and at least one source. Everything it changes stays inside the temporary directory:
$ al -target:library -out:"$work/Greeter.dll" "$work/Greeter.netmodule"
$ printf 'al status: %s\n' "$?"
al status: 0
$ file "$work/Greeter.dll"
/tmp/al-guide.XXXXXX/Greeter.dll: PE32 executable (DLL) (console) Intel 80386 Mono/.Net assembly, for MS Windows, 3 sections
-target:lib is the short target spelling. The other documented targets are exe for a console executable and win or winexe for a Windows executable. An output path is never optional: the installed help demands /out outright, and a missing source gets you an error rather than a quietly empty assembly.
Use Mono's disassembler to confirm the result actually has an assembly manifest. Nothing here executes the linked code:
$ monodis --assembly "$work/Greeter.dll"
Assembly Table
Name: Greeter
Hash Algoritm: 0x00008004
Version: 0.0.0.0
Flags: 0x00000000
PublicKey: BlobPtr (0x00000000)
Zero sized public key
Yes, Hash Algoritm is spelled that way, and it belongs to this installed monodis version, not a typo you introduced. The checks that matter are the assembly name, the version and a successful command. Need a particular version? Pass -v:1.2.3 to al; the manpage also allows * to auto-generate the remaining version numbers.
Response files keep a long command readable, and al accepts them with @filename. Put one option or source per line:
$ printf '%s\n' \
'-target:library' \
"-out:$work/Response.dll" \
"$work/Greeter.netmodule" > "$work/al.rsp"
$ al "@$work/al.rsp"
$ test -s "$work/Response.dll" && printf '%s\n' 'response-file link OK'
response-file link OK
Warning: keep response files private when they name a signing key path or other sensitive build details, and never put a private strong-name key itself inside one. For signing, -keyfile:FILE expects a strong-name key file, and a full key pair is required unless you deliberately opt into delay signing. Treat signing as a release step, not something you bolt on while troubleshooting.
A non-zero result usually means the input or a required option is wrong. Re-run with -fullpaths if an error names a file ambiguously. Check that every source exists, that -out names a writable destination, and that the target matches the intended output.
Do not add -base just to silence an error: the local manpage flags base-address support as unimplemented. Likewise, do not confuse al with monolinker, and do not assume a successful manifest build has merged the module's contents into a single native binary. The resulting assembly still leans on the managed inputs and runtime conventions of the application it ends up in.
Recovery: if a link overwrote an output you still needed, stop using that path and restore it from your build artefact or source-control copy. The examples above use fresh temporary names, so there is no persistent undo operation to run here.
al reports the expected Mono version and accepts the selected option style..netmodule was supplied along with an explicit output path.monodis --assembly confirmed the output manifest.