Add support for composer.

This commit is contained in:
Jelmer Vernooij 2021-03-25 19:06:00 +00:00
parent f75d4b6cb3
commit 0688a0cbba
No known key found for this signature in database
GPG key ID: 579C160D4C9E23E8
4 changed files with 97 additions and 8 deletions

View file

@ -40,6 +40,7 @@ from .requirements import (
CargoCrateRequirement,
RPackageRequirement,
OctavePackageRequirement,
PhpPackageRequirement,
)
from .fix_build import run_with_build_fixers
from .session import which
@ -98,6 +99,16 @@ class BuildSystem(object):
return None
def xmlparse_simplify_namespaces(path, namespaces):
import xml.etree.ElementTree as ET
namespaces = ['{%s}' % ns for ns in namespaces]
tree = ET.iterparse(path)
for _, el in tree:
for namespace in namespaces:
el.tag = el.tag.replace(namespace, '')
return tree.root
class Pear(BuildSystem):
name = "pear"
@ -130,6 +141,32 @@ class Pear(BuildSystem):
self.setup(resolver)
run_with_build_fixers(session, ["pear", "install", self.path], fixers)
def get_declared_dependencies(self, session, fixers=None):
path = os.path.join(self.path, "package.xml")
import xml.etree.ElementTree as ET
try:
root = xmlparse_simplify_namespaces(path, [
'http://pear.php.net/dtd/package-2.0',
'http://pear.php.net/dtd/package-2.1'])
except ET.ParseError as e:
logging.warning('Unable to parse package.xml: %s', e)
return
assert root.tag == 'package', 'root tag is %r' % root.tag
dependencies_tag = root.find('dependencies')
if dependencies_tag is not None:
required_tag = root.find('dependencies')
if required_tag is not None:
for package_tag in root.findall('package'):
name = package_tag.find('name').text
min_tag = package_tag.find('min')
max_tag = package_tag.find('max')
channel_tag = package_tag.find('channel')
yield "core", PhpPackageRequirement(
name,
channel=(channel_tag.text if channel_tag else None),
min_version=(min_tag.text if min_tag else None),
max_version=(max_tag.text if max_tag else None))
@classmethod
def probe(cls, path):
if os.path.exists(os.path.join(path, "package.xml")):
@ -1214,6 +1251,23 @@ class Cabal(BuildSystem):
return cls(os.path.join(path, "Setup.hs"))
class Composer(BuildSystem):
name = "composer"
def __init__(self, path):
self.path = path
def __repr__(self):
return "%s(%r)" % (type(self).__name__, self.path)
@classmethod
def probe(cls, path):
if os.path.exists(os.path.join(path, "composer.json")):
logging.debug("Found composer.json, assuming composer package.")
return cls(path)
class PerlBuildTiny(BuildSystem):
name = "perl-build-tiny"
@ -1254,7 +1308,7 @@ BUILDSYSTEM_CLSES = [
Pear, SetupPy, Npm, Waf, Cargo, Meson, Cabal, Gradle, Maven,
DistZilla, Gem, PerlBuildTiny, Golang, R, Octave,
# Make is intentionally at the end of the list.
Make, RunTests]
Make, Composer, RunTests]
def scan_buildsystems(path):

View file

@ -69,21 +69,34 @@ def run_detecting_problems(session: Session, args: List[str], **kwargs):
def run_with_build_fixers(session: Session, args: List[str], fixers: List[BuildFixer], **kwargs):
fixed_errors = []
while True:
to_resolve = []
try:
run_detecting_problems(session, args, **kwargs)
except DetailedFailure as e:
to_resolve.append(e)
else:
return
while to_resolve:
e = to_resolve.pop(-1)
logging.info("Identified error: %r", e.error)
if e.error in fixed_errors:
logging.warning(
"Failed to resolve error %r, it persisted. Giving up.", e.error
)
raise DetailedFailure(e.retcode, args, e.error)
if not resolve_error(e.error, None, fixers=fixers):
logging.warning("Failed to find resolution for error %r. Giving up.", e.error)
raise DetailedFailure(e.retcode, args, e.error)
fixed_errors.append(e.error)
else:
return
raise e
try:
if not resolve_error(e.error, None, fixers=fixers):
logging.warning("Failed to find resolution for error %r. Giving up.", e.error)
raise e
except DetailedFailure as n:
logging.info('New error %r while resolving %r', n, e)
if n in to_resolve:
raise
to_resolve.append(e)
to_resolve.append(n)
else:
fixed_errors.append(e.error)
def resolve_error(error, phase, fixers):

View file

@ -80,6 +80,22 @@ class PythonPackageRequirement(Requirement):
return p.returncode == 0
class PhpPackageRequirement(Requirement):
def __init__(self, package: str, channel: Optional[str] = None,
min_version: Optional[str] = None,
max_version: Optional[str] = None):
self.package = package
self.channel = channel
self.min_version = min_version
self.max_version = max_version
def __repr__(self):
return "%s(%r, %r, %r, %r)" % (
type(self).__name__, self.package, self.channel,
self.min_version, self.max_version)
class BinaryRequirement(Requirement):
binary_name: str

View file

@ -41,6 +41,7 @@ from ..requirements import (
GoPackageRequirement,
DhAddonRequirement,
PhpClassRequirement,
PhpPackageRequirement,
RPackageRequirement,
NodeModuleRequirement,
NodePackageRequirement,
@ -315,6 +316,10 @@ def resolve_php_class_req(apt_mgr, req):
return find_reqs_simple(apt_mgr, [path])
def resolve_php_package_req(apt_mgr, req):
return [AptRequirement.simple('php-%s' % req.package, minimum_version=req.min_version)]
def resolve_r_package_req(apt_mgr, req):
paths = [posixpath.join("/usr/lib/R/site-library/.*/R/%s$" % re.escape(req.package))]
return find_reqs_simple(apt_mgr, paths, regex=True)
@ -560,6 +565,7 @@ APT_REQUIREMENT_RESOLVERS = [
(GoPackageRequirement, resolve_go_package_req),
(DhAddonRequirement, resolve_dh_addon_req),
(PhpClassRequirement, resolve_php_class_req),
(PhpPackageRequirement, resolve_php_package_req),
(RPackageRequirement, resolve_r_package_req),
(NodeModuleRequirement, resolve_node_module_req),
(NodePackageRequirement, resolve_node_package_req),