Add --show-log-on-error option to spack install
- converted `log_path` and `env_path` to properties of PackageBase. - InstallErrors in build_environment are now annotated with the package that caused them, in the 'pkg' attribute. - Add `--show-log-on-error` option to `spack install` that catches InstallErrors and prints the log to stderr if it exists. Note that adding a reference to the Pakcage allows a lot of stuff currently handled by do_install() and build_environment to be handled externally.
This commit is contained in:
parent
742cd7f127
commit
c7a789e2d6
@ -598,6 +598,10 @@ def child_process(child_pipe, input_stream):
|
|||||||
target=child_process, args=(child_pipe, input_stream))
|
target=child_process, args=(child_pipe, input_stream))
|
||||||
p.start()
|
p.start()
|
||||||
|
|
||||||
|
except InstallError as e:
|
||||||
|
e.pkg = pkg
|
||||||
|
raise
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
# Close the input stream in the parent process
|
# Close the input stream in the parent process
|
||||||
if input_stream is not None:
|
if input_stream is not None:
|
||||||
@ -606,6 +610,10 @@ def child_process(child_pipe, input_stream):
|
|||||||
child_result = parent_pipe.recv()
|
child_result = parent_pipe.recv()
|
||||||
p.join()
|
p.join()
|
||||||
|
|
||||||
|
# let the caller know which package went wrong.
|
||||||
|
if isinstance(child_result, InstallError):
|
||||||
|
child_result.pkg = pkg
|
||||||
|
|
||||||
# If the child process raised an error, print its output here rather
|
# If the child process raised an error, print its output here rather
|
||||||
# than waiting until the call to SpackError.die() in main(). This
|
# than waiting until the call to SpackError.die() in main(). This
|
||||||
# allows exception handling output to be logged from within Spack.
|
# allows exception handling output to be logged from within Spack.
|
||||||
@ -676,10 +684,15 @@ def make_stack(tb, stack=None):
|
|||||||
|
|
||||||
|
|
||||||
class InstallError(spack.error.SpackError):
|
class InstallError(spack.error.SpackError):
|
||||||
"""Raised by packages when a package fails to install"""
|
"""Raised by packages when a package fails to install.
|
||||||
|
|
||||||
|
Any subclass of InstallError will be annotated by Spack wtih a
|
||||||
|
``pkg`` attribute on failure, which the caller can use to get the
|
||||||
|
package for which the exception was raised.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class ChildError(spack.error.SpackError):
|
class ChildError(InstallError):
|
||||||
"""Special exception class for wrapping exceptions from child processes
|
"""Special exception class for wrapping exceptions from child processes
|
||||||
in Spack's build environment.
|
in Spack's build environment.
|
||||||
|
|
||||||
|
@ -27,6 +27,8 @@
|
|||||||
import functools
|
import functools
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
import time
|
import time
|
||||||
import xml.dom.minidom
|
import xml.dom.minidom
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
@ -68,6 +70,9 @@ def setup_parser(subparser):
|
|||||||
subparser.add_argument(
|
subparser.add_argument(
|
||||||
'--restage', action='store_true',
|
'--restage', action='store_true',
|
||||||
help="if a partial install is detected, delete prior state")
|
help="if a partial install is detected, delete prior state")
|
||||||
|
subparser.add_argument(
|
||||||
|
'--show-log-on-error', action='store_true',
|
||||||
|
help="print full build log to stderr if build fails")
|
||||||
subparser.add_argument(
|
subparser.add_argument(
|
||||||
'--source', action='store_true', dest='install_source',
|
'--source', action='store_true', dest='install_source',
|
||||||
help="install source files in prefix")
|
help="install source files in prefix")
|
||||||
@ -367,13 +372,26 @@ def install(parser, args, **kwargs):
|
|||||||
for s in spec.dependencies():
|
for s in spec.dependencies():
|
||||||
p = spack.repo.get(s)
|
p = spack.repo.get(s)
|
||||||
p.do_install(**kwargs)
|
p.do_install(**kwargs)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
package = spack.repo.get(spec)
|
package = spack.repo.get(spec)
|
||||||
kwargs['explicit'] = True
|
kwargs['explicit'] = True
|
||||||
package.do_install(**kwargs)
|
package.do_install(**kwargs)
|
||||||
|
|
||||||
|
except InstallError as e:
|
||||||
|
if args.show_log_on_error:
|
||||||
|
e.print_context()
|
||||||
|
if not os.path.exists(e.pkg.build_log_path):
|
||||||
|
tty.error("'spack install' created no log.")
|
||||||
|
else:
|
||||||
|
sys.stderr.write('Full build log:\n')
|
||||||
|
with open(e.pkg.build_log_path) as log:
|
||||||
|
shutil.copyfileobj(log, sys.stderr)
|
||||||
|
raise
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
PackageBase.do_install = saved_do_install
|
PackageBase.do_install = saved_do_install
|
||||||
|
|
||||||
# Dump log file if asked to
|
# Dump test output if asked to
|
||||||
if args.log_format is not None:
|
if args.log_format is not None:
|
||||||
test_suite.dump(log_filename)
|
test_suite.dump(log_filename)
|
||||||
|
@ -785,6 +785,14 @@ def stage(self, stage):
|
|||||||
"""Allow a stage object to be set to override the default."""
|
"""Allow a stage object to be set to override the default."""
|
||||||
self._stage = stage
|
self._stage = stage
|
||||||
|
|
||||||
|
@property
|
||||||
|
def env_path(self):
|
||||||
|
return os.path.join(self.stage.source_path, 'spack-build.env')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def log_path(self):
|
||||||
|
return os.path.join(self.stage.source_path, 'spack-build.out')
|
||||||
|
|
||||||
def _make_fetcher(self):
|
def _make_fetcher(self):
|
||||||
# Construct a composite fetcher that always contains at least
|
# Construct a composite fetcher that always contains at least
|
||||||
# one element (the root package). In case there are resources
|
# one element (the root package). In case there are resources
|
||||||
@ -1331,20 +1339,11 @@ def build_process():
|
|||||||
self.stage.chdir_to_source()
|
self.stage.chdir_to_source()
|
||||||
|
|
||||||
# Save the build environment in a file before building.
|
# Save the build environment in a file before building.
|
||||||
env_path = join_path(os.getcwd(), 'spack-build.env')
|
dump_environment(self.env_path)
|
||||||
|
|
||||||
# Redirect I/O to a build log (and optionally to
|
|
||||||
# the terminal)
|
|
||||||
log_path = join_path(os.getcwd(), 'spack-build.out')
|
|
||||||
|
|
||||||
# FIXME : refactor this assignment
|
|
||||||
self.log_path = log_path
|
|
||||||
self.env_path = env_path
|
|
||||||
dump_environment(env_path)
|
|
||||||
|
|
||||||
# Spawn a daemon that reads from a pipe and redirects
|
# Spawn a daemon that reads from a pipe and redirects
|
||||||
# everything to log_path
|
# everything to log_path
|
||||||
with log_output(log_path, echo, True) as logger:
|
with log_output(self.log_path, echo, True) as logger:
|
||||||
for phase_name, phase_attr in zip(
|
for phase_name, phase_attr in zip(
|
||||||
self.phases, self._InstallPhase_phases):
|
self.phases, self._InstallPhase_phases):
|
||||||
|
|
||||||
|
@ -142,3 +142,18 @@ def test_install_with_source(
|
|||||||
spec.prefix.share, 'trivial-install-test-package', 'src')
|
spec.prefix.share, 'trivial-install-test-package', 'src')
|
||||||
assert filecmp.cmp(os.path.join(mock_archive.path, 'configure'),
|
assert filecmp.cmp(os.path.join(mock_archive.path, 'configure'),
|
||||||
os.path.join(src, 'configure'))
|
os.path.join(src, 'configure'))
|
||||||
|
|
||||||
|
|
||||||
|
def test_show_log_on_error(builtin_mock, mock_archive, mock_fetch,
|
||||||
|
config, install_mockery, capfd):
|
||||||
|
"""Make sure --show-log-on-error works."""
|
||||||
|
with capfd.disabled():
|
||||||
|
out = install('--show-log-on-error', 'build-error',
|
||||||
|
fail_on_error=False)
|
||||||
|
assert isinstance(install.error, spack.build_environment.ChildError)
|
||||||
|
assert install.error.pkg.name == 'build-error'
|
||||||
|
assert 'Full build log:' in out
|
||||||
|
|
||||||
|
errors = [line for line in out.split('\n')
|
||||||
|
if 'configure: error: cannot run C compiled programs' in line]
|
||||||
|
assert len(errors) == 2
|
||||||
|
Loading…
Reference in New Issue
Block a user