Compile a Mono .licenses Resource with lc

You will turn a Mono licenses.licx file into the .licenses resource your target assembly needs before it will embed licensed controls. This guide uses lc from Mono 6.8.0.105, installed here as package version 6.8.0.105+dfsg-3.6ubuntu2.

Allow about fifteen minutes. You need a shell, Mono's development tools, a license list, and the assemblies for the licensed controls named in that list. The examples only write under an explicit output directory. They do not install anything, modify a system service, or need root.

1. Check the installed compiler

Confirm which executable will run and read its local usage text:

$ command -v lc
/usr/bin/lc
$ dpkg-query -W -f='${Package} ${Version}\n' mono-devel
mono-devel 6.8.0.105+dfsg-3.6ubuntu2
$ lc --help
Mono License Compiler
Copyright (c) 2009 by RemObjects Software

lc -c filename -t targetassembly [-i references] [-v] [-o] [-nologo]

Options:
  -v, --verbose              Verbose output
  -t, --target=VALUE         Target assembly name
  -c, --complist=VALUE       licx file to compile
  -i, --load=VALUE           Reference to load
  -o, --outdir=VALUE         Output directory for the .licenses file
      --nologo               Do not display logo
  -h, -?, --help             Show help

Checkpoint: The important inputs are an existing license list, a target assembly name, and an output directory that already exists. The compiler does not create a missing output directory.

2. Understand the license list

Create or locate the file normally called licenses.licx. Each non-comment line names a licensed type and its assembly. The installed manual documents two forms:

# comment
Vendor.Controls.PremiumGrid, Vendor.Controls
Vendor.Controls.PremiumGrid, Vendor.Controls, Version=1.2.3.4, Culture=neutral, PublicKeyToken=0123456789abcdef

Lines beginning with # are ignored. The short assembly form needs the assembly to be loaded with -i or --load. The full form includes the locale, version and public key token, so it must match the assembly identity exactly. Do not guess these values: obtain them from the vendor assembly or the build that already succeeds.

A license list is executable build input in the broad sense: lc tries to load the named types. Treat it as reviewed source. If the file came from elsewhere, inspect it before running the command.

3. Produce the resource

Run the compiler with the target assembly name that your build will eventually contain. Replace the capitalised placeholders with real paths and names:

$ mkdir -p build/licenses
$ lc --nologo \
    --complist path/to/licenses.licx \
    --target MyApplication.exe \
    --load path/to/Vendor.Controls.dll \
    --outdir build/licenses

-c, -t, -i and -o are the short equivalents. Pass every non-GAC assembly needed to resolve the types with a separate -i option. The target is the full assembly name with its file extension, not the path to the output directory.

The output filename is fixed by that target: MyApplication.exe.licenses in build/licenses. It is not the same as the input filename. --nologo suppresses the normal logo; it does not change the generated resource.

Checkpoint: Verify the expected file before handing it to the next build step:

$ test -f build/licenses/MyApplication.exe.licenses
$ printf 'resource ready: %s\n' build/licenses/MyApplication.exe.licenses
resource ready: build/licenses/MyApplication.exe.licenses

4. Test the command without a vendor control

When you are checking paths or CI wiring, an empty input is a safe smoke test. It produces a valid resource container without trying to resolve a licensed type:

$ mkdir -p /tmp/lc-smoke
$ lc --nologo -c /dev/null -t SmokeTest.exe -o /tmp/lc-smoke
$ test -f /tmp/lc-smoke/SmokeTest.exe.licenses
$ printf 'resource exists: %s\n' /tmp/lc-smoke/SmokeTest.exe.licenses
resource exists: /tmp/lc-smoke/SmokeTest.exe.licenses

A zero exit status and the presence of SmokeTest.exe.licenses are the useful checks. This does not prove that a real vendor type can be resolved or that the resource is embedded by your compiler.

For more detail, add --verbose. On the installed version it reports the input, output filename and a saved message, then returns status 0 for the empty-input test:

$ lc --verbose --nologo -c /dev/null -t SmokeTest.exe -o /tmp/lc-smoke
Input file: /dev/null
Output filename: /tmp/lc-smoke/SmokeTest.exe.licenses
Saved to: /tmp/lc-smoke/SmokeTest.exe.licenses

5. Diagnose the common failures

If the output directory does not exist, lc fails while opening the resource. Create the directory first, then rerun the same command. Do not redirect the output to a path that happens to be a file.

If a type cannot be loaded, check the spelling of the namespace and type, then check its assembly identity. Add the containing DLL with --load when the assembly is not in the Global Assembly Cache. Multiple references are allowed:

$ lc --nologo \
    --complist path/to/licenses.licx \
    --target MyApplication.exe \
    --load path/to/Vendor.Controls.dll \
    --load path/to/Vendor.Common.dll \
    --outdir build/licenses
$ printf 'lc exit status: %s\n' "$?"
lc exit status: 0

Capture the status immediately. A later printf or test would replace lc's status. If the command fails, keep the diagnostic output and fix the input or reference rather than ignoring the failure and embedding an old resource.

A missing reference can also be caused by a short assembly name in the license list when the runtime needs the full identity. In that case use the full line supplied by the component vendor, or make sure the exact assembly is the one loaded with --load. Do not edit a public key token or version merely to make a line look plausible.

6. Put the generated file into the build

The .licenses file is an intermediate resource. Your subsequent compiler step must embed MyApplication.exe.licenses into the assembly whose name was supplied to lc. Keep those names aligned: if the final assembly is renamed, regenerate the resource with the new target name.

The example has changed only the chosen output directory. To undo it, remove the generated file from that build directory using your normal build-clean command, or delete the disposable smoke-test directory after checking its contents. Do not remove a shared build directory without confirming that no other artefacts are stored there.

Done means