[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Review of debputy editor provided docs for packagers



Justin B Rye:
Niels Thykier wrote:
I think I never got around to tackle the Multi-Arch understanding issues
that you had and my last attempt seems to have gotten lost in the mail ( I
do not see it on the mailing list). I am deliberately re-quoting a bit
further up for the full context with the goal of aiding you by not requiring
you to dig through old emails for context, should you be missing it.

Thanks...

Justin B Rye:
"    \"non-neutral Multi-Arch\" interface if their output is architecture dependent or if their dependencies\n"
"    force them out of the `foreign` role. The dependency issue usually happens when depending indirectly\n"
"    on an `Multi-Arch: allowed` package.\n"
"\n"
"    Some programs have \"Multi-Arch dependent interfaces\" and are not safe to declare as\n"
"    `Multi-Arch: foreign`. The name `foreign` refers to the fact that the package can satisfy relations\n"
"    for native*and foreign* architectures at the same time.\n"
"\n"
"  * `same` - The same version of the package can be co-installed for multiple architecture. However,\n"
No, sorry, I give up.  I thought I understood what this was saying,
but then I can't see how "co-installed" fits in.  If it's one single
version of one binary package that works for multiple architectures,
what does it mean to talk about it being "co-installed"?  What*with*?

I can at least diagnose a number error: you want "multiple
architectures".

A key feature of `Multi-Arch: same` is that you can simultaneously install
the same package (name + version) for different architectures on the system.

The trouble is, we also refer to these different things that can be
co-installed as (architecture-specific) "versions" of the package, so
"version" becomes ambiguous.  Lower down you use the word "variant",
which seems the neatest solution:

  "  * `same` - variants of this package for multiple architectures can be co-installed. However,\n"

As an example, suppose you wanted the amd64 variant (64-bit) of pkg-a and
the i386 variant (32-bit) of pkg-b with both of them depending on pkg-c.

If pkg-c is `Multi-Arch: same`, then what happens is that apt will install
the following packages:

  * pkg-c version X for i386
  * pkg-c version X for amd64
  * pkg-a version Y for amd64
  * pkg-b version Z for i386

In other words, you have pkg-c *twice* but each built for a different
architecture. The technical term used here is "co-installation" or
"co-installable" (see https://wiki.debian.org/Multiarch and
https://wiki.debian.org/Multiarch/HOWTO as existing literature that uses
"co-install*").

The way this works is that pkg-a (amd64) "sees" the amd64 variant as pkg-c
while pkg-b (i386) "sees" the i386 variant - that is, they see the pkg-c
built for the "same" architecture as package itself was built for.

"Variant" works for me; we might even say "build variant" the first
time we use the term, but that may be going too far.

"    for this to work, the package **must** ship all files in architecture unique paths (usually\n"
"    beneath `/usr/lib/${DEB_HOST_MULTIARCH}`) **or** have bit-for-bit identical content in files\n"
"    that are in non-architecture unique paths (e.g. `/usr/share/doc`). Note that these packages\n"
"    typically do not contain configuration files or `dpkg` `conffile`s.\n"
"\n"
"    The name `same` refers to the fact that the package can satisfy relations only for the \"same\"\n"
"    architecture as itself.  However, in this case, it is co-installable with itself as noted above.\n"
This makes no sense to me.

Even knowing what it means, talking about one thing being in the same
place as itself (and being the same as itself) still sounds like some
sort of tautology, and it doesn't help that it's completely normal for
a single package like libwww-perl_6.77-1_all.deb to be installed with
the same name and version "for multiple architectures" in the sense of
being installed on multiple machines.

So maybe

  "    The name `same` refers to the fact that the package can satisfy relations only for the same\n"
  "    architecture.  However, in this case, variants are co-installable as noted above.\n"

or

  "    The name `same` refers to the fact that the package can satisfy relations only for its own\n"
  "    architecture. However, in this case, variants are co-installable as noted above.\n"


It seems to be the exactly the same text on either side of that "or" (of the different is very subtle). For now, I have chosen the top one, but I am curious what the alternative was intended to be (or to be pointed out the difference, because I do not see it)

Hopefully, what I am getting at is more clear now. Another phrase used from
the wiki page above is:

"""
To enable more than one architecture version of a package to be installed at
the **same** time (generally libraries and dev- packages) files need to be
moved so they don't clash. These packages are marked 'Multi-Arch: same'.
"""

"Architecture version" would risk confusion with all the other uses of
"version"; "variant" avoids that.

My goal is that "hook" the term with something memorable that explains what
it does in a way that helps the reader remember it and tell it apart from
the others.
    I feel `same` here ends in the same problem we have with `any` vs. `all`
in the `Architecture` field, where people confuse the two.

The definition at https://wiki.debian.org/Glossary#same is

     As a [multiarch] classifier, indicates that a [package] does support
     the parallel installation of multiple versions with different
     [architectures], but cannot satisfy cross-architecture dependencies.

...which I *think* is saying the same thing but coming to it from the
opposite direction.  Given that most of that page was written by me I
must have understood it at some point.


I forgot we had a glossary and that it might have documented these things!

"\n"
"    Note: This value **cannot** be used with `Architecture: all`.\n"
"\n"
"  * `allowed` - **Advanced value**. This value is for a complex use-case that most people do not\n"
"    need. Consider it only if none of the other values seem to do the trick.\n"
"\n"
"    The package is **NOT** co-installable with itself but can satisfy Multi-Arch `foreign` and Multi-Arch\n"
"    `same` relations at the same. This is useful for implementations of scripting languages\n"
"    (e.g. Perl or Python). Here the interpreter contextually need to satisfy some relations as\n"
"    `Multi-Arch: foreign` and others as `Multi-Arch: same` (or `Multi-Arch: no`).\n"
I think that's "needs to satisfy", but if this is more complicated
than "same", I don't understand it and can't review it.


Once you understand "same" and "foreign", "allowed" is not that much more
difficult. However, it is almost never needed, so I am happy scaring people
away from it by default - especially to avoid people using "allowed" when
they should have picked one of the other values.

The version that doesn't trigger my overactive logic-problem detector
is:

  "    Variants of this package are **NOT** co-installable, but it can satisfy Multi-Arch `foreign`\n"
  "    and Multi-Arch `same` relations at the same time. This is useful for implementations of\n"
  "    scripting languages (e.g. Perl or Python), where the interpreter may need to satisfy some
  "    relations as `Multi-Arch: foreign` and others as `Multi-Arch: same` (or `Multi-Arch: no`).\n"


Thanks.

(Talking about satisfying a dependency as "Multi-Arch: no" is a bit
obscure, but I take it you just mean "the old-fashioned way" using
anarchic dependencies.)


On second thought, I dropped the `Multi-Arch: no` in the spirit of not mentally overloading the reader.

I think technically, `allowed` works like `foreign` or `no`. But it is easier to explain as `foreign` + `same` without the architecture variant co-installability (and it is also how others teach it). That might have been a mistake, but if so that is a windmill for someone else to fight.

To go over the Multi-Arch values with the example from before. Imagine you
want pkg-a (amd64) and pkg-b (i386) installed on the system. Both depend on
pkg-c to be installed.

  * If pkg-c is `Multi-Arch: no` (or the field is omitted), then this
    is not possible. The `apt` tool will abort with an error, and you
    are forced to pick either pkg-a or pkg-b, but you can never have
    both.

  * If pkg-c is `Multi-Arch: same`, then as explained above, you end
    up with pkg-a (amd64) + pkg-c (amd64) and pkg-b (i386) + pkg-c (i386)

  * If pkg-c is `Multi-Arch: foreign`, then you end up with
     - pkg-a (amd64)
     - pkg-b (i386)
     - pkg-c (amd64 OR i386)

    That is, you will have pkg-c installed. It might be built for amd64
    or for i386 (apt decides which you get), but it is only installed
    once. It is not possible to have pkg-c installed twice (a difference
    to `Multi-Arch: same`).

These are the 3 "simple" Multi-Arch values. The `allowed` comes with a
twist. Up till now, all the scenarios have been decided entirely based on
pkg-c and its Multi-Arch value. However, if pkg-c is `Multi-Arch: allowed`
then the scenario depends on pkg-a or/and pkg-b.

  Case 1) If pkg-a and pkg-b both have "Depends: pkg-c", then this ends
          up like the `Multi-Arch: no`-case.

  Case 2) If pkg-a and pkg-b both have "Depends: pkg-c:any", then this
          ends up like the `Multi-Arch: foreign`-case.

  Case 3) If pkg-a has "Depends: pkg-c:any" and pkg-b has
          "Depends: pkg-c", then you end up with:

          - pkg-a (amd64)
          - pkg-b (i386)
          - pkg-c (i386)

  Case 4) Case 3 except we swap the Depends lines around, then you get
          pkg-c (amd64)


