When the native arch (e.g. `x86_64`) `APKINDEX` files are outdated, and
`pmbootstrap` gets instructed to build a linux package for a foreign
arch, then the `APKINDEX` cache did not get used anymore for the
current session. This means that every lookup of a package in an
`APKINDEX` caused the whole `APKINDEX` file to get parsed again instead
of using the cached version. This slowed it down so much that it felt
like `pmbootstrap` was looping forever.
How this happens in detail:
* Whenever pmbootstrap parses an `APKINDEX`, it fills up the
`args.cache["apkindex"]` dict with the parsed information and the
last modified date of the file.
* `pmbootstrap` checks the last modified date of the `APKINDEX` files
and updates them if they are older than 4 hours.
* When the bug appeared, then the cache was already filled up, then an
update happened and then `pmbootstrap` tried to read from the cache.
* So when reading from the `APKINDEX`, the cache gets ignored because
the last modified date is different.
* Up to this commit, the cache does not get deleted and filled up
again!
How to test:
Try these commands once without this commit, and then with this commit
applied:
```
$ sudo touch -m -t 201801010000 \
~/.local/var/pmbootstrap/cache_apk_x86_64/APKINDEX.*
$ pmbootstrap -v build linux-postmarketos-mainline --arch=armhf
```
Without the patch, you can see in `pmbootstrap log` that it is
resolving the dependencies properly, but very slowly. With the patch
the resolving happens almost instantly.
When parsing the depends of entries in the APKINDEX file, we ignore
all operators (<, =, >). (This is enough for our use case, was we only
do the dependency resolving to check which packages need to be built
and `apk` does the dependency resolving again before installing
anything).
We did not ignore the ~ character for fuzzy version compares, this is
fixed with this commit.
Since PR #1247 we are using the virtual package `.pmbootstrap` to mark
packages as not explcitly installed. In case `pmbootstrap` doesn't
finish the installation because of a bug, the `.pmbootstrap` virtual
package may not get uninstalled.
As virtual packages don't have the "timestamp" attribute set in the
package database (which indicates when the package was built), the
APKINDEX parser fails to parse them.
Changes:
* pmb.parse.apkindex.parse_next_block(): don't require the "timestamp"
attribute to be set (but the arch attribute instead, which is always
present)
* pmb.parse.apkindex.parse(): when a block does not have a `timestamp`
attribute, skip it, because it must be a virtual package.
* add test cases for both functions with a package database that
contains a virtual package.
`-m` is for deleting local compiled packages, for which there is no
aport with the same version. Prior to this change, this only worked
for packages where no aport exists, or for packages that are newer
than the aports.
That is, because we used the usual APKINDEX parsing logic, which
ignores old packages in the APKINDEX and only returns the one with the
highest version (that makes sense during dependency resolution).
Changes:
* New `pmb.parse.apkindex.parse_blocks()` function that returns a raw
list of blocks, instead of the dict with removed duplicates with
lower version you get from the usual `.parse()` function.
* Renamed each of the zap flags and their descriptions to make clear
what they are doing now.
```
short long (old) long (new)
-p --packages --pkgs-local
-m --mismatch-bins --pkgs-local-mismatch
-o, --old-bins --pkgs-online-mismatch
```
The message appears 20 or so times in a row when setting up a new
chroot. Right now we log it into the debug log (which is what
"pmbootstrap log" and "pmbootstrap --details-to-stdout" show). This
is annoying for normal pmbootstrap usage, and really clutters up the
Travis output.
This commit changes the log level to verbose, so it only shows up
when "-v" is passed to pmbootstrap.
* The APKINDEX parser used to return a dictionary with one package for
a given package name. This works for the installed packages database,
because there can only be one provider for a package. But when
parsing packages from binary repositories, we need to support
multiple providers for one package. It is now possible to get a
dictionary with either multiple providers, or just a single provider
for each package.
* Dependency parsing logic has been adjusted, to support multiple
providers. For multiple providers, the one with the same package
name as the package we are looking up is prefered. If there is none
(eg. "so:libEGL.so.1" is provided by "mesa-egl"), it prefers packages
that will be installed anyway, and after that packages that are
already installed. When all else fails, it just picks the first one
and prints a note in the "pmbootstrap log".
* Added testcases for all functions in pmb.parse.apkindex and
pmb.parse.depends
* pmbootstrap chroot has a new "--add" parameter to specify packages
that pmbootstrap should build if neccessary, and install in the
chroot. This can be used to quickly test the depencency resolution
of pmbootstrap without doing a full "pmbootstrap install".
Fixes#1122.
zap -m:
* APKINDEX parsing: parse the "origin" field as well, so we know
where a subpackage comes from
* pmbootstrap zap -m: properly delete all packages, that do not
have an aport or where the aport has another version. This also
works with subpackages now,
we use the origin field to resolve it.
* Only reindex when packages have been deleted in "zap -m"
zap in general:
* Show the amount of cleared up space after the deletion instead
of "Done"
* Print "Shutdown complete" to "pmbootstrap log" instead of stdout
(we need to call it twice during zap now to get the space
calculation right)
* Add `--dry` argument to `pmbootstrap zap` (this was very useful
for debugging) to list the packages/chroots that would get
deleted
* Roughly output the command that would get executed to delete
files, so it's obvious what's going on in --dry mode. (% rm ...)
Preparation for #1122.
* `pmb.parse.apkindex.parse()`, removed strict parameter: This used to raise
an exception when two entries in the apkindex provided the same package.
Turns out this is *not* invalid after all, two packages can provide the same
soname for example (e.g. libhybris, mesa-egl). In an APKINDEX, sonames are
listed as they were packages ("so:libjpeg.so.8" etc.).
* Remove `pmbootstrap challenge` leftover code from reproducible builds effort,
which was a dead end. This code uses the broken strict feature.
Fixes#955. Previously we did not look through all APKINDEXes while
looking for the package with a given name and the highest version.
This caused pmbootstrap to build packages even if they are in the
binary repo and up to date.
* Rename pmb/build/package.py to pmb/build/_package.py, so we can
access the functions it contains in testcases, and still use
pmb.build.package()
* Refactor the entire file. Instead of one big function that does
too many things, we have many small ones now, that are tested
in the testsuite and easier to modify
* Whenever building a package, pmbootstrap does not only build and
install the "makedepends" (like we did before), now it does the
same for the "depends". That's required to be compatible with
abuild. The old behavior can still be used with 'pmbootstrap
build --ignore-depends'.
* Because of that change, noarch packages can no longer be built in
the native chroot if we need them for a foreign chroot. A device-
package depending on a kernel would pull in the same kernel for
the native architecture otherwise.
* Running 'pmbootstrap build device-...' without '--ignore-depends'
and without a matching '--arch' displays a note that explains
this change to the user and tells how to use it instead.
* Noarch packages no longer get symlinked. That was only
implemented for packages built in the native chroot, and now that
is not always the case anymore. Symlinking these packages creates
packages with broken dependencies anyway (e.g.
device-samsung-i9100 can't be installed in x86_64, because
linux-samsung-i9100 is armhf only).
* Rename "carch" to "arch" wherever used. Naming it "carch"
sometimes is confusing with no benefit.
* Add a testcase for the aarch64 qemu workaround (because it failed
first and I needed to know for sure if it is working again).
* Improved some verbose logging, which helped with development of
this feature.
* Removed the old "build" test case (which was disabled in
testcases_fast.sh) as the new "build_package" test case covers its
functionallity.
* Only build indexes if the packages folder exists for that arch (Travis
couldn't run a test case otherwise)
* apkindex:
* Also parse the architecture field
* symlink_noarch_package:
* Renamed to symlink_noarch_packages
* Always work on all packages (so we don't need to guess which
subpackages have been generated after a certain build)
* Get invoked when running 'pmbootstrap index'
* Use 'apk index' to generate one index, where the architecture
does not get rewritten (abuild does that by default, due to
Alpine's repos not having a 'noarch' folder and diverging from
that doesn't make things easier for us). That goes super fast,
and then we know which packages are noarch packages and can
create the symlinks.
* Made output less verbose:
* Use -q for 'apk index' when calling it directly (when it gets
called by abuild we can't control that)
* Output that the APKINDEXes get reindexed only to the 'pmbootstrap
log'.
* Don't specify pkgnames from "provides" as dependencies
Always use the regular pkgname. That way, we avoid listing all
kinds of so: files as dependencies (because Alpine automatically
adds them as depends= to the package database). This fixes building
weston, and reproducing the build with `pmbootstrap challenge`.
Additional changes.
* Clear the parsed APKINDEX cache for the current pmbootstrap
session after building a package
* Avoid rebuilding a package, in case it was already built due to
circular dependencies
Previously, distutils.version.LooseVersion was used, because it was
sort of close enough to how Alpine parses versions.
This new version uses the exact same algorithm, as `apk` does, and
it passes *all* of `apk`'s testcases for version checking (previously
we simply skipped the ones, that did not pass).
* Remove pmb/helpers/version.py left-over (it is in parse now)
* Make asserts consistent, do not use unnecessary parenthesis
* Ignore `>`, `<`, `=` and `!` operators, when they are specified in
the dependencies. This was the desired behavior before, but it was
not implemented correctly (so it wouldn't ignore them everywhere).
Of course the real fix would be to honor these operators like apk
does. But this isn't feasible right now, and it should work for
most, if not all, our use-cases. I have documented this in the wiki
under build internals and if we happen to need correct operator
handling, we should do it then.
Minor other changes:
* `pmbootstrap parse_apkindex`: support optional package parameter to
only show the parsed content for one package.
* Support building most python APKBUILDs by replacing ${pkgname#py-}
properly
TLDR: Always rebuild/install packages when something changed when executing "pmbootstrap install/initfs/flash", more speed in dependency resolution.
---
pmbootstrap has already gotten some support for "timestamp based rebuilds", which modifies the logic for when packages should be rebuilt. It doesn't only consider packages outdated with old pkgver/pkgrel combinations, but also packages, where a source file has a newer timestamp, than the built package has.
I've found out, that this can lead to more rebuilds than expected. For example, when you check out the pmbootstrap git repository again into another folder, although you have already built packages. Then all files have the timestamp of the checkout, and the packages will appear to be outdated. While this is not largely a concern now, this will become a problem once we have a binary package repository, because then the packages from the binary repo will always seem to be outdated, if you just freshly checked out the repository.
To combat this, git gets asked if the files from the aport we're looking at are in sync with upstream, or not. Only when the files are not in sync with upstream and the timestamps of the sources are newer, a rebuild gets triggered from now on.
In case this logic should fail, I've added an option during "pmbootstrap init" where you can enable or disable the "timestamp based rebuilds" option.
In addition to that, this commit also works on fixing #120: packages do not get updated in "pmbootstrap install" after they have been rebuilt. For this to work, we specify all packages explicitly for abuild, instead of letting abuild do the resolving. This feature will also work with the "timestamp based rebuilds".
This commit also fixes the working_dir argument in pmb.helpers.run.user, which was simply ignored before.
Finally, the performance of the dependency resolution is faster again (when compared to the current version in master), because the parsed apkbuilds and finding the aport by pkgname gets cached during one pmbootstrap call (in args.cache, which also makes it easy to put fake data there in testcases).
The new dependency resolution code can output lots of verbose messages for debugging by specifying the `-v` parameter. The meaning of that changed, it used to output the file names where log messages come from, but no one seemed to use that anyway.
...instead of running apk every time to get the list of installed
packages and their versions. The internal package database from
apk has the same format, as the extracted APKINDEX file (except
that it has more key-value pairs, which we ignore/do not need
right now). So the APKINDEX code has been extended to parse both
tar-packed APKINDEX files and regular text files in the APKINDEX
format.
This is required for #108, for a better detection of outdated
packages (because the internal package database saves the
package's timestamp, too). A nice benefit is, that this is faster
than calling apk every time and it doesn't fill up the log as much.
I've also used this improved function for determining the apk
version (for the outdated version check), and I've deleted
pmb.parse.other.package_split(), as it is not needed anymore.
* New commandline parameter --mirror-pmOS, where the binary repository
URL for postmarketOS can be specified (empty by default as of now,
this will be filled with the real URL once the repo works)
* Do not build packages, when they are in the binary repository and
the version of the package in the binary repository is up-to-date.
* Add a testcase for pmb.build.is_necessary().
When an APKINDEX contained a package with multiple versions,
pmbootstrap did not use the last version to determine if the
package is out of date (regression from af8c9fcf5b).
Previously, it was only possible to get information about one
package inside the APKINDEX at a time.
This is needed for #64 to verify the APKINDEX.
Please note, that this implementation is actually slower, than
the previous one. But the code is more readable and it makes
caching possible (which will speed up the APKINDEX massively,
especially for the buildinfo.json file generation!)