Bug 818545 - Gracefully handle errors when loading mozconfigs; r=glandium

DONTBUILD (NPOTB)
This commit is contained in:
Gregory Szorc 2012-12-05 14:34:14 -08:00
parent 28b3111621
commit 6659946d35
3 changed files with 71 additions and 6 deletions

View File

@ -2,7 +2,7 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from __future__ import unicode_literals
from __future__ import print_function, unicode_literals
import logging
import os
@ -14,7 +14,11 @@ from mach.mixin.logging import LoggingMixin
from mach.mixin.process import ProcessExecutionMixin
from .config import BuildConfig
from .mozconfig import MozconfigLoader
from .mozconfig import (
MozconfigFindException,
MozconfigLoadException,
MozconfigLoader,
)
class MozbuildObject(ProcessExecutionMixin):
@ -244,3 +248,27 @@ class MachCommandBase(MozbuildObject):
def __init__(self, context):
MozbuildObject.__init__(self, context.topdir, context.settings,
context.log_manager)
# Incur mozconfig processing so we have unified error handling for
# errors. Otherwise, the exceptions could bubble back to mach's error
# handler.
try:
self.mozconfig
except MozconfigFindException as e:
print(e.message)
sys.exit(1)
except MozconfigLoadException as e:
print('Error loading mozconfig: ' + e.path)
print('')
print(e.message)
if e.output:
print('')
print('mozconfig output:')
print('')
for line in e.output:
print(line)
sys.exit(1)

View File

@ -26,6 +26,12 @@ supported. Please move it to %s/.mozconfig or set an explicit path
via the $MOZCONFIG environment variable.
'''.strip()
MOZCONFIG_BAD_EXIT_CODE = '''
Evaluation of your mozconfig exited with an error. This could be triggered
by a command inside your mozconfig failing. Please change your mozconfig
to not error and/or to catch errors in executed commands.
'''.strip()
class MozconfigFindException(Exception):
"""Raised when a mozconfig location is not defined properly."""
@ -37,8 +43,9 @@ class MozconfigLoadException(Exception):
This typically indicates a malformed or misbehaving mozconfig file.
"""
def __init__(self, path, message):
def __init__(self, path, message, output=None):
self.path = path
self.output = output
Exception.__init__(self, message)
@ -173,8 +180,22 @@ class MozconfigLoader(ProcessExecutionMixin):
args = self._normalize_command([self._loader_script, self.topsrcdir,
path], True)
output = subprocess.check_output(args, stderr=subprocess.PIPE,
cwd=self.topsrcdir, env=env)
try:
# We need to capture stderr because that's where the shell sends
# errors if execution fails.
output = subprocess.check_output(args, stderr=subprocess.STDOUT,
cwd=self.topsrcdir, env=env)
except subprocess.CalledProcessError as e:
lines = e.output.splitlines()
# Output before actual execution shouldn't be relevant.
try:
index = lines.index('------END_BEFORE_SOURCE')
lines = lines[index + 1:]
except ValueError:
pass
raise MozconfigLoadException(path, MOZCONFIG_BAD_EXIT_CODE, lines)
parsed = self._parse_loader_output(output)
@ -245,7 +266,7 @@ class MozconfigLoader(ProcessExecutionMixin):
current_type = None
in_variable = None
for line in output.split('\n'):
for line in output.splitlines():
if not len(line):
continue

View File

@ -17,6 +17,7 @@ from tempfile import (
from mozbuild.mozconfig import (
MozconfigFindException,
MozconfigLoadException,
MozconfigLoader,
)
@ -296,3 +297,18 @@ class TestMozconfigLoader(unittest.TestCase):
self.assertIn('EMPTY', result['env']['added'])
self.assertEqual(result['env']['added']['EMPTY'], '')
def test_read_load_exception(self):
"""Ensure non-0 exit codes in mozconfigs are handled properly."""
with NamedTemporaryFile(mode='w') as mozconfig:
mozconfig.write('echo "hello world"\n')
mozconfig.write('exit 1\n')
mozconfig.flush()
with self.assertRaises(MozconfigLoadException) as e:
self.get_loader().read_mozconfig(mozconfig.name)
self.assertTrue(e.exception.message.startswith(
'Evaluation of your mozconfig exited with an error'))
self.assertEquals(e.exception.path, mozconfig.name)
self.assertEquals(e.exception.output, ['hello world'])