Skip to content

Commit 8ca3848

Browse files
authored
Dynamic library/include paths (#8136)
Fixes #7855 Closes #8070 Closes #2645 When searching for library directories (e.g. to add "-L" arguments to the compiler wrapper) Spack was only trying the "lib/" and "lib64/" directories for each dependency install prefix; this missed cases where packages would install libraries to subdirectories and also was not customizable. This PR makes use of the ".headers" and ".libs" properties for more-advanced location of header/library directories. Since packages can override the default behavior of ".headers" and ".libs", it also allows package writers to customize. The following environment variables which used to be set by Spack for a package build have been removed: * Remove SPACK_PREFIX and SPACK_DEPENDENCIES environment variables as they are no-longer used * Remove SPACK_INSTALL environment variable: it was not used before this PR
1 parent 1bf8629 commit 8ca3848

File tree

9 files changed

+261
-252
lines changed

9 files changed

+261
-252
lines changed

lib/spack/env/cc

Lines changed: 22 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
# the script runs. They are set by routines in spack.build_environment
2525
# as part of spack.package.Package.do_install().
2626
parameters=(
27-
SPACK_PREFIX
2827
SPACK_ENV_PATH
2928
SPACK_DEBUG_LOG_DIR
3029
SPACK_DEBUG_LOG_ID
@@ -46,8 +45,6 @@ parameters=(
4645
# SPACK_DEBUG
4746
# Test command is used to unit test the compiler script.
4847
# SPACK_TEST_COMMAND
49-
# Dependencies can be empty for pkgs with no deps:
50-
# SPACK_DEPENDENCIES
5148

5249
# die()
5350
# Prints a message and exits with error 1.
@@ -385,52 +382,30 @@ case "$mode" in
385382
flags=("${flags[@]}" "${SPACK_LDFLAGS[@]}") ;;
386383
esac
387384

385+
# Prepend include directories
386+
IFS=':' read -ra include_dirs <<< "$SPACK_INCLUDE_DIRS"
387+
if [[ $mode == cpp || $mode == cc || $mode == as || $mode == ccld ]]; then
388+
for include_dir in "${include_dirs[@]}"; do
389+
includes=("${includes[@]}" "$include_dir")
390+
done
391+
fi
388392

389-
# Include the package's prefix/lib[64] dirs in rpath. We don't know until
390-
# *after* installation which one's correct, so we include both lib and
391-
# lib64, assuming that only one will be present.
392-
case "$mode" in
393-
ld|ccld)
394-
$add_rpaths && rpaths+=("$SPACK_PREFIX/lib")
395-
$add_rpaths && rpaths+=("$SPACK_PREFIX/lib64")
396-
;;
397-
esac
398-
399-
# Read spack dependencies from the environment. This is a list of prefixes.
400-
IFS=':' read -ra deps <<< "$SPACK_DEPENDENCIES"
401-
for dep in "${deps[@]}"; do
402-
# Append include directories in any compilation mode
403-
case "$mode" in
404-
cpp|cc|as|ccld)
405-
if [[ -d $dep/include ]]; then
406-
includes=("${includes[@]}" "$dep/include")
407-
fi
408-
;;
409-
esac
410-
411-
# Append lib/lib64 and RPATH directories, but only if we're linking
412-
case "$mode" in
413-
ld|ccld)
414-
if [[ -d $dep/lib ]]; then
415-
if [[ $SPACK_RPATH_DEPS == *$dep* ]]; then
416-
$add_rpaths && rpaths=("${rpaths[@]}" "$dep/lib")
417-
fi
418-
if [[ $SPACK_LINK_DEPS == *$dep* ]]; then
419-
libdirs=("${libdirs[@]}" "$dep/lib")
420-
fi
421-
fi
393+
IFS=':' read -ra rpath_dirs <<< "$SPACK_RPATH_DIRS"
394+
if [[ $mode == ccld || $mode == ld ]]; then
395+
for rpath_dir in "${rpath_dirs[@]}"; do
396+
# Append RPATH directories. Note that in the case of the
397+
# top-level package these directories may not exist yet. For dependencies
398+
# it is assumed that paths have already been confirmed.
399+
$add_rpaths && rpaths=("${rpaths[@]}" "$rpath_dir")
400+
done
401+
fi
422402

