Allow 'spack external find' to find executables on the system path (#22091)

Co-authored-by: Lou Lawrence <lou.lawrence@kitware.com>
This commit is contained in:
Betsy McPhail
2021-08-06 15:51:37 -04:00
committed by Peter Scheibel
parent fb0e91c534
commit d4d101f57e
5 changed files with 247 additions and 51 deletions

View File

@@ -9,8 +9,10 @@
import os
import os.path
import re
import sys
import warnings
import llnl.util.filesystem
import llnl.util.tty
@@ -73,6 +75,8 @@ def by_executable(packages_to_check, path_hints=None):
for pkg in packages_to_check:
if hasattr(pkg, 'executables'):
for exe in pkg.executables:
if sys.platform == 'win32':
exe = exe.replace('$', r'\.exe$')
exe_pattern_to_pkgs[exe].append(pkg)
pkg_to_found_exes = collections.defaultdict(set)

View File

@@ -43,7 +43,7 @@
import llnl.util.lock as lk
import llnl.util.tty as tty
from llnl.util.tty.color import colorize
from llnl.util.tty.log import log_output
from llnl.util.tty.log import log_output, winlog
import spack.binary_distribution as binary_distribution
import spack.compilers
@@ -1936,61 +1936,80 @@ def _real_install(self):
# Spawn a daemon that reads from a pipe and redirects
# everything to log_path, and provide the phase for logging
for i, (phase_name, phase_attr) in enumerate(zip(
pkg.phases, pkg._InstallPhase_phases)):
if sys.platform != 'win32':
for i, (phase_name, phase_attr) in enumerate(zip(
pkg.phases, pkg._InstallPhase_phases)):
# Keep a log file for each phase
log_dir = os.path.dirname(pkg.log_path)
log_file = "spack-build-%02d-%s-out.txt" % (
i + 1, phase_name.lower()
)
log_file = os.path.join(log_dir, log_file)
try:
# DEBUGGING TIP - to debug this section, insert an IPython
# embed here, and run the sections below without log capture
log_contextmanager = log_output(
log_file,
self.echo,
True,
env=self.unmodified_env,
filter_fn=self.filter_fn
# Keep a log file for each phase
log_dir = os.path.dirname(pkg.log_path)
log_file = "spack-build-%02d-%s-out.txt" % (
i + 1, phase_name.lower()
)
log_file = os.path.join(log_dir, log_file)
with log_contextmanager as logger:
with logger.force_echo():
inner_debug_level = tty.debug_level()
tty.set_debug(debug_level)
tty.msg(
"{0} Executing phase: '{1}'" .format(
self.pre,
phase_name
try:
# DEBUGGING TIP - to debug this section, insert an IPython
# embed here, and run the sections below without log capture
log_contextmanager = log_output(
log_file,
self.echo,
True,
env=self.unmodified_env,
filter_fn=self.filter_fn
)
with log_contextmanager as logger:
with logger.force_echo():
inner_debug_level = tty.debug_level()
tty.set_debug(debug_level)
tty.msg(
"{0} Executing phase: '{1}'" .format(
self.pre,
phase_name
)
)
)
tty.set_debug(inner_debug_level)
tty.set_debug(inner_debug_level)
# Redirect stdout and stderr to daemon pipe
phase = getattr(pkg, phase_attr)
self.timer.phase(phase_name)
# Catch any errors to report to logging
phase(pkg.spec, pkg.prefix)
spack.hooks.on_phase_success(pkg, phase_name, log_file)
except BaseException:
combine_phase_logs(pkg.phase_log_files, pkg.log_path)
spack.hooks.on_phase_error(pkg, phase_name, log_file)
# phase error indicates install error
spack.hooks.on_install_failure(pkg.spec)
raise
# We assume loggers share echo True/False
self.echo = logger.echo
else:
with winlog(pkg.log_path, True, True,
env=self.unmodified_env) as logger:
for phase_name, phase_attr in zip(
pkg.phases, pkg._InstallPhase_phases):
# with logger.force_echo():
# inner_debug_level = tty.debug_level()
# tty.set_debug(debug_level)
# tty.msg("{0} Executing phase: '{1}'"
# .format(pre, phase_name))
# tty.set_debug(inner_debug_level)
# Redirect stdout and stderr to daemon pipe
phase = getattr(pkg, phase_attr)
self.timer.phase(phase_name)
# Catch any errors to report to logging
phase(pkg.spec, pkg.prefix)
spack.hooks.on_phase_success(pkg, phase_name, log_file)
except BaseException:
combine_phase_logs(pkg.phase_log_files, pkg.log_path)
spack.hooks.on_phase_error(pkg, phase_name, log_file)
# phase error indicates install error
spack.hooks.on_install_failure(pkg.spec)
raise
# We assume loggers share echo True/False
self.echo = logger.echo
# After log, we can get all output/error files from the package stage
combine_phase_logs(pkg.phase_log_files, pkg.log_path)
log(pkg)
if sys.platform != 'win32':
# After log, we can get all output/error files from the package stage
combine_phase_logs(pkg.phase_log_files, pkg.log_path)
log(pkg)
def build_process(pkg, install_args):

View File

@@ -31,7 +31,7 @@
import llnl.util.tty as tty
import llnl.util.tty.colify
import llnl.util.tty.color as color
from llnl.util.tty.log import log_output
from llnl.util.tty.log import log_output, winlog
import spack
import spack.cmd
@@ -494,6 +494,7 @@ def setup_main_options(args):
# debug must be set first so that it can even affect behavior of
# errors raised by spack.config.
tty.debug(spack.config.config)
if args.debug:
spack.error.debug = True
spack.util.debug.register_interrupt_handler()
@@ -611,9 +612,14 @@ def __call__(self, *argv, **kwargs):
out = StringIO()
try:
with log_output(out):
self.returncode = _invoke_command(
self.command, self.parser, args, unknown)
if sys.platform == 'win32':
with winlog(out):
self.returncode = _invoke_command(
self.command, self.parser, args, unknown)
else:
with log_output(out):
self.returncode = _invoke_command(
self.command, self.parser, args, unknown)
except SystemExit as e:
self.returncode = e.code

View File

@@ -22,6 +22,8 @@ class Executable(object):
"""Class representing a program that can be run on the command line."""
def __init__(self, name):
if sys.platform == 'win32':
name = name.replace('\\', '/')
self.exe = shlex.split(str(name))
self.default_env = {}
from spack.util.environment import EnvironmentModifications # no cycle