With Case 3, pkg-a works with pkg-c as if pkg-c had `Multi-Arch: foreign`
due to the `:any` suffix in the Depends. Meanwhile, pkg-b works with pkg-c
as if pkg-c had `Multi-Arch: same`. Case 4 is conceptually the same only we
have to swap pkg-a and pkg-b around.

The part I had never considered before is that if my machine is amd64,
so I already have pkg-a:amd64 and pkg-c:amd64 and so on installed
before I try to install pkg-b:i386 (presumably because I need it to
support some legacy piece of 32-bit software), the dependencies in
case 3 would involve *uninstalling* pkg-c:amd64.


Correct, in that scenario, pkg-c:amd64 would be uninstalled and the i386 variant would be installed as a replacement.

"\n"
"    Typically, native extensions or plugins will need a `Multi-Arch: same`-relation as they only work with\n"
"    the interpreter compiled for the same machine architecture as themselves whereas scripts are usually\n"
"    less picky and can rely on the `Multi-Arch: foreign` relation.  Packages wanting to rely on the\n"
"    `Multi-Arch: foreign` interface must explicitly declare this adding a `:any` suffix to the package\n"
"    name in the dependency relation (such as `Depends: python3:any`).  However, the `:any` suffix cannot\n"
"    be used unconditionally and should not be used unless you know you need it.\n"
Should there be a "by" in "declare this by adding"?

