Mono Packaging Guidelines

File Locations and Architectures

For a while, Fedora considered mono packages to be architecture-specific, and installed assemblies to %{_libdir}. However, after discussions with upstream, we now consider mono packages to be architecture (and platform) independent. This means that mono packages should be correctly installed into the GAC in in %{_monogacdir} or installed into /usr/lib/PACKAGENAME.

As a notable exception, any ELF binary libraries generated in a mono package must be correctly installed into %{_libdir}, because these files are architecture-specific.

Also, even though we consider mono packages to be architecture independent, they must not be marked as "noarch". Although the assemblies are the same, the files may differ due to strings referring to the build architecture.

In addition, because some architectures simply do not support mono, every mono package (either a library or a mono-using application) must include

ExclusiveArch: %{mono_arches}

The %mono_arches must be used instead of a list of architectures in order to facilitate changes to the architecture list.

gacutil in a spec file

gacutil is used to register dlls with mono (think of it as installing a library - which it is!).

When packaging any mono application which generates libraries which gacutil then registers (say mysql-connector-net), you need something like the following in the spec file

%install
mkdir -p %{buildroot}/%{_monogacdir}
gacutil -i bin/mono-1.0/release/MySql.Data.dll -f -package mysql-connector-net -root %{buildroot}/usr/lib

%files
%{_monogacdir}/MySql.Data
%{_monodir}/mysql-connector-net/

gacutil format

-i = input dll
-f = check references
-package = package name
-root = build root

RPMS and source

Don’t build RPMS against built from source versions of mono. It will work for you but probably not for other users!

While you may get away with recompiling the source for part of the overall package (such as gnome-panel is part of gnome or evolution-data-server is part of evolution) for other programs, you should not attempt this with Mono.

If you’re going to use the source, you MUST remove the RPMS first.

Compiling mono is not a trivial matter and may not even work (when you download the source, you must also make get-monolite-latest which grabs a version of the corelib and mcs which are need for compiling the main C# compilers - the monolite-latest does not always work and you end up without a working copy of Mono.

rpmlint and mono packages

rpmlint is a program that checks packages for common problems. For mono packages, some of the rpmlint messages can be disregarded.

Mono installs binaries in %{_libdir}//bin with symlinks back to /usr/bin. rpmlint is not happy with this and generates an error (which is the correct behaviour). It will also not recognise that mono libraries are not ELF format and may generate errors on this as well.

rpmlint will also pick up on any .pc file installed in the rpm (see below).

-devel packages

Mono packages must package .pc files in a -devel package, even if that is the only file that will be included. If we were to permit .pc files in non-devel packages, then we’ll have non-devel packages that depend on -devel packages, inflating the install needlessly.

Empty debuginfo

Sometimes building mono packages results in an empty debuginfo sub package, one without any files to install. See Packaging/Debuginfo

Incorrect Behaviours

Distributing Prebuilt Assemblies

Because mono .dlls are generally architecture independent, upstream may ship tarballs which install precompiled .dlls and .exes. All packages must build from source so the packager needs to watch out for these tarballs and be certain not to use them. (This can sneak in during upgrades as well, so the packager has to make sure they’re building from source every time the tarball is changed.)

Distributing .DLLs from other projects

The Mono project’s website makes this suggestion

Sometimes developers might want to distribute a library to other developers but they might not have a library that is API stable or has not matured enough over time to guarantee the backwards-compatibility of their libraries or they are not willing to maintain multiple packages of the various versions for users.
[...]
To solve this problem, we recommend that:

* The library developer ships a properly configured pkg-config file.
* The library consumers include an "update-libraries" target on their Makefile that will import the latest version of a library from a system directory into their application source code distribution.
* The library consumers ship this library as part of their package.

This suggestion may make it easier for applications targetting unstable library APIs but it is extremely poor practice. Using libraries in this manner has all the same problems as linking with static libraries, most notably that the application can suffer from security holes in the library long after it is fixed upstream. Mono applications in Fedora cannot include upstream DLLs (even if they are compiled from source). This is a blocker issue and must be fixed.

There are several techniques for detecting the presence of these libraries, none of them fool proof. If you know of a better method, please add it:

  1. Upstream tarball contains .dlls that were not rebuilt from source contained in the package.

  2. Look through the installed .dlls for any that have the same name as system .dlls or are suspiciously out of place (Package is myDiary and contains mysql.dll, sqlite.dll, and gtk-sharp.dll)

  3. Source directories look odd:

PKGNAME/
src/
data/
libs/
gtk-sharp/
atk-sharp/

Redefining _libdir

Packagers should avoid redefining _libdir in their spec file. Redefinition of this macro will cover up problems instead of helping fix them. Packagers should:

  1. Identify which directories the files should install into according to the Packaging Guidelines .

  2. Patch the packages build scripts to install to those locations.

  3. Identify places where the package has hardcoded the old locations instead of the new ones and fix those.

  4. Either report the issues to upstream or submit patches. Note that upstream projects are generally receptive to patches that allow package builders to redefine install locations at build time — less receptive to patches which change upstream’s hardcoded defaults to our hardcoded defaults.

Defining target

Was done for a brief period when we attempted to package mono apps as noarch. It was not necessary then (the actual fix was to stop using AC_CANONICAL_* in the configure.ac file) and it is definitely not needed now that we are no longer building noarch mono packages.

Glossary

  • AOT: Ahead Of Time. This usually refers to the ELF .so file that is the result of ahead of time compiling an assembly. AOTs are dependent on the assemblies they were generated from for certain data (unlike their equivalents in python and java). AOTs are created explicitly, not automatically.

  • Assembly: An assembly is the EXE or DLL file created by compiling a mono application. These are not the same as EXE’s or DLL’s created by compiling a C or C++ program on Windows. An assembly contains CIL code rather than machine code.

  • CIL: CIL stands for Common Intermediate Language. It is roughly equivalent to java bytecode and is generally portable across architectures. Some programming practices (calling out to native system libraries) can lead to CIL code that will not run on all architectures.

  • GAC: GAC stands for Global Assembly Cache. It is a machine-wide .NET assemblies cache.

  • Glue Libraries: Libraries which bridge a system library written in C or C++ with Mono. These wrappers are separate different than AOTs.