def _std_args(pkg): """Computes the standard cmake arguments for a generic package""" try: generator = pkg.generator except AttributeError: generator = 'Unix Makefiles' # Make sure a valid generator was chosen valid_primary_generators = ['Unix Makefiles', 'Ninja'] primary_generator = _extract_primary_generator(generator) if primary_generator not in valid_primary_generators: msg = "Invalid CMake generator: '{0}'\n".format(generator) msg += "CMakePackage currently supports the following " msg += "primary generators: '{0}'".\ format("', '".join(valid_primary_generators)) raise InstallError(msg) try: build_type = pkg.spec.variants['build_type'].value except KeyError: build_type = 'RelWithDebInfo' try: ipo = pkg.spec.variants['ipo'].value except KeyError: ipo = False define = CMakePackage.define args = [ '-G', generator, define('CMAKE_INSTALL_PREFIX', pkg.prefix), define('CMAKE_BUILD_TYPE', build_type), ] # CMAKE_INTERPROCEDURAL_OPTIMIZATION only exists for CMake >= 3.9 if pkg.spec.satisfies('^[email protected]:'): args.append(define('CMAKE_INTERPROCEDURAL_OPTIMIZATION', ipo)) if primary_generator == 'Unix Makefiles': args.append(define('CMAKE_VERBOSE_MAKEFILE', True)) if platform.mac_ver()[0]: args.extend([ define('CMAKE_FIND_FRAMEWORK', "LAST"), define('CMAKE_FIND_APPBUNDLE', "LAST"), ]) # Set up CMake rpath args.extend([ define('CMAKE_INSTALL_RPATH_USE_LINK_PATH', False), define('CMAKE_INSTALL_RPATH', spack.build_environment.get_rpaths(pkg)), ]) # CMake's find_package() looks in CMAKE_PREFIX_PATH first, help CMake # to find immediate link dependencies in right places: deps = [d.prefix for d in pkg.spec.dependencies(deptype=('build', 'link'))] deps = filter_system_paths(deps) args.append(define('CMAKE_PREFIX_PATH', deps)) return args
def test_filter_system_paths(self): filtered = filter_system_paths([ '/usr/local/Cellar/gcc/5.3.0/lib', '/usr/local/lib', '/usr/local/include', '/usr/local/lib64', '/usr/local/opt/some-package/lib', '/usr/opt/lib', '/lib', '/lib64', '/include', '/opt/some-package/include', ]) self.assertEqual(filtered, ['/usr/local/Cellar/gcc/5.3.0/lib', '/usr/local/opt/some-package/lib', '/usr/opt/lib', '/opt/some-package/include']) filtered = filter_system_bin_paths([ '/usr/local/Cellar/gcc/5.3.0/bin', '/usr/local/bin', '/usr/local/opt/some-package/bin', '/usr/opt/bin', '/bin', '/opt/some-package/bin', ]) self.assertEqual(filtered, ['/usr/local/bin', '/bin', '/usr/local/Cellar/gcc/5.3.0/bin', '/usr/local/opt/some-package/bin', '/usr/opt/bin', '/opt/some-package/bin'])
def get_cmake_prefix_path(pkg): build_deps = set(pkg.spec.dependencies(deptype=('build', 'test'))) link_deps = set(pkg.spec.traverse(root=False, deptype=('link'))) build_link_deps = build_deps | link_deps build_link_deps = _place_externals_last(build_link_deps) build_link_prefixes = filter_system_paths(x.prefix for x in build_link_deps) return build_link_prefixes
def test_filter_system_paths(miscellaneous_paths): """Tests that the filtering of system paths works as expected.""" filtered = filter_system_paths(miscellaneous_paths) expected = [ '/usr/local/Cellar/gcc/5.3.0/lib', '/usr/local/opt/some-package/lib', '/usr/opt/lib', '/opt/some-package/include', '/opt/some-package/local/..', ] assert filtered == expected
def test_filter_system_paths(miscellaneous_paths): """Tests that the filtering of system paths works as expected.""" filtered = filter_system_paths(miscellaneous_paths) expected = [ '/usr/local/Cellar/gcc/5.3.0/lib', '/usr/local/opt/some-package/lib', '/usr/opt/lib', '/opt/some-package/include', '/opt/some-package/local/..', ] assert filtered == expected
def get_rpaths(pkg): """Get a list of all the rpaths for a package.""" rpaths = [pkg.prefix.lib, pkg.prefix.lib64] deps = get_rpath_deps(pkg) rpaths.extend(d.prefix.lib for d in deps if os.path.isdir(d.prefix.lib)) rpaths.extend(d.prefix.lib64 for d in deps if os.path.isdir(d.prefix.lib64)) # Second module is our compiler mod name. We use that to get rpaths from # module show output. if pkg.compiler.modules and len(pkg.compiler.modules) > 1: rpaths.append(path_from_modules([pkg.compiler.modules[1]])) return list(dedupe(filter_system_paths(rpaths)))
def _std_args(pkg): """Computes the standard cmake arguments for a generic package""" try: generator = pkg.generator except AttributeError: generator = 'Unix Makefiles' # Make sure a valid generator was chosen valid_primary_generators = ['Unix Makefiles', 'Ninja'] primary_generator = _extract_primary_generator(generator) if primary_generator not in valid_primary_generators: msg = "Invalid CMake generator: '{0}'\n".format(generator) msg += "CMakePackage currently supports the following " msg += "primary generators: '{0}'".\ format("', '".join(valid_primary_generators)) raise InstallError(msg) try: build_type = pkg.spec.variants['build_type'].value except KeyError: build_type = 'RelWithDebInfo' args = [ '-G', generator, '-DCMAKE_INSTALL_PREFIX:PATH={0}'.format(pkg.prefix), '-DCMAKE_BUILD_TYPE:STRING={0}'.format(build_type), ] if primary_generator == 'Unix Makefiles': args.append('-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON') if platform.mac_ver()[0]: args.extend([ '-DCMAKE_FIND_FRAMEWORK:STRING=LAST', '-DCMAKE_FIND_APPBUNDLE:STRING=LAST' ]) # Set up CMake rpath args.append('-DCMAKE_INSTALL_RPATH_USE_LINK_PATH:BOOL=FALSE') rpaths = ';'.join(spack.build_environment.get_rpaths(pkg)) args.append('-DCMAKE_INSTALL_RPATH:STRING={0}'.format(rpaths)) # CMake's find_package() looks in CMAKE_PREFIX_PATH first, help CMake # to find immediate link dependencies in right places: deps = [ d.prefix for d in pkg.spec.dependencies(deptype=('build', 'link')) ] deps = filter_system_paths(deps) args.append('-DCMAKE_PREFIX_PATH:STRING={0}'.format(';'.join(deps))) return args
def setup_run_environment(self, env): if '+java' in self.spec: class_paths = find(self.prefix, '*.jar') classpath = os.pathsep.join(class_paths) env.prepend_path('CLASSPATH', classpath) # `spack test run gdal+python` requires these for the Python bindings # to find the correct libraries libs = [] for dep in self.spec.dependencies(deptype='link'): query = self.spec[dep.name] libs.extend(filter_system_paths(query.libs.directories)) if sys.platform == 'darwin': env.prepend_path('DYLD_FALLBACK_LIBRARY_PATH', ':'.join(libs)) else: env.prepend_path('LD_LIBRARY_PATH', ':'.join(libs))
def _parse_non_system_link_dirs(string): """Parses link paths out of compiler debug output. Args: string (str): compiler debug output as a string Returns: (list of str): implicit link paths parsed from the compiler output """ link_dirs = _parse_link_paths(string) # Return set of directories containing needed compiler libs, minus # system paths. Note that 'filter_system_paths' only checks for an # exact match, while 'in_system_subdirectory' checks if a path contains # a system directory as a subdirectory link_dirs = filter_system_paths(link_dirs) return list(p for p in link_dirs if not in_system_subdirectory(p))
def _std_args(pkg): """Computes the standard cmake arguments for a generic package""" try: generator = pkg.generator except AttributeError: generator = 'Unix Makefiles' # Make sure a valid generator was chosen valid_generators = ['Unix Makefiles', 'Ninja'] if generator not in valid_generators: msg = "Invalid CMake generator: '{0}'\n".format(generator) msg += "CMakePackage currently supports the following " msg += "generators: '{0}'".format("', '".join(valid_generators)) raise InstallError(msg) try: build_type = pkg.spec.variants['build_type'].value except KeyError: build_type = 'RelWithDebInfo' args = [ '-G', generator, '-DCMAKE_INSTALL_PREFIX:PATH={0}'.format(pkg.prefix), '-DCMAKE_BUILD_TYPE:STRING={0}'.format(build_type), '-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON' ] if platform.mac_ver()[0]: args.extend([ '-DCMAKE_FIND_FRAMEWORK:STRING=LAST', '-DCMAKE_FIND_APPBUNDLE:STRING=LAST' ]) # Set up CMake rpath args.append('-DCMAKE_INSTALL_RPATH_USE_LINK_PATH:BOOL=FALSE') rpaths = ';'.join(spack.build_environment.get_rpaths(pkg)) args.append('-DCMAKE_INSTALL_RPATH:STRING={0}'.format(rpaths)) # CMake's find_package() looks in CMAKE_PREFIX_PATH first, help CMake # to find immediate link dependencies in right places: deps = [d.prefix for d in pkg.spec.dependencies(deptype=('build', 'link'))] deps = filter_system_paths(deps) args.append('-DCMAKE_PREFIX_PATH:STRING={0}'.format(';'.join(deps))) return args
def test_filter_system_paths(self): filtered = filter_system_paths([ '/usr/local/Cellar/gcc/5.3.0/lib', '/usr/local/lib', '/usr/local', '/usr/local/include', '/usr/local/lib64', '/usr/local/opt/some-package/lib', '/usr/opt/lib', '/lib', '/', '/usr', '/lib64', '/include', '/opt/some-package/include', ]) self.assertEqual(filtered, ['/usr/local/Cellar/gcc/5.3.0/lib', '/usr/local/opt/some-package/lib', '/usr/opt/lib', '/opt/some-package/include']) filtered = filter_system_bin_paths([ '/usr/local/Cellar/gcc/5.3.0/bin', '/usr/local/bin', '/usr/local/opt/some-package/bin', '/usr/opt/bin', '/bin', '/opt/some-package/bin', ]) self.assertEqual(filtered, ['/usr/local/bin', '/bin', '/usr/local/Cellar/gcc/5.3.0/bin', '/usr/local/opt/some-package/bin', '/usr/opt/bin', '/opt/some-package/bin'])
def test_filter_system_paths(): expected = [p for p in test_paths if p.startswith('/nonsense_path')] filtered = envutil.filter_system_paths(test_paths) assert(expected == filtered)
def set_build_environment_variables(pkg, env, dirty): """Ensure a clean install environment when we build packages. This involves unsetting pesky environment variables that may affect the build. It also involves setting environment variables used by Spack's compiler wrappers. Args: pkg: The package we are building env: The build environment dirty (bool): Skip unsetting the user's environment settings """ # Gather information about various types of dependencies build_deps = set(pkg.spec.dependencies(deptype=('build', 'test'))) link_deps = set(pkg.spec.traverse(root=False, deptype=('link'))) build_link_deps = build_deps | link_deps rpath_deps = get_rpath_deps(pkg) link_dirs = [] include_dirs = [] rpath_dirs = [] # The top-level package is always RPATHed. It hasn't been installed yet # so the RPATHs are added unconditionally (e.g. even though lib64/ may # not be created for the install). for libdir in ['lib', 'lib64']: lib_path = os.path.join(pkg.prefix, libdir) rpath_dirs.append(lib_path) # Set up link, include, RPATH directories that are passed to the # compiler wrapper for dep in link_deps: if is_system_path(dep.prefix): continue query = pkg.spec[dep.name] dep_link_dirs = list() try: dep_link_dirs.extend(query.libs.directories) except NoLibrariesError: tty.debug("No libraries found for {0}".format(dep.name)) for default_lib_dir in ['lib', 'lib64']: default_lib_prefix = os.path.join(dep.prefix, default_lib_dir) if os.path.isdir(default_lib_prefix): dep_link_dirs.append(default_lib_prefix) link_dirs.extend(dep_link_dirs) if dep in rpath_deps: rpath_dirs.extend(dep_link_dirs) try: include_dirs.extend(query.headers.directories) except NoHeadersError: tty.debug("No headers found for {0}".format(dep.name)) link_dirs = list(dedupe(filter_system_paths(link_dirs))) include_dirs = list(dedupe(filter_system_paths(include_dirs))) rpath_dirs = list(dedupe(filter_system_paths(rpath_dirs))) env.set(SPACK_LINK_DIRS, ':'.join(link_dirs)) env.set(SPACK_INCLUDE_DIRS, ':'.join(include_dirs)) env.set(SPACK_RPATH_DIRS, ':'.join(rpath_dirs)) build_prefixes = [dep.prefix for dep in build_deps] build_link_prefixes = [dep.prefix for dep in build_link_deps] # add run-time dependencies of direct build-time dependencies: for build_dep in build_deps: for run_dep in build_dep.traverse(deptype='run'): build_prefixes.append(run_dep.prefix) # Filter out system paths: ['/', '/usr', '/usr/local'] # These paths can be introduced into the build when an external package # is added as a dependency. The problem with these paths is that they often # contain hundreds of other packages installed in the same directory. # If these paths come first, they can overshadow Spack installations. build_prefixes = filter_system_paths(build_prefixes) build_link_prefixes = filter_system_paths(build_link_prefixes) # Add dependencies to CMAKE_PREFIX_PATH env.set_path('CMAKE_PREFIX_PATH', build_link_prefixes) # Set environment variables if specified for # the given compiler compiler = pkg.compiler env.extend(spack.schema.environment.parse(compiler.environment)) if compiler.extra_rpaths: extra_rpaths = ':'.join(compiler.extra_rpaths) env.set('SPACK_COMPILER_EXTRA_RPATHS', extra_rpaths) # Add bin directories from dependencies to the PATH for the build. for prefix in build_prefixes: for dirname in ['bin', 'bin64']: bin_dir = os.path.join(prefix, dirname) if os.path.isdir(bin_dir): env.prepend_path('PATH', bin_dir) # Add spack build environment path with compiler wrappers first in # the path. We add the compiler wrapper path, which includes default # wrappers (cc, c++, f77, f90), AND a subdirectory containing # compiler-specific symlinks. The latter ensures that builds that # are sensitive to the *name* of the compiler see the right name when # we're building with the wrappers. # # Conflicts on case-insensitive systems (like "CC" and "cc") are # handled by putting one in the <build_env_path>/case-insensitive # directory. Add that to the path too. env_paths = [] compiler_specific = os.path.join( spack.paths.build_env_path, os.path.dirname(pkg.compiler.link_paths['cc'])) for item in [spack.paths.build_env_path, compiler_specific]: env_paths.append(item) ci = os.path.join(item, 'case-insensitive') if os.path.isdir(ci): env_paths.append(ci) for item in env_paths: env.prepend_path('PATH', item) env.set_path(SPACK_ENV_PATH, env_paths) # Working directory for the spack command itself, for debug logs. if spack.config.get('config:debug'): env.set(SPACK_DEBUG, 'TRUE') env.set(SPACK_SHORT_SPEC, pkg.spec.short_spec) env.set(SPACK_DEBUG_LOG_ID, pkg.spec.format('{name}-{hash:7}')) env.set(SPACK_DEBUG_LOG_DIR, spack.main.spack_working_dir) # Find ccache binary and hand it to build environment if spack.config.get('config:ccache'): ccache = Executable('ccache') if not ccache: raise RuntimeError("No ccache binary found in PATH") env.set(SPACK_CCACHE_BINARY, ccache) # Add any pkgconfig directories to PKG_CONFIG_PATH for prefix in build_link_prefixes: for directory in ('lib', 'lib64', 'share'): pcdir = os.path.join(prefix, directory, 'pkgconfig') if os.path.isdir(pcdir): env.prepend_path('PKG_CONFIG_PATH', pcdir) return env
def set_build_environment_variables(pkg, env, dirty): """Ensure a clean install environment when we build packages. This involves unsetting pesky environment variables that may affect the build. It also involves setting environment variables used by Spack's compiler wrappers. Args: pkg: The package we are building env: The build environment dirty (bool): Skip unsetting the user's environment settings """ # Gather information about various types of dependencies build_deps = set(pkg.spec.dependencies(deptype=('build', 'test'))) link_deps = set(pkg.spec.traverse(root=False, deptype=('link'))) build_link_deps = build_deps | link_deps rpath_deps = get_rpath_deps(pkg) # This includes all build dependencies and any other dependencies that # should be added to PATH (e.g. supporting executables run by build # dependencies) build_and_supporting_deps = set() for build_dep in build_deps: build_and_supporting_deps.update(build_dep.traverse(deptype='run')) # Establish an arbitrary but fixed ordering of specs so that resulting # environment variable values are stable def _order(specs): return sorted(specs, key=lambda x: x.name) # External packages may be installed in a prefix which contains many other # package installs. To avoid having those installations override # Spack-installed packages, they are placed at the end of search paths. # System prefixes are removed entirely later on since they are already # searched. build_deps = _place_externals_last(_order(build_deps)) link_deps = _place_externals_last(_order(link_deps)) build_link_deps = _place_externals_last(_order(build_link_deps)) rpath_deps = _place_externals_last(_order(rpath_deps)) build_and_supporting_deps = _place_externals_last( _order(build_and_supporting_deps)) link_dirs = [] include_dirs = [] rpath_dirs = [] # The top-level package is always RPATHed. It hasn't been installed yet # so the RPATHs are added unconditionally (e.g. even though lib64/ may # not be created for the install). for libdir in ['lib', 'lib64']: lib_path = os.path.join(pkg.prefix, libdir) rpath_dirs.append(lib_path) # Set up link, include, RPATH directories that are passed to the # compiler wrapper for dep in link_deps: if is_system_path(dep.prefix): continue query = pkg.spec[dep.name] dep_link_dirs = list() try: dep_link_dirs.extend(query.libs.directories) except NoLibrariesError: tty.debug("No libraries found for {0}".format(dep.name)) for default_lib_dir in ['lib', 'lib64']: default_lib_prefix = os.path.join(dep.prefix, default_lib_dir) if os.path.isdir(default_lib_prefix): dep_link_dirs.append(default_lib_prefix) link_dirs.extend(dep_link_dirs) if dep in rpath_deps: rpath_dirs.extend(dep_link_dirs) try: include_dirs.extend(query.headers.directories) except NoHeadersError: tty.debug("No headers found for {0}".format(dep.name)) link_dirs = list(dedupe(filter_system_paths(link_dirs))) include_dirs = list(dedupe(filter_system_paths(include_dirs))) rpath_dirs = list(dedupe(filter_system_paths(rpath_dirs))) env.set(SPACK_LINK_DIRS, ':'.join(link_dirs)) env.set(SPACK_INCLUDE_DIRS, ':'.join(include_dirs)) env.set(SPACK_RPATH_DIRS, ':'.join(rpath_dirs)) build_and_supporting_prefixes = filter_system_paths( x.prefix for x in build_and_supporting_deps) build_link_prefixes = filter_system_paths(x.prefix for x in build_link_deps) # Add dependencies to CMAKE_PREFIX_PATH env.set_path('CMAKE_PREFIX_PATH', build_link_prefixes) # Set environment variables if specified for # the given compiler compiler = pkg.compiler env.extend(spack.schema.environment.parse(compiler.environment)) if compiler.extra_rpaths: extra_rpaths = ':'.join(compiler.extra_rpaths) env.set('SPACK_COMPILER_EXTRA_RPATHS', extra_rpaths) # Add bin directories from dependencies to the PATH for the build. # These directories are added to the beginning of the search path, and in # the order given by 'build_and_supporting_prefixes' (the iteration order # is reversed because each entry is prepended) for prefix in reversed(build_and_supporting_prefixes): for dirname in ['bin', 'bin64']: bin_dir = os.path.join(prefix, dirname) if os.path.isdir(bin_dir): env.prepend_path('PATH', bin_dir) # Add spack build environment path with compiler wrappers first in # the path. We add the compiler wrapper path, which includes default # wrappers (cc, c++, f77, f90), AND a subdirectory containing # compiler-specific symlinks. The latter ensures that builds that # are sensitive to the *name* of the compiler see the right name when # we're building with the wrappers. # # Conflicts on case-insensitive systems (like "CC" and "cc") are # handled by putting one in the <build_env_path>/case-insensitive # directory. Add that to the path too. env_paths = [] compiler_specific = os.path.join( spack.paths.build_env_path, os.path.dirname(pkg.compiler.link_paths['cc'])) for item in [spack.paths.build_env_path, compiler_specific]: env_paths.append(item) ci = os.path.join(item, 'case-insensitive') if os.path.isdir(ci): env_paths.append(ci) for item in env_paths: env.prepend_path('PATH', item) env.set_path(SPACK_ENV_PATH, env_paths) # Working directory for the spack command itself, for debug logs. if spack.config.get('config:debug'): env.set(SPACK_DEBUG, 'TRUE') env.set(SPACK_SHORT_SPEC, pkg.spec.short_spec) env.set(SPACK_DEBUG_LOG_ID, pkg.spec.format('{name}-{hash:7}')) env.set(SPACK_DEBUG_LOG_DIR, spack.main.spack_working_dir) # Find ccache binary and hand it to build environment if spack.config.get('config:ccache'): ccache = Executable('ccache') if not ccache: raise RuntimeError("No ccache binary found in PATH") env.set(SPACK_CCACHE_BINARY, ccache) # Add any pkgconfig directories to PKG_CONFIG_PATH for prefix in reversed(build_link_prefixes): for directory in ('lib', 'lib64', 'share'): pcdir = os.path.join(prefix, directory, 'pkgconfig') if os.path.isdir(pcdir): env.prepend_path('PKG_CONFIG_PATH', pcdir) return env
def set_build_environment_variables(pkg, env, dirty): """Ensure a clean install environment when we build packages. This involves unsetting pesky environment variables that may affect the build. It also involves setting environment variables used by Spack's compiler wrappers. Args: pkg: The package we are building env: The build environment dirty (bool): Skip unsetting the user's environment settings """ # Gather information about various types of dependencies build_deps = set(pkg.spec.dependencies(deptype=('build', 'test'))) link_deps = set(pkg.spec.traverse(root=False, deptype=('link'))) build_link_deps = build_deps | link_deps rpath_deps = get_rpath_deps(pkg) build_prefixes = [dep.prefix for dep in build_deps] link_prefixes = [dep.prefix for dep in link_deps] build_link_prefixes = [dep.prefix for dep in build_link_deps] rpath_prefixes = [dep.prefix for dep in rpath_deps] # add run-time dependencies of direct build-time dependencies: for build_dep in build_deps: for run_dep in build_dep.traverse(deptype='run'): build_prefixes.append(run_dep.prefix) # Filter out system paths: ['/', '/usr', '/usr/local'] # These paths can be introduced into the build when an external package # is added as a dependency. The problem with these paths is that they often # contain hundreds of other packages installed in the same directory. # If these paths come first, they can overshadow Spack installations. build_prefixes = filter_system_paths(build_prefixes) link_prefixes = filter_system_paths(link_prefixes) build_link_prefixes = filter_system_paths(build_link_prefixes) rpath_prefixes = filter_system_paths(rpath_prefixes) # Prefixes of all of the package's dependencies go in SPACK_DEPENDENCIES env.set_path(SPACK_DEPENDENCIES, build_link_prefixes) # These variables control compiler wrapper behavior env.set_path(SPACK_RPATH_DEPS, rpath_prefixes) env.set_path(SPACK_LINK_DEPS, link_prefixes) # Add dependencies to CMAKE_PREFIX_PATH env.set_path('CMAKE_PREFIX_PATH', build_link_prefixes) # Install prefix env.set(SPACK_PREFIX, pkg.prefix) # Install root prefix env.set(SPACK_INSTALL, spack.store.root) # Set environment variables if specified for # the given compiler compiler = pkg.compiler environment = compiler.environment for command, variable in iteritems(environment): if command == 'set': for name, value in iteritems(variable): env.set(name, value) elif command == 'unset': for name, _ in iteritems(variable): env.unset(name) elif command == 'prepend-path': for name, value in iteritems(variable): env.prepend_path(name, value) elif command == 'append-path': for name, value in iteritems(variable): env.append_path(name, value) if compiler.extra_rpaths: extra_rpaths = ':'.join(compiler.extra_rpaths) env.set('SPACK_COMPILER_EXTRA_RPATHS', extra_rpaths) # Add bin directories from dependencies to the PATH for the build. for prefix in build_prefixes: for dirname in ['bin', 'bin64']: bin_dir = os.path.join(prefix, dirname) if os.path.isdir(bin_dir): env.prepend_path('PATH', bin_dir) # Add spack build environment path with compiler wrappers first in # the path. We add the compiler wrapper path, which includes default # wrappers (cc, c++, f77, f90), AND a subdirectory containing # compiler-specific symlinks. The latter ensures that builds that # are sensitive to the *name* of the compiler see the right name when # we're building with the wrappers. # # Conflicts on case-insensitive systems (like "CC" and "cc") are # handled by putting one in the <build_env_path>/case-insensitive # directory. Add that to the path too. env_paths = [] compiler_specific = os.path.join( spack.paths.build_env_path, pkg.compiler.name) for item in [spack.paths.build_env_path, compiler_specific]: env_paths.append(item) ci = os.path.join(item, 'case-insensitive') if os.path.isdir(ci): env_paths.append(ci) for item in reversed(env_paths): env.prepend_path('PATH', item) env.set_path(SPACK_ENV_PATH, env_paths) # Working directory for the spack command itself, for debug logs. if spack.config.get('config:debug'): env.set(SPACK_DEBUG, 'TRUE') env.set(SPACK_SHORT_SPEC, pkg.spec.short_spec) env.set(SPACK_DEBUG_LOG_ID, pkg.spec.format('${PACKAGE}-${HASH:7}')) env.set(SPACK_DEBUG_LOG_DIR, spack.main.spack_working_dir) # Find ccache binary and hand it to build environment if spack.config.get('config:ccache'): ccache = Executable('ccache') if not ccache: raise RuntimeError("No ccache binary found in PATH") env.set(SPACK_CCACHE_BINARY, ccache) # Add any pkgconfig directories to PKG_CONFIG_PATH for prefix in build_link_prefixes: for directory in ('lib', 'lib64', 'share'): pcdir = os.path.join(prefix, directory, 'pkgconfig') if os.path.isdir(pcdir): env.prepend_path('PKG_CONFIG_PATH', pcdir) return env
def test_filter_system_paths(): nonsense_prefix = 'C:\\nonsense_path' if is_windows else '/nonsense_path' expected = [p for p in test_paths if p.startswith(nonsense_prefix)] filtered = envutil.filter_system_paths(test_paths) assert (expected == filtered)
def set_build_environment_variables(pkg, env, dirty): """Ensure a clean install environment when we build packages. This involves unsetting pesky environment variables that may affect the build. It also involves setting environment variables used by Spack's compiler wrappers. Args: pkg: The package we are building env: The build environment dirty (bool): Skip unsetting the user's environment settings """ # Gather information about various types of dependencies build_deps = set(pkg.spec.dependencies(deptype=('build', 'test'))) link_deps = set(pkg.spec.traverse(root=False, deptype=('link'))) build_link_deps = build_deps | link_deps rpath_deps = get_rpath_deps(pkg) build_prefixes = [dep.prefix for dep in build_deps] link_prefixes = [dep.prefix for dep in link_deps] build_link_prefixes = [dep.prefix for dep in build_link_deps] rpath_prefixes = [dep.prefix for dep in rpath_deps] # add run-time dependencies of direct build-time dependencies: for build_dep in build_deps: for run_dep in build_dep.traverse(deptype='run'): build_prefixes.append(run_dep.prefix) # Filter out system paths: ['/', '/usr', '/usr/local'] # These paths can be introduced into the build when an external package # is added as a dependency. The problem with these paths is that they often # contain hundreds of other packages installed in the same directory. # If these paths come first, they can overshadow Spack installations. build_prefixes = filter_system_paths(build_prefixes) link_prefixes = filter_system_paths(link_prefixes) build_link_prefixes = filter_system_paths(build_link_prefixes) rpath_prefixes = filter_system_paths(rpath_prefixes) # Prefixes of all of the package's dependencies go in SPACK_DEPENDENCIES env.set_path(SPACK_DEPENDENCIES, build_link_prefixes) # These variables control compiler wrapper behavior env.set_path(SPACK_RPATH_DEPS, rpath_prefixes) env.set_path(SPACK_LINK_DEPS, link_prefixes) # Add dependencies to CMAKE_PREFIX_PATH env.set_path('CMAKE_PREFIX_PATH', build_link_prefixes) # Install prefix env.set(SPACK_PREFIX, pkg.prefix) # Install root prefix env.set(SPACK_INSTALL, spack.store.root) # Stuff in here sanitizes the build environment to eliminate # anything the user has set that may interfere. if not dirty: # Remove these vars from the environment during build because they # can affect how some packages find libraries. We want to make # sure that builds never pull in unintended external dependencies. env.unset('LD_LIBRARY_PATH') env.unset('LIBRARY_PATH') env.unset('CPATH') env.unset('LD_RUN_PATH') env.unset('DYLD_LIBRARY_PATH') # Remove any macports installs from the PATH. The macports ld can # cause conflicts with the built-in linker on el capitan. Solves # assembler issues, e.g.: # suffix or operands invalid for `movq'" path = get_path('PATH') for p in path: if '/macports/' in p: env.remove_path('PATH', p) # Set environment variables if specified for # the given compiler compiler = pkg.compiler environment = compiler.environment if 'set' in environment: env_to_set = environment['set'] for key, value in iteritems(env_to_set): env.set('SPACK_ENV_SET_%s' % key, value) env.set('%s' % key, value) # Let shell know which variables to set env_variables = ":".join(env_to_set.keys()) env.set('SPACK_ENV_TO_SET', env_variables) if compiler.extra_rpaths: extra_rpaths = ':'.join(compiler.extra_rpaths) env.set('SPACK_COMPILER_EXTRA_RPATHS', extra_rpaths) # Add bin directories from dependencies to the PATH for the build. for prefix in build_prefixes: for dirname in ['bin', 'bin64']: bin_dir = os.path.join(prefix, dirname) if os.path.isdir(bin_dir): env.prepend_path('PATH', bin_dir) # Add spack build environment path with compiler wrappers first in # the path. We add the compiler wrapper path, which includes default # wrappers (cc, c++, f77, f90), AND a subdirectory containing # compiler-specific symlinks. The latter ensures that builds that # are sensitive to the *name* of the compiler see the right name when # we're building with the wrappers. # # Conflicts on case-insensitive systems (like "CC" and "cc") are # handled by putting one in the <build_env_path>/case-insensitive # directory. Add that to the path too. env_paths = [] compiler_specific = os.path.join(spack.paths.build_env_path, pkg.compiler.name) for item in [spack.paths.build_env_path, compiler_specific]: env_paths.append(item) ci = os.path.join(item, 'case-insensitive') if os.path.isdir(ci): env_paths.append(ci) for item in reversed(env_paths): env.prepend_path('PATH', item) env.set_path(SPACK_ENV_PATH, env_paths) # Working directory for the spack command itself, for debug logs. if spack.config.get('config:debug'): env.set(SPACK_DEBUG, 'TRUE') env.set(SPACK_SHORT_SPEC, pkg.spec.short_spec) env.set(SPACK_DEBUG_LOG_ID, pkg.spec.format('${PACKAGE}-${HASH:7}')) env.set(SPACK_DEBUG_LOG_DIR, spack.main.spack_working_dir) # Find ccache binary and hand it to build environment if spack.config.get('config:ccache'): ccache = Executable('ccache') if not ccache: raise RuntimeError("No ccache binary found in PATH") env.set(SPACK_CCACHE_BINARY, ccache) # Add any pkgconfig directories to PKG_CONFIG_PATH for prefix in build_link_prefixes: for directory in ('lib', 'lib64', 'share'): pcdir = os.path.join(prefix, directory, 'pkgconfig') if os.path.isdir(pcdir): env.prepend_path('PKG_CONFIG_PATH', pcdir) return env
def _parse_implicit_rpaths(string): """Parse implicit link paths from compiler debug output. This gives the compiler runtime library paths that we need to add to the RPATH of generated binaries and libraries. It allows us to ensure, e.g., that codes load the right libstdc++ for their compiler. """ lib_search_paths = False raw_link_dirs = [] tty.debug('parsing implicit link info') for line in string.splitlines(): if lib_search_paths: if line.startswith('\t'): raw_link_dirs.append(line[1:]) continue else: lib_search_paths = False elif line.startswith('Library search paths:'): lib_search_paths = True if not _LINKER_LINE.match(line): continue if _LINKER_LINE_IGNORE.match(line): continue tty.debug('linker line: %s' % line) next_arg = False for arg in line.split(): if arg in ('-L', '-Y'): next_arg = True continue if next_arg: raw_link_dirs.append(arg) next_arg = False continue link_dir_arg = _LINK_DIR_ARG.match(arg) if link_dir_arg: link_dir = link_dir_arg.group('dir') tty.debug('linkdir: %s' % link_dir) raw_link_dirs.append(link_dir) link_dir_arg = _LIBPATH_ARG.match(arg) if link_dir_arg: link_dir = link_dir_arg.group('dir') tty.debug('libpath: %s', link_dir) raw_link_dirs.append(link_dir) tty.debug('found raw link dirs: %s' % ', '.join(raw_link_dirs)) implicit_link_dirs = list() visited = set() for link_dir in raw_link_dirs: normalized_path = os.path.abspath(link_dir) if normalized_path not in visited: implicit_link_dirs.append(normalized_path) visited.add(normalized_path) implicit_link_dirs = filter_system_paths(implicit_link_dirs) # Additional filtering: we also want to exclude paths that are # subdirectories of /usr/lib/ and /lib/ implicit_link_dirs = list( path for path in implicit_link_dirs if not any(is_subdirectory(path, d) for d in ['/lib/', '/usr/lib/'])) tty.debug('found link dirs: %s' % ', '.join(implicit_link_dirs)) return implicit_link_dirs