Yes.


Thanks. It was already added, I forgot to remark it.

*Advanced question*: and should "a `:any` suffix" be "an `:any`
suffix"?


"Chefs choice?" :D

I have changed to "an" because I end up skipping the punctuation myself most
of the time. But if you read it as "colon any", then "a" would have been
correct!

Yes, for a lot of these things it's not clear how common it is to read
out (e.g.) "a slash-E.T.C.-slash-network-slash-interfaces file" or what.
Tragically, POSIX is silent about such matters.

Can we also appreciate how all configuration files are placed in "et cetera." folder? :D

"\n"
"    Note that depending indirectly on a `Multi-Arch: allowed` package can require a `Architecture: all` +\n"
"    `Multi-Arch: foreign` package to be converted to a `Architecture: any` package. This case is named\n"
"    the \"Multi-Arch interpreter problem\", since it is commonly seen with script interpreters. However,\n"
"    despite the name, it can happen to any kind of package. The bug [Debian#984701] is an example of\n"
"    this happen in practice.\n"
I'm fairly sure it's "a⁁n `Architecture: ..." (repeatedly).  Plus:

    "    this happening in practice.\n"

"\n"
"[Multi-Arch hinter]:https://wiki.debian.org/MultiArch/Hints\n";
"[Debian#984701]:https://bugs.debian.org/984701\n";
msgstr ""

#. [Synopsis] One-line description for the value "same" [Plaintext]. Shown
#. with completion (etc.)
#: src/debputy/lsp/data/debian_control_reference_data.yaml
msgctxt "Stanza:Package|Field:Multi-Arch"
msgid ""
"Co-installable with itself for different architectures (common for native"
"libraries)"
msgstr ""
I've bounced off "native libraries" before, and in this context I have
little hope that I'll guess what it means.

The packages that can be `Multi-Arch: same` is 99% "native libraries". These
are packages compiled into machine code (usually from C, C++, etc.) and are
therefore `Architecture: any` packages.

Native library is jargon for a library being compiled into native machine
language . So libc would be a native library. These would also be
`Architecture: any` (that is, they must be built for each architecture
separately).

