Remove rest of 'pmbootstrap challenge' left overs (#1173)

Follow up to #1162.

* `pmb.build.buildinfo()`: Used to record the build environment. It is
  flawed because it scans the repo APKINDEX files instead of using the
  actually installed packages list. When it was implemented we were not
  able to do the latter. After this is removed, `pmb.parse.depends` can
  be simplified (it needs to be rewritten for #1122).
* `pmb.helpers.repo.diff()` and `pmb.helpers.repo.files()`: These were
  used exclusively by `pmb.build.buildinfo()`, to learn about which
  files have been changed in the local repository folder after a
  package was built. The idea was, that we could find subpackages that
  way. But this information is present in the installed package list as
  well, which is a much cleaner approach.
This commit is contained in:
Oliver Smith 2018-02-01 22:03:21 +00:00 committed by GitHub
parent 987ec7a00a
commit e8c27795a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 5 additions and 216 deletions

View File

@ -21,7 +21,6 @@ import logging
import pmb.build
import pmb.build.autodetect
import pmb.build.buildinfo
import pmb.chroot
import pmb.chroot.apk
import pmb.chroot.distccd
@ -272,8 +271,7 @@ def run_abuild(args, apkbuild, arch, strict=False, force=False, cross=None,
return (output, cmd, env)
def finish(args, apkbuild, arch, output, strict=False, suffix="native",
buildinfo=False):
def finish(args, apkbuild, arch, output, strict=False, suffix="native"):
"""
Various finishing tasks that need to be done after a build.
"""
@ -282,13 +280,6 @@ def finish(args, apkbuild, arch, output, strict=False, suffix="native",
if not os.path.exists(path):
raise RuntimeError("Package not found after build: " + path)
# Create .buildinfo.json file (captures the build environment, from the
# reproducible builds approach in #64 that we aren't using anymore, but it
# might still be useful)
if buildinfo:
logging.info("(" + suffix + ") generate " + output + ".buildinfo.json")
pmb.build.buildinfo.write(args, output, arch, suffix, apkbuild)
# Clear APKINDEX cache (we only parse APKINDEX files once per session and
# cache the result for faster dependency resolving, but after we built a
# package we need to parse it again)
@ -301,15 +292,14 @@ def finish(args, apkbuild, arch, output, strict=False, suffix="native",
pmb.chroot.user(args, ["abuild", "undeps"], suffix, "/home/pmos/build")
def package(args, pkgname, arch=None, force=False, buildinfo=False,
strict=False, skip_init_buildenv=False):
def package(args, pkgname, arch=None, force=False, strict=False,
skip_init_buildenv=False):
"""
Build a package and its dependencies with Alpine Linux' abuild.
:param pkgname: package name to be built, as specified in the APKBUILD
:param arch: architecture we're building for (default: native)
:param force: even build, if not necessary
:param buildinfo: record the build environment in a .buildinfo.json file
:param strict: avoid building with irrelevant dependencies installed by
letting abuild install and uninstall all dependencies.
:param skip_init_buildenv: can be set to False to avoid initializing the
@ -340,5 +330,5 @@ def package(args, pkgname, arch=None, force=False, buildinfo=False,
# Build and finish up
(output, cmd, env) = run_abuild(args, apkbuild, arch, strict, force, cross,
suffix)
finish(args, apkbuild, arch, output, strict, suffix, buildinfo)
finish(args, apkbuild, arch, output, strict, suffix)
return output

View File

@ -1,74 +0,0 @@
"""
Copyright 2018 Oliver Smith
This file is part of pmbootstrap.
pmbootstrap is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
pmbootstrap is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
"""
import os
import json
import pmb.chroot
import pmb.chroot.apk
import pmb.parse.apkindex
import pmb.parse.depends
def generate(args, apk_path, arch, suffix, apkbuild):
"""
:param apk_path: Path to the .apk file, relative to the packages cache.
:param arch: Architecture, that the package has been built for.
:apkbuild: Return from pmb.parse.apkbuild().
"""
ret = {"pkgname": apkbuild["pkgname"],
"pkgver": apkbuild["pkgver"],
"pkgrel": apkbuild["pkgrel"],
"arch": arch,
"versions": {}}
# Add makedepends versions
installed = pmb.chroot.apk.installed(args, suffix)
relevant = (apkbuild["makedepends"] + [apkbuild["pkgname"], "abuild",
"build-base"])
relevant = pmb.parse.depends.recurse(args, relevant, arch, in_aports=False,
strict=True)
for pkgname in relevant:
if pkgname == apkbuild["pkgname"]:
continue
if pkgname in installed:
ret["versions"][pkgname] = installed[pkgname]["version"]
return ret
def write(args, apk_path, arch, suffix, apkbuild):
"""
Write a .buildinfo.json file for a package, right after building it.
It stores all information required to rebuild the package, very similar
to how they do it in Debian (but as JSON file, so it's easier to parse in
Python): https://wiki.debian.org/ReproducibleBuilds/BuildinfoFiles
:param apk_path: Path to the .apk file, relative to the packages cache.
:param arch: Architecture, that the package has been built for.
:apkbuild: Return from pmb.parse.apkbuild().
"""
# Write to temp
if os.path.exists(args.work + "/chroot_native/tmp/buildinfo"):
pmb.chroot.root(args, ["rm", "/tmp/buildinfo"])
buildinfo = generate(args, apk_path, arch, suffix, apkbuild)
with open(args.work + "/chroot_native/tmp/buildinfo", "w") as handle:
handle.write(json.dumps(buildinfo, indent=4, sort_keys=True) + "\n")
# Move to packages
pmb.chroot.root(args, ["chown", "pmos:pmos", "/tmp/buildinfo"])
pmb.chroot.user(args, ["mv", "/tmp/buildinfo", "/home/pmos/packages/pmos/" +
apk_path + ".buildinfo.json"])

View File

@ -144,7 +144,7 @@ def build(args):
for package in args.packages:
arch_package = args.arch or pmb.build.autodetect.arch(args, package)
if not pmb.build.package(args, package, arch_package, args.force,
args.buildinfo, args.strict):
args.strict):
logging.info("NOTE: Package '" + package + "' is up to date. Use"
" 'pmbootstrap build " + package + " --force'"
" if needed.")

View File

@ -16,7 +16,6 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
"""
import glob
import os
import hashlib
import logging
@ -24,52 +23,6 @@ import pmb.helpers.http
import pmb.helpers.run
def files(args):
"""
Returns all files (apk/buildinfo) with their last modification timestamp
inside the package repository, sorted by architecture.
:returns: {"x86_64": {"first.apk": last_modified_timestamp, ... }, ... }
"""
ret = {}
for arch_folder in glob.glob(args.work + "/packages/*"):
arch = os.path.basename(arch_folder)
ret[arch] = {}
for file in glob.glob(arch_folder + "/*"):
basename = os.path.basename(file)
ret[arch][basename] = os.path.getmtime(file)
return ret
def diff(args, files_a, files_b=None):
"""
Returns a list of files, that have been added or modified inside the
package repository.
:param files_a: return value from pmb.helpers.repo.files()
:param files_b: defaults to creating a new list
:returns: ["x86_64/APKINDEX.tar.gz", "x86_64/package.apk",
"x86_64/package.buildinfo", ...]
"""
if not files_b:
files_b = files(args)
ret = []
for arch in files_b.keys():
for file, timestamp in files_b[arch].items():
add = False
if arch not in files_a:
add = True
elif file not in files_a[arch]:
add = True
elif timestamp != files_a[arch][file]:
add = True
if add:
ret.append(arch + "/" + file)
return sorted(ret)
def hash(url, length=8):
"""
Generate the hash, that APK adds to the APKINDEX and apk packages

View File

@ -312,7 +312,6 @@ def arguments():
" APKBUILD)")
build.add_argument("--force", action="store_true", help="even build if not"
" necessary")
build.add_argument("--buildinfo", action="store_true")
build.add_argument("--strict", action="store_true", help="(slower) zap and install only"
" required depends when building, to detect dependency errors")
build.add_argument("-i", "--ignore-depends", action="store_true",

View File

@ -243,7 +243,6 @@ def test_finish(args, monkeypatch):
output = pmb.build.package(args, "hello-world", force=True)
# Disable effects of functions we don't want to test below
monkeypatch.setattr(pmb.build.buildinfo, "write", return_none)
monkeypatch.setattr(pmb.chroot, "user", return_none)
# Shortcut and fake apkbuild

View File

@ -18,91 +18,13 @@ along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
"""
import os
import sys
import pytest
import types
import time
# Import from parent directory
pmb_src = os.path.realpath(os.path.join(os.path.dirname(__file__) + "/.."))
sys.path.append(pmb_src)
import pmb.build
import pmb.helpers.logging
import pmb.helpers.repo
@pytest.fixture
def args(request):
import pmb.parse
sys.argv = ["pmbootstrap.py", "chroot"]
args = pmb.parse.arguments()
args.log = args.work + "/log_testsuite.txt"
pmb.helpers.logging.init(args)
request.addfinalizer(args.logfd.close)
return args
@pytest.fixture
def args_fake_work_dir(request, tmpdir):
args = types.SimpleNamespace()
args.work = str(tmpdir)
return args
def clear_timestamps_from_files(files):
"""
Replace all last modified timestamps from pmb.helpers.repo.files() with
None. The files-parameter gets changed in place.
"""
for arch in files.keys():
for file in files[arch].keys():
files[arch][file] = None
def test_files_empty(args_fake_work_dir):
args = args_fake_work_dir
os.mkdir(args.work + "/packages")
assert pmb.helpers.repo.files(args) == {}
def test_files_not_empty(args_fake_work_dir):
args = args_fake_work_dir
pkgs = args.work + "/packages"
for dir in ["", "armhf", "x86_64"]:
os.mkdir(pkgs + "/" + dir)
open(pkgs + "/x86_64/test", "a").close()
files = pmb.helpers.repo.files(args)
clear_timestamps_from_files(files)
assert files == {"armhf": {}, "x86_64": {"test": None}}
def test_files_diff(args_fake_work_dir):
args = args_fake_work_dir
# Create x86_64/test, x86_64/test2
pkgs = args.work + "/packages"
for dir in ["", "x86_64"]:
os.mkdir(pkgs + "/" + dir)
for file in ["x86_64/test", "x86_64/test2"]:
open(pkgs + "/" + file, "a").close()
# First snapshot
first = pmb.helpers.repo.files(args)
# Change: x86_64/test (set the lastmod timestamp 5 seconds in the future)
mtime_old = os.path.getmtime(pkgs + "/x86_64/test")
time_new = time.time() + 5
os.utime(pkgs + "/x86_64/test", (time_new, time_new))
mtime_new = os.path.getmtime(pkgs + "/x86_64/test")
assert mtime_old != mtime_new
# Create: aarch64/test3, x86_64/test4
os.mkdir(pkgs + "/aarch64")
open(pkgs + "/aarch64/test3", "a").close()
open(pkgs + "/x86_64/test4", "a").close()
diff = pmb.helpers.repo.diff(args, first)
assert diff == ["aarch64/test3", "x86_64/test", "x86_64/test4"]
def test_hash():
url = "https://nl.alpinelinux.org/alpine/edge/testing"
hash = "865a153c"