423-
if [[ -d $dep/lib64 ]]; then
424-
if [[ $SPACK_RPATH_DEPS == *$dep* ]]; then
425-
$add_rpaths && rpaths+=("$dep/lib64")
426-
fi
427-
if [[ $SPACK_LINK_DEPS == *$dep* ]]; then
428-
libdirs+=("$dep/lib64")
429-
fi
430-
fi
431-
;;
432-
esac
433-
done
403+
IFS=':' read -ra link_dirs <<< "$SPACK_LINK_DIRS"
404+
if [[ $mode == ccld || $mode == ld ]]; then
405+
for link_dir in "${link_dirs[@]}"; do
406+
libdirs=("${libdirs[@]}" "$link_dir")
407+
done
408+
fi
434409

435410
# add RPATHs if we're in in any linking mode
436411
case "$mode" in

lib/spack/spack/build_environment.py

Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@
5353
import spack.paths
5454
import spack.store
5555
from spack.util.string import plural
56-
from spack.util.environment import EnvironmentModifications, validate
57-
from spack.util.environment import preserve_environment
58-
from spack.util.environment import env_flag, filter_system_paths, get_path
56+
from spack.util.environment import (
57+
env_flag, filter_system_paths, get_path, is_system_path,
58+
EnvironmentModifications, validate, preserve_environment)
5959
from spack.util.environment import system_dirs
6060
from spack.util.executable import Executable
6161
from spack.util.module_cmd import load_module, get_path_from_module
@@ -73,7 +73,9 @@
7373
# Spack's compiler wrappers.
7474
#
7575
SPACK_ENV_PATH = 'SPACK_ENV_PATH'
76-
SPACK_DEPENDENCIES = 'SPACK_DEPENDENCIES'
76+
SPACK_INCLUDE_DIRS = 'SPACK_INCLUDE_DIRS'
77+
SPACK_LINK_DIRS = 'SPACK_LINK_DIRS'
78+
SPACK_RPATH_DIRS = 'SPACK_RPATH_DIRS'
7779
SPACK_RPATH_DEPS = 'SPACK_RPATH_DEPS'
7880
SPACK_LINK_DEPS = 'SPACK_LINK_DEPS'
7981
SPACK_PREFIX = 'SPACK_PREFIX'
@@ -257,10 +259,47 @@ def set_build_environment_variables(pkg, env, dirty):
257259
build_link_deps = build_deps | link_deps
258260
rpath_deps = get_rpath_deps(pkg)
259261

262+
link_dirs = []
263+
include_dirs = []
264+
rpath_dirs = []
265+
266+
# The top-level package is always RPATHed. It hasn't been installed yet
267+
# so the RPATHs are added unconditionally (e.g. even though lib64/ may
268+
# not be created for the install).
269+
for libdir in ['lib', 'lib64']:
270+
lib_path = os.path.join(pkg.prefix, libdir)
271+
rpath_dirs.append(lib_path)
272+
273+
# Set up link, include, RPATH directories that are passed to the
274+
# compiler wrapper
275+
for dep in link_deps:
276+
if is_system_path(dep.prefix):
277+
continue
278+
# TODO: packages with alternative implementations of .libs which
279+
# are external may place libraries in nonstandard directories, so
280+
# there should be a check for that
281+
query = pkg.spec[dep.name]
282+
try:
283+
dep_link_dirs = list(query.libs.directories)
284+
link_dirs.extend(dep_link_dirs)
285+
if dep in rpath_deps:
286+
rpath_dirs.extend(dep_link_dirs)
287+
except spack.spec.NoLibrariesError:
288+
tty.debug("No libraries found for {0}".format(dep.name))
289+
290+
try:
291+
include_dirs.extend(query.headers.directories)
292+
except spack.spec.NoHeadersError:
293+
tty.debug("No headers found for {0}".format(dep.name))
294+
if os.path.isdir(dep.prefix.include):
295+
include_dirs.append(dep.prefix.include)
296+
297+
env.set(SPACK_LINK_DIRS, ':'.join(link_dirs))
298+
env.set(SPACK_INCLUDE_DIRS, ':'.join(include_dirs))
299+
env.set(SPACK_RPATH_DIRS, ':'.join(rpath_dirs))
300+
260301
build_prefixes = [dep.prefix for dep in build_deps]
261-
link_prefixes = [dep.prefix for dep in link_deps]
262302
build_link_prefixes = [dep.prefix for dep in build_link_deps]
263-
rpath_prefixes = [dep.prefix for dep in rpath_deps]
264303