On the other hand, libraries written in most scripting languages will be a
library but will not be native (machine code). These would generally be
Architecture: all (ignoring the special-cases). However, `Architecture: all`
packages *cannot* be `Multi-Arch: same`.

When I read "native" I tend to assume a contrast with "foreign", or
indeed "cross-architecture".  Not that I understand how packages like
libc6-arm64-cross work; they're full of compiled binary .so files
which you'd think would make them "native" and yet mysteriously they
count as "arch: all".  Anyway...


I see where you are coming from. The word `native` is also used for cross builds in Multi-Arch, where it has a slightly different meaning (You can have a `foo:native` rather than `foo:any` dependency).

Probably best to avoid `native` when we can, even if it is common jargon.

Surely what you mean is "arch-specific"?  A package like libwww-perl
isn't "coinstallable with itself" because it really is only one thing
with no build-variants, and installs to /usr/share/perl5/LWP.  But it
isn't "non-native" so much as "not only native".
In other words, "native (code) libraries" defines the subset of libraries
that could and typically are `Multi-Arch: same` in contract to "non-native
(code) libraries" that never is `Multi-Arch: same` (instead they tend to be
`Multi-Arch: foreign`)

You can find examples of native library packages on your system with the
command:

     dpkg -l | awk '{print $2}' | grep  '^lib.*:'

(it has a few false positives, but you should see a clear pattern of
`lib<name><version>:<architecture>` like liblz4-1:amd64 in the output)

On the flip side, non-native library packages are often named after the
language they are written in, such as `python3-X` or `libX-perl` to take the
two patterns I know best.

PS: In the previous case of me using "native libraries" (not quoted in this
email), the "native" part was irrelevant and I ended up removing it.

I'm really not convinced this use of "native" to mean "arch-specific"
is sensible technical jargon, but of course it's always possible that
it is nonethelesss standard technical jargon.

I am happy to change it to arch specific to avoid overloading the reader.

Reading a bit more on it, I think it might come from "native machine code", because I saw some "compile into native machine code" references along the way in other projects. Anyway, the etymology have to wait for another time. Avoiding "native" for now is fine.

For now, I have replaced all `native` (except when used in contrast to "foreign" architectures) with "architecture specific" (or "arch specific" when

#. [Hover Doc] Extended description for the value "same" [Markdown]. Shown in
#. hover docs (etc.)
#: src/debputy/lsp/data/debian_control_reference_data.yaml
#, python-brace-format
msgctxt "Stanza:Package|Field:Multi-Arch"
msgid ""
"The exact same version of the package can be co-installed for multiple architecture. However,\n"
Same old same old.  Oh, except that this version is even more
insistent that the things that can be installed together are exactly
one thing.  On multiple but unpluralised architecture.

So this should be something like
  "Variants of the package for multiple architectures can be co-installed. However,\n"


Thanks

"for this to work, the package*must* ship all files in architecture unique paths (usually\n"
"beneath `/usr/lib/<DEB_HOST_MULTIARCH>`) or have bit-for-bit identical content\n"
"in files that are in non-architecture unique paths (such as files beneath `/usr/share/doc`).\n"
"\n"
"The name `same` refers to the fact that the package can satisfy relations only for the `same`\n"
"architecture as itself.  However, in this case, it is co-installable with itself as noted above.\n"
"Note: This value **cannot** be used with `Architecture: all`.\n"
msgstr ""

  " The name `same` refers to the fact that the package can satisfy relations only for the same\n"
  " architecture. However, in this case, variants are co-installable as noted above.\n"

Alternatively (here and above)

  " The name `same` refers to the fact that the package can satisfy relations only for its own\n"
  " architecture. However, in this case, variants are co-installable as noted above.\n"


Thanks. Same as above with the same text in both cases. :)


#. [Synopsis] One-line description for the value "no" [Plaintext]. Shown with
#. completion (etc.)
#: src/debputy/lsp/data/debian_control_reference_data.yaml
msgctxt "Stanza:Package|Field:Multi-Arch"
msgid ""
"No Multi-Arch support (often used for \"reviewed and no support"
"possible/needed\")"
msgstr ""

