[m-dev.] Determining which grades an installed Mercury compiler supports
Keri Harris
keri at gentoo.org
Thu Jul 19 17:00:07 AEST 2018
On 18/07/2018 20:53, Zoltan Somogyi wrote:
>
>
> On Wed, 18 Jul 2018 18:28:48 +0200, Keri Harris <keri at gentoo.org> wrote:
>> I've run into a couple of interesting issues in how Mercury determines
>> which grades it supports.
>
> The Mercury compiler can generate code for any of its target languages
> without any outside help, so I presume you are talking about how
> it determines which grades have their libraries installed in the locations
> in which it expects, i.e. how it figures what to report when given
> --output-libgrades.
Yes, that's correct.
>> The erlang grade can have problems with this approach since init files
>> can be installed into the mercury/module/erlang directory. In
>> particular, a problem can arise when a 3rd-party library installs an
>> erlang init file, and then Mercury is later reinstalled without support
>> for the erlang grade; these residual 3rd-party init files can confuse
>> Mercury into believing that it still does support the erlang grade. The
>> following sequence of events leads to this:
>>
>> * compile and install Mercury package such that grade erlang is installed
>> - /usr/lib/mercury/modules/erlang/mer_std.init is created
>>
>> * compile and install mercury_fixed library from mercury/extras/fixed
>> - /usr/lib/mercury/modules/erlang/mercury_fixed.init is created
>>
>> * recompile and install Mercury such that grade erlang is no longer
>> installed
>> - /usr/lib/mercury/modules/erlang/ still exists
>
> Why does the package manager allow the installation of something that is
> *already* installed, without *uninstalling* first?
>
> From what I can see, if one installation overwrites another, the results
> are inherently fragile. Mercury installs are tested *only* in the context of a fresh,
> clean install; using any other kind of install will work only by luck. One could
> get around this by expending specific engineering effort to design (and later test)
> an install scheme that could survive specific kinds of partial installs, but
> in Mercury's case (and in most other cases) I don't think this would be
> worthwhile.
Package managers do uninstall old instances of packages whenever a
package is upgraded/reinstalled. Paradoxically the uninstall phase
always of an old package occurs *after* the install phase of the new
package.
Gentoo Linux is a bit particular in that it is a source distribution, so
in addition to installing the package it must first compile the package.
Installing mercury on Gentoo Linux involves the following steps:
1. compile mercury in a sandbox. If anything during the compile attempts
to write anywhere outside the sandbox, the compile is aborted.
2. install mercury in a sandbox. The install uses DESTDIR so that files
are installed into a safe location still within the sandbox. If the
install attempts to write anywhere outside the sandbox, the compile is
aborted.
(At this stage the old instance of mercury still resides on the live
filesystem and is still fully functional; the new instance of mercury
now resides at DESTDIR in the sandbox).
3. package manager copies mercury installation from sandbox to live
filesystem.
(At this stage the live filesystem contains a mix of both old and new
versions. Many files from the old instance will have been overwritten by
copies from the new instance; some cruft from the old instance may still
remain).
4. package manager deletes any stray files from old instance of package
that have not been overwritten by newer versions. All files within the
sandbox are then deleted.
(At this stage just the new instance of mercury exists on the live
filesystem; all files from the old instance have either been overwritten
in step #3 or deleted in step #4).
Package managers that are not source based (rpm, dpkg etc) essentially
just run steps #2, #3 and #4 where they extract files into a sandbox,
copy them to the live filesystem, and then clean up old files.
Installing the new instance of a package before uninstalling the old
instance is done for a number of reasons:
* there is (obviously) a gap between uninstalling a package and then
reinstalling that package. During this gap files from the package no
longer exist on the filesystem. If you are upgrading an important
package (e.g some system package like coreutils) then uninstalling the
package before the new version is installed will likely leave the system
in a non-functional state.
* if steps #1 or #2 of a package upgrade fail, you still have the old
version of the package in a functional state. (This is particularly
important for source-based distributions since compile-time failures are
possible).
So in the case of upgrading mercury via a package manager (or possibly
just reinstalling mercury to change the supported std library grades)
you're never left with non-directory files from an old instance of
mercury. Thus an install of mercury using a package manager gives you
the same result as if you had performed a clean install. (And behind the
scenes you actually are performing a clean install - into a safe
location in a sandbox).
The problem with the erlang grade is simply that when mercury is
reinstalled without the erlang grade, the package manager may decide not
to delete the mercury/modules/erlang directory since other packages
(e.g. mercury-extras) have placed their own files into this directory.
And since the mere existence of mercury/modules/erlang currently implies
that the erlang std library is installed we can eventually get into
trouble when compiling Mercury code.
I'm in agreement that Mercury shouldn't be expected to re-engineer
anything in order to follow some special install scheme. Handling cruft
from old installs is already part of the package manager's
responsibility. As an aside, the existing Mercury build system is very
good. Two strengths in particular stand out:
1. it uses sane defaults for things (CFLAGS, install locations etc)
2. so many things can be overridden via environment variables or
mmc/mmake arguments. (This is important since each OS or distribution
tends to have their own special policies that need to be followed).
>> I can work around the empty-directory problem easily enough by adding
>> zero-byte files into the empty modules/$grade directories when the
>> mercury compiler is installed. Then uninstalling any other package that
>> also owns the modules/$grade directories won't result in these
>> directories being deleted.
>
> That would fix this particular instance of this problem, but not other instances.
> I don't think anything would be guaranted to work short of
>
> - checking that every single file that is supposed to be installed is present,
> and has the expected cryptographic hash, and
> - checking that *only* the expected files are present in the install directories.
Package managers do keep track of which installed packages own which
files, and package managers prevent different packages from attempting
to install the same (non-directory) file. So upgrading or reinstalling
packages tends to go remarkably smoothly. Handling empty directories on
package uninstall is a corner case that some package managers have
trouble with.
>> Strictly speaking, these issues are more related to bundling Mercury
>> with package manager than being inherent to Mercury itself. But both of
>> these issues make me wonder if there is a more resilient way of
>> determining which grades the compiler supports.
>
> We could bake into the compiler the list of the grades that were configured
> to be installed at the time the compiler executable was itself created.
> However, that would still be vulnerable to parts of the install directory
> being overwritten later.
That would certainly work. Another option would be to follow the same
logic as that used for C grades - look for a uniquely identifiable file
belonging to the std library for each non-C grade.
Thanks
Keri
More information about the developers
mailing list