265304
# add run-time dependencies of direct build-time dependencies:
266305
for build_dep in build_deps:
@@ -273,26 +312,11 @@ def set_build_environment_variables(pkg, env, dirty):
273312
# contain hundreds of other packages installed in the same directory.
274313
# If these paths come first, they can overshadow Spack installations.
275314
build_prefixes = filter_system_paths(build_prefixes)
276-
link_prefixes = filter_system_paths(link_prefixes)
277315
build_link_prefixes = filter_system_paths(build_link_prefixes)
278-
rpath_prefixes = filter_system_paths(rpath_prefixes)
279-
280-
# Prefixes of all of the package's dependencies go in SPACK_DEPENDENCIES
281-
env.set_path(SPACK_DEPENDENCIES, build_link_prefixes)
282-
283-
# These variables control compiler wrapper behavior
284-
env.set_path(SPACK_RPATH_DEPS, rpath_prefixes)
285-
env.set_path(SPACK_LINK_DEPS, link_prefixes)
286316

287317
# Add dependencies to CMAKE_PREFIX_PATH
288318
env.set_path('CMAKE_PREFIX_PATH', build_link_prefixes)
289319

290-
# Install prefix
291-
env.set(SPACK_PREFIX, pkg.prefix)
292-
293-
# Install root prefix
294-
env.set(SPACK_INSTALL, spack.store.root)
295-
296320
# Set environment variables if specified for
297321
# the given compiler
298322
compiler = pkg.compiler
@@ -656,6 +680,11 @@ def setup_package(pkg, dirty):
656680
dpkg.setup_dependent_package(pkg.module, spec)
657681
dpkg.setup_dependent_environment(spack_env, run_env, spec)
658682

683+
if (not dirty) and (not spack_env.is_unset('CPATH')):
684+
tty.warn("A dependency has updated CPATH, this may lead pkg-config"
685+
" to assume that the package is part of the system"
686+
" includes and omit it when invoked with '--cflags'.")
687+
659688
set_module_variables_for_package(pkg)
660689
pkg.setup_environment(spack_env, run_env)
661690

lib/spack/spack/spec.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@
107107

108108
from spack.dependency import Dependency, all_deptypes, canonical_deptype
109109
from spack.util.module_cmd import get_path_from_module, load_module
110-
from spack.error import SpecError, UnsatisfiableSpecError
110+
from spack.error import SpackError, SpecError, UnsatisfiableSpecError
111111
from spack.provider_index import ProviderIndex
112112
from spack.util.crypto import prefix_bits
113113
from spack.util.executable import Executable
@@ -669,15 +669,15 @@ def _headers_default_handler(descriptor, spec, cls):
669669
HeaderList: The headers in ``prefix.include``
670670
671671
Raises:
672-
RuntimeError: If no headers are found
672+
NoHeadersError: If no headers are found
673673
"""
674674
headers = find_headers('*', root=spec.prefix.include, recursive=True)
675675

676676
if headers:
677677
return headers
678678
else:
679679
msg = 'Unable to locate {0} headers in {1}'
680-
raise RuntimeError(msg.format(spec.name, spec.prefix.include))
680+
raise NoHeadersError(msg.format(spec.name, spec.prefix.include))
681681

682682

683683
def _libs_default_handler(descriptor, spec, cls):
@@ -697,7 +697,7 @@ def _libs_default_handler(descriptor, spec, cls):
697697
LibraryList: The libraries found
698698
699699
Raises:
700-
RuntimeError: If no libraries are found
700+
NoLibrariesError: If no libraries are found
701701
"""
702702

703703
# Variable 'name' is passed to function 'find_libraries', which supports
@@ -737,7 +737,7 @@ def _libs_default_handler(descriptor, spec, cls):
737737
return libs
738738

739739
msg = 'Unable to recursively locate {0} libraries in {1}'
740-
raise RuntimeError(msg.format(spec.name, prefix))
740+
raise NoLibrariesError(msg.format(spec.name, prefix))
741741

742742

743743
class ForwardQueryToPackage(object):
@@ -3721,6 +3721,14 @@ class DuplicateCompilerSpecError(SpecError):
37213721
"""Raised when the same compiler occurs in a spec twice."""
37223722

37233723

3724+
class NoLibrariesError(SpackError):
3725+
"""Raised when package libraries are requested but cannot be found"""
3726+
3727+
3728+
class NoHeadersError(SpackError):
3729+
"""Raised when package headers are requested but cannot be found"""
3730+
3731+
37243732
class UnsupportedCompilerError(SpecError):
37253733
"""Raised when the user asks for a compiler spack doesn't know about."""
37263734
def __init__(self, compiler_name):

0 commit comments

Comments
 (0)