#. [Hover Doc] Extended description for the value "no" [Markdown]. Shown in
#. hover docs (etc.)
#: src/debputy/lsp/data/debian_control_reference_data.yaml
#, python-brace-format
msgctxt "Stanza:Package|Field:Multi-Arch"
msgid ""
"The default. The package can be installed for at most one architecture at the time.  It can\n"
"*only* satisfy relations for the same architecture as itself. Note that `Architecture: all`\n"
"packages are considered as a part of the system's \"primary\" architecture (see output of\n"
"`dpkg --print-architecture`).\n"
"\n"
"Note: Despite the `no`, the package*can* be installed for a foreign architecture (as an example,\n"
"you can install a 32-bit version of a package on a 64-bit system).  However, packages depending\n"
"on it must also be installed for the foreign architecture.\n"
msgstr ""
Perhaps this needs the same fixes as above.  Perhaps I need to give up
reviewing this and get some sleep - I am after all very near the
halfway point.

Yes, it needs similar fixes - something like

  "The default. The package can be installed for at most one architecture at a time.  It can\n"
  "*only* satisfy relations for that one architecture. Note that `Architecture: all` packages\n"
  "are considered as a part of the system's \"primary\" architecture (see output of\n"
  "`dpkg --print-architecture`).\n"

(I was going to add "primary architecture" to my queue of words that
need definitions on wiki.d.o/Glossary, but while it definitely makes
sense I can't find evidence of anybody else using it...)

Thanks

I hope the examples above will help with this and the cases below. The rest
is just quoting for full context.

#. [Synopsis] One-line description for the value "foreign" [Plaintext]. Shown
#. with completion (etc.)
#: src/debputy/lsp/data/debian_control_reference_data.yaml
msgctxt "Stanza:Package|Field:Multi-Arch"
msgid ""
"Can satisfy dependencies for other architectures (common for data and some"
"scripts)"
msgstr ""

#. [Hover Doc] Extended description for the value "foreign" [Markdown]. Shown
#. in hover docs (etc.)
#: src/debputy/lsp/data/debian_control_reference_data.yaml
#, python-brace-format
msgctxt "Stanza:Package|Field:Multi-Arch"
msgid ""
"The package can be installed for at most one architecture at the time.  However, it can\n"
"satisfy relations for packages regardless of their architecture.  This is often useful for packages\n"
"solely providing data or binaries that have \"Multi-Arch neutral interfaces\".\n"
"\n"
"Sadly, describing a \"Multi-Arch neutral interface\" is hard and often only done by Multi-Arch\n"
"experts on a case-by-case basis.  Some programs and scripts have \"Multi-Arch dependent interfaces\"\n"
"and are not safe to declare as `Multi-Arch: foreign`.\n"
"\n"
"The name \"foreign\" refers to the fact that the package can satisfy relations for native\n"
"*and foreign* architectures at the same time.\n"
msgstr ""
As above, or before, or somewhere.

  "The package can be installed for at most one architecture at a time.  However, it can\n"
  ...

Hang on, why do we say "at most one" as if it made sense to think of a
package being installed for zero architectures at a time?  No, no,
that's definitely too pedantic.


It is a habit from my "Requirements Engineering" time, where you never used "one", because it could be read "exactly one" (1-1), "at most one" (0-1), "at least one" (1-N), etc. Given we have `same` being the `0-N` case, it made sense to me to ensure was always read as a `0-1` case.

Though, I am happy to admit that "at most one" is heavy handed.

#. [Synopsis] One-line description for the value "allowed" [Plaintext]. Shown
#. with completion (etc.)
#: src/debputy/lsp/data/debian_control_reference_data.yaml
msgctxt "Stanza:Package|Field:Multi-Arch"
msgid "Consumer decides whether it is same and foreign"
msgstr ""
Same*and* foreign?  I'm not even going to*try* to understand.

If this is "'allowed' in a nutshell" then presumably you wanted
     msgid "Consumer decides whether it is same or foreign"


Glad you caught that one.

#. [Hover Doc] Extended description for the value "allowed" [Markdown]. Shown
#. in hover docs (etc.)
#: src/debputy/lsp/data/debian_control_reference_data.yaml
#, python-brace-format
msgctxt "Stanza:Package|Field:Multi-Arch"
msgid ""
"**Advanced and very rare value**. This value is exceedingly rare to the point that less\n"
"than 0.40% of all packages in Debian used in it (May 2024). It is even rarer for for\n"
                                          use it (as of May 2024).

Is that an excess "for" or should it be "rarer than"?

Excess "for": M-A: allowed is even rarer for Arch: all packages (than
for Arch: specific ones), where the number is...
"`Architecture: all` packages, where the number an order of magnitude smaller. Unless\n"
                                                  ⁁is


I think I rewrote that snippet in between, because I cannot find the double "for" and the text looks a bit different now. The current text is:

                **Advanced and very rare value**. This value is exceedingly rare to the point that less
                than 0.40% of all packages in Debian used in it (May 2024). It is even rarer than
                `Architecture: all` packages, where the number is an order of magnitude smaller. Unless
                a Multi-Arch expert or the Multi-Arch hinter suggested that you use this value, for
                this package, then probably it is not the right choice.


"a Multi-Arch expert or the Multi-Arch hinter suggested that you use this value, for\n"
"this package, then probably it is not the right choice.\n"
"\n"
"The value means that the package is*not* co-installable with itself but can satisfy Multi-Arch\n"
"`foreign` and Multi-Arch `same` relations at the same time.  This is useful for implementations of\n"
"scripting languages (such as Perl or Python).  Here the interpreter contextually need to\n"
Number agreement: "needs".
"satisfy some relations as `Multi-Arch: foreign` and others as `Multi-Arch: same`.\n"

     "The value means that variants of this package are *not* co-installable, but it can satisfy\n"
     "Multi-Arch `foreign` and Multi-Arch `same` relations at the same time. This is useful for\n"
     "implementations of scripting languages (such as Perl or Python). Here the interpreter may need to\n"
     "satisfy some relations as `Multi-Arch: foreign` and others as `Multi-Arch: same`.\n"

"\n"
"Typically, native extensions or plugins will need a `Multi-Arch: same`-relation as they only\n"

For reference, this use of "native" has been replaced by "architecture specific" (per above).

"work with the interpreter compiled for the same machine architecture as themselves whereas\n"

Oh, now here "the same as themselves" is less weird, since you're
saying "they need something compiled for the same arch as (they were)
themselves (compiled for)".  I'd still suggest "compiled for their own
architecture", though.

"scripts are usually less picky and can rely on the `Multi-Arch: foreign` relation.  Packages\n"
"wanting to rely on the \"Multi-Arch: foreign\" interface must explicitly declare this adding a\n"
Missing "by", and should that be "an"?

Definite "by adding", optional "an `:any` suffix".
"`:any` suffix to the package name in the dependency relation (e.g. `Depends: python3:any`).\n"
"However, the `:any\"`suffix cannot be used unconditionally and should not be used unless you\n"
"know you need it.\n"
msgstr ""
[...]


The entire thing is now:

                **Advanced and very rare value**. This value is exceedingly rare to the point that less
                than 0.40% of all packages in Debian used in it (May 2024). It is even rarer than
                `Architecture: all` packages, where the number is an order of magnitude smaller. Unless
                a Multi-Arch expert or the Multi-Arch hinter suggested that you use this value, for
                this package, then probably it is not the right choice.

                The value means that variants of this package are *not* co-installable, but it can satisfy
Multi-Arch `foreign` and Multi-Arch `same` relations at the same time. This is useful for implementations of scripting languages (such as Perl or Python). Here the interpreter may need to
                satisfy some relations as `Multi-Arch: foreign` and others as `Multi-Arch: same`.

                Typically, architecture specific extensions or plugins will need a `Multi-Arch: same`-relation
                as they only work with the interpreter compiled for their own architecture whereas scripts are
                usually less picky and can rely on the `Multi-Arch: foreign` relation.  Packages wanting to rely
                on the "Multi-Arch: foreign" interface must explicitly declare this by adding
                an `:any` suffix to the package name in the dependency relation (e.g. `Depends: python3:any`).
                However, the `:any"`suffix cannot be used unconditionally and should not be used unless you
                know you need it.


Thanks for helping me with this. :)

Best regards,
Niels


Attachment: OpenPGP_signature.asc
Description: OpenPGP digital signature


Reply to: