wrap: Improve error handling and logging

pull/4327/head
Xavier Claessens 6 years ago
parent 1889e33389
commit 3f98ad8eed
  1. 49
      mesonbuild/interpreter.py
  2. 68
      mesonbuild/wrap/wrap.py

@ -2261,19 +2261,21 @@ external dependencies (including libraries) must go to "dependencies".''')
r = wrap.Resolver(subproject_dir_abs, self.coredata.get_builtin_option('wrap_mode')) r = wrap.Resolver(subproject_dir_abs, self.coredata.get_builtin_option('wrap_mode'))
try: try:
resolved = r.resolve(dirname) resolved = r.resolve(dirname)
except RuntimeError as e: except wrap.WrapException as e:
# if the reason subproject execution failed was because subprojdir = os.path.join(self.subproject_dir, r.directory)
# the directory doesn't exist, try to give some helpful if not required:
# advice if it's a nested subproject that needs mlog.log('\nSubproject ', mlog.bold(subprojdir), 'is buildable:', mlog.red('NO'), '(disabling)\n')
# promotion... return self.disabled_subproject(dirname)
self.print_nested_info(dirname)
if isinstance(e, wrap.WrapNotFoundException):
if required: # if the reason subproject execution failed was because
msg = 'Subproject directory {!r} does not exist and cannot be downloaded:\n{}' # the directory doesn't exist, try to give some helpful
raise InterpreterException(msg.format(os.path.join(self.subproject_dir, dirname), e)) # advice if it's a nested subproject that needs
# promotion...
mlog.log('\nSubproject ', mlog.bold(dirname), 'is buildable:', mlog.red('NO'), '(disabling)\n') self.print_nested_info(dirname)
return self.disabled_subproject(dirname)
msg = 'Failed to initialize {!r}:\n{}'
raise InterpreterException(msg.format(subprojdir, e))
subdir = os.path.join(self.subproject_dir, resolved) subdir = os.path.join(self.subproject_dir, resolved)
os.makedirs(os.path.join(self.build.environment.get_build_dir(), subdir), exist_ok=True) os.makedirs(os.path.join(self.build.environment.get_build_dir(), subdir), exist_ok=True)
@ -2979,26 +2981,21 @@ external dependencies (including libraries) must go to "dependencies".''')
return Disabler() return Disabler()
def print_nested_info(self, dependency_name): def print_nested_info(self, dependency_name):
message_templ = '''\nDependency %s not found but it is available in a sub-subproject. message = ['Dependency', mlog.bold(dependency_name), 'not found but it is available in a sub-subproject.\n' +
To use it in the current project, promote it by going in the project source 'To use it in the current project, promote it by going in the project source\n'
root and issuing %s. 'root and issuing']
'''
sprojs = mesonlib.detect_subprojects('subprojects', self.source_root) sprojs = mesonlib.detect_subprojects('subprojects', self.source_root)
if dependency_name not in sprojs: if dependency_name not in sprojs:
return return
found = sprojs[dependency_name] found = sprojs[dependency_name]
if len(found) > 1: if len(found) > 1:
suffix = 'one of the following commands' message.append('one of the following commands:')
else: else:
suffix = 'the following command' message.append('the following command:')
message = message_templ % (dependency_name, suffix) command_templ = '\nmeson wrap promote {}'
cmds = []
command_templ = 'meson wrap promote '
for l in found: for l in found:
cmds.append(command_templ + l[len(self.source_root) + 1:]) message.append(mlog.bold(command_templ.format(l[len(self.source_root) + 1:])))
final_message = message + '\n'.join(cmds) mlog.warning(*message)
print(final_message)
def get_subproject_infos(self, kwargs): def get_subproject_infos(self, kwargs):
fbinfo = kwargs['fallback'] fbinfo = kwargs['fallback']

@ -18,8 +18,8 @@ import urllib.request, os, hashlib, shutil, tempfile, stat
import subprocess import subprocess
import sys import sys
import configparser import configparser
from pathlib import Path
from . import WrapMode from . import WrapMode
from ..mesonlib import MesonException
try: try:
import ssl import ssl
@ -67,19 +67,35 @@ def open_wrapdburl(urlstring):
urlstring = 'http' + urlstring[5:] urlstring = 'http' + urlstring[5:]
return urllib.request.urlopen(urlstring, timeout=req_timeout) return urllib.request.urlopen(urlstring, timeout=req_timeout)
class WrapException(MesonException):
pass
class WrapNotFoundException(WrapException):
pass
class PackageDefinition: class PackageDefinition:
def __init__(self, fname): def __init__(self, fname):
self.config = configparser.ConfigParser() self.basename = os.path.basename(fname)
self.config.read(fname) try:
self.config = configparser.ConfigParser()
self.config.read(fname)
except:
raise WrapException('Failed to parse {}'.format(self.basename))
if len(self.config.sections()) < 1:
raise WrapException('Missing sections in {}'.format(self.basename))
self.wrap_section = self.config.sections()[0] self.wrap_section = self.config.sections()[0]
if not self.wrap_section.startswith('wrap-'): if not self.wrap_section.startswith('wrap-'):
raise RuntimeError('Invalid format of package file') m = '{!r} is not a valid first section in {}'
raise WrapException(m.format(self.wrap_section, self.basename))
self.type = self.wrap_section[5:] self.type = self.wrap_section[5:]
self.values = dict(self.config[self.wrap_section]) self.values = dict(self.config[self.wrap_section])
def get(self, key): def get(self, key):
return self.values[key] try:
return self.values[key]
except KeyError:
m = 'Missing key {!r} in {}'
raise WrapException(m.format(key, self.basename))
def has_patch(self): def has_patch(self):
return 'patch_url' in self.values return 'patch_url' in self.values
@ -92,32 +108,30 @@ class Resolver:
def resolve(self, packagename): def resolve(self, packagename):
self.packagename = packagename self.packagename = packagename
self.directory = packagename
# We always have to load the wrap file, if it exists, because it could # We always have to load the wrap file, if it exists, because it could
# override the default directory name. # override the default directory name.
p = self.load_wrap() p = self.load_wrap()
directory = packagename
if p and 'directory' in p.values: if p and 'directory' in p.values:
directory = p.get('directory') self.directory = p.get('directory')
dirname = os.path.join(self.subdir_root, directory) dirname = os.path.join(self.subdir_root, self.directory)
subprojdir = os.path.join(*Path(dirname).parts[-2:])
meson_file = os.path.join(dirname, 'meson.build') meson_file = os.path.join(dirname, 'meson.build')
# The directory is there and has meson.build? Great, use it. # The directory is there and has meson.build? Great, use it.
if os.path.exists(meson_file): if os.path.exists(meson_file):
return directory return self.directory
# Check if the subproject is a git submodule # Check if the subproject is a git submodule
self.resolve_git_submodule(dirname) self.resolve_git_submodule(dirname)
if os.path.exists(dirname): if os.path.exists(dirname):
if not os.path.isdir(dirname): if not os.path.isdir(dirname):
m = '{!r} already exists and is not a dir; cannot use as subproject' raise WrapException('Path already exists but is not a directory')
raise RuntimeError(m.format(subprojdir))
else: else:
# A wrap file is required to download # A wrap file is required to download
if not p: if not p:
m = 'No {}.wrap found for {!r}' m = 'Subproject directory not found and {}.wrap file not found'
raise RuntimeError(m.format(packagename, subprojdir)) raise WrapNotFoundException(m.format(self.packagename))
if p.type == 'file': if p.type == 'file':
self.get_file(p) self.get_file(p)
@ -130,14 +144,13 @@ class Resolver:
elif p.type == "svn": elif p.type == "svn":
self.get_svn(p) self.get_svn(p)
else: else:
raise AssertionError('Unreachable code.') raise WrapException('Unknown wrap type {!r}'.format(p.type))
# A meson.build file is required in the directory # A meson.build file is required in the directory
if not os.path.exists(meson_file): if not os.path.exists(meson_file):
m = '{!r} is not empty and has no meson.build files' raise WrapException('Subproject exists but has no meson.build file')
raise RuntimeError(m.format(subprojdir))
return directory return self.directory
def load_wrap(self): def load_wrap(self):
fname = os.path.join(self.subdir_root, self.packagename + '.wrap') fname = os.path.join(self.subdir_root, self.packagename + '.wrap')
@ -150,7 +163,7 @@ class Resolver:
# Git submodules are ok (see above)! # Git submodules are ok (see above)!
if self.wrap_mode is WrapMode.nodownload: if self.wrap_mode is WrapMode.nodownload:
m = 'Automatic wrap-based subproject downloading is disabled' m = 'Automatic wrap-based subproject downloading is disabled'
raise RuntimeError(m) raise WrapException(m)
def resolve_git_submodule(self, dirname): def resolve_git_submodule(self, dirname):
# Are we in a git repository? # Are we in a git repository?
@ -163,15 +176,15 @@ class Resolver:
return False return False
# Submodule has not been added, add it # Submodule has not been added, add it
if out.startswith(b'+'): if out.startswith(b'+'):
mlog.warning('git submodule {} might be out of date'.format(dirname)) mlog.warning('git submodule might be out of date')
return True return True
elif out.startswith(b'U'): elif out.startswith(b'U'):
raise RuntimeError('submodule {} has merge conflicts'.format(dirname)) raise WrapException('git submodule has merge conflicts')
# Submodule exists, but is deinitialized or wasn't initialized # Submodule exists, but is deinitialized or wasn't initialized
elif out.startswith(b'-'): elif out.startswith(b'-'):
if subprocess.call(['git', '-C', self.subdir_root, 'submodule', 'update', '--init', dirname]) == 0: if subprocess.call(['git', '-C', self.subdir_root, 'submodule', 'update', '--init', dirname]) == 0:
return True return True
raise RuntimeError('Failed to git submodule init {!r}'.format(dirname)) raise WrapException('git submodule failed to init')
# Submodule looks fine, but maybe it wasn't populated properly. Do a checkout. # Submodule looks fine, but maybe it wasn't populated properly. Do a checkout.
elif out.startswith(b' '): elif out.startswith(b' '):
subprocess.call(['git', 'checkout', '.'], cwd=dirname) subprocess.call(['git', 'checkout', '.'], cwd=dirname)
@ -182,7 +195,7 @@ class Resolver:
# It is not a submodule, just a folder that exists in the main repository. # It is not a submodule, just a folder that exists in the main repository.
return False return False
m = 'Unknown git submodule output: {!r}' m = 'Unknown git submodule output: {!r}'
raise RuntimeError(m.format(out)) raise WrapException(m.format(out))
def get_file(self, p): def get_file(self, p):
path = self.get_file_internal(p, 'source') path = self.get_file_internal(p, 'source')
@ -190,12 +203,9 @@ class Resolver:
extract_dir = self.subdir_root extract_dir = self.subdir_root
# Some upstreams ship packages that do not have a leading directory. # Some upstreams ship packages that do not have a leading directory.
# Create one for them. # Create one for them.
try: if 'lead_directory_missing' in p.values:
p.get('lead_directory_missing')
os.mkdir(target_dir) os.mkdir(target_dir)
extract_dir = target_dir extract_dir = target_dir
except KeyError:
pass
shutil.unpack_archive(path, extract_dir) shutil.unpack_archive(path, extract_dir)
if p.has_patch(): if p.has_patch():
self.apply_patch(p) self.apply_patch(p)
@ -285,7 +295,7 @@ class Resolver:
h.update(f.read()) h.update(f.read())
dhash = h.hexdigest() dhash = h.hexdigest()
if dhash != expected: if dhash != expected:
raise RuntimeError('Incorrect hash for %s:\n %s expected\n %s actual.' % (what, expected, dhash)) raise WrapException('Incorrect hash for %s:\n %s expected\n %s actual.' % (what, expected, dhash))
def download(self, p, what, ofname): def download(self, p, what, ofname):
self.check_can_download() self.check_can_download()
@ -295,7 +305,7 @@ class Resolver:
expected = p.get(what + '_hash') expected = p.get(what + '_hash')
if dhash != expected: if dhash != expected:
os.remove(tmpfile) os.remove(tmpfile)
raise RuntimeError('Incorrect hash for %s:\n %s expected\n %s actual.' % (what, expected, dhash)) raise WrapException('Incorrect hash for %s:\n %s expected\n %s actual.' % (what, expected, dhash))
os.rename(tmpfile, ofname) os.rename(tmpfile, ofname)
def get_file_internal(self, p, what): def get_file_internal(self, p, what):

Loading…
Cancel
Save