Archive:PackagingDrafts/Mono

From FedoraProject

Jump to: navigation, search
:Changelog for Revision: 0.2
:StartDate: June 25, 2006
:Description:
[1]  Remove erroneous advice to redefine _libdir.
[2]  Remove longer redefining _libdir section.
[3]  Add note about avoiding precompiled mono programs.
[4]  Add note to check for inclusion of upstream files.
[6]  Add a glossary.
[7]  Add a note about file locations.
- Better explanation of the non-noarch rule
[8]  Reword rpmlint section.
[9]  Add a deprecation section about _libdir and target.

Contents

Mono Packaging

Revision: 0.2
Last Revised: Thursday June 29, 2006


#!html
<div id="6">
<div class="addition">

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.
  • Glue Libraries: Libraries which bridge a system library written in C or C++ with Mono. These wrappers are separate different than AOTs.
#!html
</div>
<p class="justification">
Some terms from mono are confusing or have no meaning outside of mono.
</p>
</div>

Packaging Tips

#!html
<div id="7">
<div class="addition">

File Locations

Mono packages should install assemblies to %{_libdir} rather than /usr/lib or %{_datadir}. In most cases the preference is for %{_libdir}/PACKAGENAME. We use %{_libdir} because we do not consider mono packages to be noarch.

The main reason for this is that mono can ahead-of-time compile its assemblies into ELF shared objects. These AOTs have to exist in the same directory as their DLL/EXE counterparts otherwise mono cannot use them. Even if we, as packagers, choose not to create the AOT files when we build the mono rpms, the system administrator can choose to create them after install. Since there's no way to place the mono assemblies into an arch independent directory and the AOTs into arch dependent directories, the whole thing has to go into an arch dependent tree.

#!html
</div>
<p class="justification">
Because of the way mono discovers AOTs we cannot put AOTs into an arch independent area so %{_libdir}/PACKAGENAME is the preferred placement.
</p>
</div>

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
%{__rm} -rf %{buildroot}
%{__mkdir_p} %{buildroot}%{_prefix}/lib/mono/gac/
gacutil -i bin/mono-1.0/release/MySql.Data.dll -f -package mysql-connector-net -root %{buildroot}%{_prefix}/lib/

%files
%{_prefix}/lib/mono/gac/MySql.Data
%{_prefix}/lib/mono/mysql-connector-net/MySql.Data.dll

gacutil format

-i = input dll
-f = check references
-package = package name
-root = build root
#!html
<div id="1">
<div class="removal">

Stop (medium size).png /lib vs %{_libdir}

All mono packages are built to be installed in /usr/lib rather than %{_libdir}. This means that if you're using a 64 bit system, the dlls will be installed into the incorrect directory which may cause problems. These problems don't cause problems for the package itself. Instead they can cause packages which build against them to not find the mono libraries they provide. I have explained below a method for averting any problems while generating rpms.

#!html
</div>
<p class="justification">
Packages should be installing to /usr/lib on ix86 and /usr/lib64 on x86_64.  Redefining %{_libdir} addresses the wrong problem. Identify the real reasons the package will not build and run successfully when it installs to %{_libdir} and fix those issues.
</p>
</div>

RPMS and source

Stop (medium size).png 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.

Reporting Mono bugs

Upstream Mono's bugtracker is located at Ximian: [1]

#!html
<div id="2">
<div class="removal">

A simple fix for building RPMS

All mono packages need to be in /usr/lib rather than whereever %{_libdir} happens to point to. There is a simple method to ensure this happens. At the start of the spec file have the line

%define _libdir /usr/lib

(having %{prefix}/lib for some reason doesn't always work). You should then use the line

make DESTDIR=${RPM_BUILD_ROOT} install

In some circumstances, this system may not work, so revert to the method I've suggested above.

#!html
</div>
<p class="justification">
Packages should be installing to /usr/lib on ix86 and /usr/lib64 on x86_64.  Redefining %{_libdir} addresses the wrong problem. Identify the real reasons the package will not build and run successfully when it installs to %{_libdir} and fix those issues.
</p>
</div>

rpmlint and mono packages

#!html
<div id="8">
<div class="removal">

rpmlint is a package which ensures that there isn't a problem with rpms by checking the package for mistakes and reporting back. For mono packages though, it is not as useful.

#!html
</div>
<div class="addition">

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

#!html
</div>
<p class="justification">
We want people to know there are specific messages which can be disregarded, not that rpmlint is generally not useful for mono packages.
</p>
</div>

Mono installs binaries in /usr/lib/<package>/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.

Building Mono Packages Using mock with SELinux Enabled

See wiki:Self:Extras/MockTricks

Empty debuginfo

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

#!html
<div id="3">
<div class="addition">

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.)

#!html
</div>
<p class="justification">
This is an extension of the general no-binary blobs/everything must compile from source guidelines.
</p>
</div>
#!html
<div id="4">
<div class="addition">

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/
#!html
</div>
<p class="justification">This is a security issue just like using static libraries.
</p>
</div>
#!html
<div id="9">
<div class="addition">

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 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.

#!html
</div>
<p class="justification">
Redefining _libdir and target were done in the past to try to fix problems once mono packages were installed.  They are the wrong fix for the following reasons:
</p>
<ol class="justification">
<li>They redefine macros that are intended to be used informationally, not for changing how things are created.</li>
<li>They fix symptoms so that it's hard to figure out how to fix the real problem.</li>
<li>Changing libdir installs files to the wrong directory.</li>
</ol>
<p class="justification">
Outlining ways to correct and diagnose those deficiencies in packaging will help to solve the real root of the problem.
</p>
</div>