pmbootstrap-meow/pmb/helpers/pmaports.py
Oliver Smith 933c4d0f0d new action: 'pmbootstrap repo_missing'
Add a new action that lists all aports, for which no binary packages
exist. Only list packages that can be built for the relevant arch
(specified with --arch). This works recursively: when a package can be
built for a certain arch, but one of its dependencies
(or their depends) can not be built for that arch, then don't list it.

This action will be used for the new sr.ht based build infrastructure,
to figure out which packages need to be built ahead of time (so we can
trigger each of them as single build job). Determining the order of the
packages to be built is not determined with pmbootstrap, the serverside
code of build.postmarketos.org takes care of that.

For testing purposes, a single package can also be specified and the
action will list if it can be built for that arch with its
dependencies, and what needs to be built exactly.

Add pmb/helpers/package.py to hold functions that work on both pmaports
and (binary package) repos - in contrary to the existing
pmb/helpers/pmaports.py (see previous commit) and pmb/helpers/repo.py,
which only work with one of those.

Refactoring:
* pmb/helpers/pmaports.py: add a get_list() function, which lists all
  aports and use it instead of writing the same glob loop over and over
* add pmb.helpers.pmaports.get(), which finds an APKBUILD and parses it
  in one step.
* rename pmb.build._package.check_arch to ...check_arch_abort to
  distinguish it from the other check_arch function
2018-12-01 21:30:59 +00:00

153 lines
5.1 KiB
Python

#!/usr/bin/env python3
"""
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/>.
"""
"""
Functions that work only on pmaports. See also:
- pmb/helpers/repo.py (only work on binary package repos)
- pmb/helpers/package.py (work on both)
"""
import glob
import logging
import os
import pmb.parse
def get_list(args):
""" :returns: list of all pmaport pkgnames (["hello-world", ...]) """
ret = []
for apkbuild in glob.glob(args.aports + "/*/*/APKBUILD"):
ret.append(os.path.basename(os.path.dirname(apkbuild)))
ret.sort()
return ret
def guess_main(args, subpkgname):
"""
Find the main package by assuming it is a prefix of the subpkgname.
We do that, because in some APKBUILDs the subpkgname="" variable gets
filled with a shell loop and the APKBUILD parser in pmbootstrap can't
parse this right. (Intentionally, we don't want to implement a full shell
parser.)
:param subpkgname: subpackage name (e.g. "u-boot-some-device")
:returns: * full path to the aport, e.g.:
"/home/user/code/pmbootstrap/aports/main/u-boot"
* None when we couldn't find a main package
"""
# Iterate until the cut up subpkgname is gone
words = subpkgname.split("-")
while len(words) > 1:
# Remove one dash-separated word at a time ("a-b-c" -> "a-b")
words.pop()
pkgname = "-".join(words)
# Look in pmaports
paths = glob.glob(args.aports + "/*/" + pkgname)
if paths:
logging.debug(subpkgname + ": guessed to be a subpackage of " +
pkgname)
return paths[0]
def find(args, package, must_exist=True):
"""
Find the aport path, that provides a certain subpackage.
If you want the parsed APKBUILD instead, use pmb.helpers.pmaports.get().
:param must_exist: Raise an exception, when not found
:returns: the full path to the aport folder
"""
# Try to get a cached result first (we assume, that the aports don't change
# in one pmbootstrap call)
ret = None
if package in args.cache["find_aport"]:
ret = args.cache["find_aport"][package]
else:
# Sanity check
if "*" in package:
raise RuntimeError("Invalid pkgname: " + package)
# Search in packages
paths = glob.glob(args.aports + "/*/" + package)
if len(paths) > 1:
raise RuntimeError("Package " + package + " found in multiple"
" aports subfolders. Please put it only in one"
" folder.")
elif len(paths) == 1:
ret = paths[0]
# Search in subpackages
if not ret:
for path_current in glob.glob(args.aports + "/*/*/APKBUILD"):
apkbuild = pmb.parse.apkbuild(args, path_current)
if (package in apkbuild["subpackages"] or
package in apkbuild["provides"]):
ret = os.path.dirname(path_current)
break
# Guess a main package
if not ret:
ret = guess_main(args, package)
# Crash when necessary
if ret is None and must_exist:
raise RuntimeError("Could not find aport for package: " +
package)
# Save result in cache
args.cache["find_aport"][package] = ret
return ret
def get(args, pkgname, must_exist=True):
""" Find and parse an APKBUILD file.
Run 'pmbootstrap apkbuild_parse hello-world' for a full output example.
Relevant variables are defined in pmb.config.apkbuild_attributes.
:param pkgname: the package name to find
:param must_exist: raise an exception when it can't be found
:returns: relevant variables from the APKBUILD as dictionary, e.g.:
{ "pkgname": "hello-world",
"arch": ["all"],
"pkgrel": "4",
"pkgrel": "1",
"options": [],
... }
"""
aport = find(args, pkgname, must_exist)
if aport:
return pmb.parse.apkbuild(args, aport + "/APKBUILD")
return None
def get_repo(args, pkgname, must_exist=True):
""" Get the repository folder of an aport.
:pkgname: package name
:must_exist: raise an exception when it can't be found
:returns: a string like "main", "device", "cross", ...
or None when the aport could not be found """
aport = find(args, pkgname, must_exist)
if not aport:
return None
return os.path.basename(os.path.dirname(aport))