summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJohn W. Parent <45471568+johnwparent@users.noreply.github.com>2024-09-06 08:26:46 -0400
committerGitHub <noreply@github.com>2024-09-06 14:26:46 +0200
commit4042afaa99426d8ed35643e531b69a8012fbd3f2 (patch)
tree5ff5555897e77f6538a192ee623595b54bbccc90 /lib
parent7fdf1029b747555f1d3040075bfeaf322ee6837e (diff)
downloadspack-4042afaa99426d8ed35643e531b69a8012fbd3f2.tar.gz
spack-4042afaa99426d8ed35643e531b69a8012fbd3f2.tar.bz2
spack-4042afaa99426d8ed35643e531b69a8012fbd3f2.tar.xz
spack-4042afaa99426d8ed35643e531b69a8012fbd3f2.zip
Bootstrap GnuPG and `file` on Windows (#41810)
Spack can now bootstrap two new dependencies on Windows: GnuPG, and file. These dependencies are modeled as a separate package, and they install a cross-compiled binary. Details on how they binaries are built are in https://github.com/spack/windows-bootstrap-resources
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/llnl/util/filesystem.py74
-rw-r--r--lib/spack/spack/binary_distribution.py3
-rw-r--r--lib/spack/spack/bootstrap/__init__.py2
-rw-r--r--lib/spack/spack/bootstrap/core.py23
-rw-r--r--lib/spack/spack/bootstrap/status.py2
-rw-r--r--lib/spack/spack/package.py1
-rw-r--r--lib/spack/spack/relocate.py6
-rw-r--r--lib/spack/spack/util/filesystem.py102
8 files changed, 130 insertions, 83 deletions
diff --git a/lib/spack/llnl/util/filesystem.py b/lib/spack/llnl/util/filesystem.py
index 308c6154e1..54ace7d42f 100644
--- a/lib/spack/llnl/util/filesystem.py
+++ b/lib/spack/llnl/util/filesystem.py
@@ -27,8 +27,6 @@ from llnl.util import tty
from llnl.util.lang import dedupe, memoized
from llnl.util.symlink import islink, readlink, resolve_link_target_relative_to_the_link, symlink
-from spack.util.executable import Executable, which
-
from ..path import path_to_os_path, system_path_filter
if sys.platform != "win32":
@@ -53,7 +51,6 @@ __all__ = [
"find_all_headers",
"find_libraries",
"find_system_libraries",
- "fix_darwin_install_name",
"force_remove",
"force_symlink",
"getuid",
@@ -248,42 +245,6 @@ def path_contains_subdirectory(path, root):
return norm_path.startswith(norm_root)
-@memoized
-def file_command(*args):
- """Creates entry point to `file` system command with provided arguments"""
- file_cmd = which("file", required=True)
- for arg in args:
- file_cmd.add_default_arg(arg)
- return file_cmd
-
-
-@memoized
-def _get_mime_type():
- """Generate method to call `file` system command to aquire mime type
- for a specified path
- """
- if sys.platform == "win32":
- # -h option (no-dereference) does not exist in Windows
- return file_command("-b", "--mime-type")
- else:
- return file_command("-b", "-h", "--mime-type")
-
-
-def mime_type(filename):
- """Returns the mime type and subtype of a file.
-
- Args:
- filename: file to be analyzed
-
- Returns:
- Tuple containing the MIME type and subtype
- """
- output = _get_mime_type()(filename, output=str, error=str).strip()
- tty.debug("==> " + output)
- type, _, subtype = output.partition("/")
- return type, subtype
-
-
#: This generates the library filenames that may appear on any OS.
library_extensions = ["a", "la", "so", "tbd", "dylib"]
@@ -1679,41 +1640,6 @@ def safe_remove(*files_or_dirs):
raise
-@system_path_filter
-def fix_darwin_install_name(path):
- """Fix install name of dynamic libraries on Darwin to have full path.
-
- There are two parts of this task:
-
- 1. Use ``install_name('-id', ...)`` to change install name of a single lib
- 2. Use ``install_name('-change', ...)`` to change the cross linking between
- libs. The function assumes that all libraries are in one folder and
- currently won't follow subfolders.
-
- Parameters:
- path (str): directory in which .dylib files are located
- """
- libs = glob.glob(join_path(path, "*.dylib"))
- for lib in libs:
- # fix install name first:
- install_name_tool = Executable("install_name_tool")
- install_name_tool("-id", lib, lib)
- otool = Executable("otool")
- long_deps = otool("-L", lib, output=str).split("\n")
- deps = [dep.partition(" ")[0][1::] for dep in long_deps[2:-1]]
- # fix all dependencies:
- for dep in deps:
- for loc in libs:
- # We really want to check for either
- # dep == os.path.basename(loc) or
- # dep == join_path(builddir, os.path.basename(loc)),
- # but we don't know builddir (nor how symbolic links look
- # in builddir). We thus only compare the basenames.
- if os.path.basename(dep) == os.path.basename(loc):
- install_name_tool("-change", dep, loc, lib)
- break
-
-
def find_first(root: str, files: Union[Iterable[str], str], bfs_depth: int = 2) -> Optional[str]:
"""Find the first file matching a pattern.
diff --git a/lib/spack/spack/binary_distribution.py b/lib/spack/spack/binary_distribution.py
index 8d3c3cfb7a..15189f9d43 100644
--- a/lib/spack/spack/binary_distribution.py
+++ b/lib/spack/spack/binary_distribution.py
@@ -54,6 +54,7 @@ import spack.user_environment
import spack.util.archive
import spack.util.crypto
import spack.util.file_cache as file_cache
+import spack.util.filesystem as ssys
import spack.util.gpg
import spack.util.parallel
import spack.util.path
@@ -687,7 +688,7 @@ def get_buildfile_manifest(spec):
# Non-symlinks.
for rel_path in visitor.files:
abs_path = os.path.join(root, rel_path)
- m_type, m_subtype = fsys.mime_type(abs_path)
+ m_type, m_subtype = ssys.mime_type(abs_path)
if relocate.needs_binary_relocation(m_type, m_subtype):
# Why is this branch not part of needs_binary_relocation? :(
diff --git a/lib/spack/spack/bootstrap/__init__.py b/lib/spack/spack/bootstrap/__init__.py
index 85935cd0e0..d710caee68 100644
--- a/lib/spack/spack/bootstrap/__init__.py
+++ b/lib/spack/spack/bootstrap/__init__.py
@@ -9,6 +9,7 @@ from .core import (
all_core_root_specs,
ensure_clingo_importable_or_raise,
ensure_core_dependencies,
+ ensure_file_in_path_or_raise,
ensure_gpg_in_path_or_raise,
ensure_patchelf_in_path_or_raise,
)
@@ -19,6 +20,7 @@ __all__ = [
"is_bootstrapping",
"ensure_bootstrap_configuration",
"ensure_core_dependencies",
+ "ensure_file_in_path_or_raise",
"ensure_gpg_in_path_or_raise",
"ensure_clingo_importable_or_raise",
"ensure_patchelf_in_path_or_raise",
diff --git a/lib/spack/spack/bootstrap/core.py b/lib/spack/spack/bootstrap/core.py
index 62b6b86570..02909cbdf7 100644
--- a/lib/spack/spack/bootstrap/core.py
+++ b/lib/spack/spack/bootstrap/core.py
@@ -472,7 +472,8 @@ def ensure_clingo_importable_or_raise() -> None:
def gnupg_root_spec() -> str:
"""Return the root spec used to bootstrap GnuPG"""
- return _root_spec("gnupg@2.3:")
+ root_spec_name = "win-gpg" if IS_WINDOWS else "gnupg"
+ return _root_spec(f"{root_spec_name}@2.3:")
def ensure_gpg_in_path_or_raise() -> None:
@@ -482,6 +483,19 @@ def ensure_gpg_in_path_or_raise() -> None:
)
+def file_root_spec() -> str:
+ """Return the root spec used to bootstrap file"""
+ root_spec_name = "win-file" if IS_WINDOWS else "file"
+ return _root_spec(root_spec_name)
+
+
+def ensure_file_in_path_or_raise() -> None:
+ """Ensure file is in the PATH or raise"""
+ return ensure_executables_in_path_or_raise(
+ executables=["file"], abstract_spec=file_root_spec()
+ )
+
+
def patchelf_root_spec() -> str:
"""Return the root spec used to bootstrap patchelf"""
# 0.13.1 is the last version not to require C++17.
@@ -565,14 +579,15 @@ def ensure_core_dependencies() -> None:
"""Ensure the presence of all the core dependencies."""
if sys.platform.lower() == "linux":
ensure_patchelf_in_path_or_raise()
- if not IS_WINDOWS:
- ensure_gpg_in_path_or_raise()
+ elif sys.platform == "win32":
+ ensure_file_in_path_or_raise()
+ ensure_gpg_in_path_or_raise()
ensure_clingo_importable_or_raise()
def all_core_root_specs() -> List[str]:
"""Return a list of all the core root specs that may be used to bootstrap Spack"""
- return [clingo_root_spec(), gnupg_root_spec(), patchelf_root_spec()]
+ return [clingo_root_spec(), gnupg_root_spec(), patchelf_root_spec(), file_root_spec()]
def bootstrapping_sources(scope: Optional[str] = None):
diff --git a/lib/spack/spack/bootstrap/status.py b/lib/spack/spack/bootstrap/status.py
index 582927af6e..6d3270b42c 100644
--- a/lib/spack/spack/bootstrap/status.py
+++ b/lib/spack/spack/bootstrap/status.py
@@ -88,7 +88,7 @@ def _core_requirements() -> List[RequiredResponseType]:
def _buildcache_requirements() -> List[RequiredResponseType]:
_buildcache_exes = {
- "file": _missing("file", "required to analyze files for buildcaches"),
+ "file": _missing("file", "required to analyze files for buildcaches", system_only=False),
("gpg2", "gpg"): _missing("gpg2", "required to sign/verify buildcaches", False),
}
if platform.system().lower() == "darwin":
diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py
index d0b7beda1d..99135b4834 100644
--- a/lib/spack/spack/package.py
+++ b/lib/spack/spack/package.py
@@ -104,6 +104,7 @@ from spack.package_base import (
from spack.spec import InvalidSpecDetected, Spec
from spack.util.cpus import determine_number_of_jobs
from spack.util.executable import *
+from spack.util.filesystem import file_command, fix_darwin_install_name, mime_type
from spack.variant import (
any_combination_of,
auto_or_any_combination_of,
diff --git a/lib/spack/spack/relocate.py b/lib/spack/spack/relocate.py
index 357dd92f84..364e72f7c3 100644
--- a/lib/spack/spack/relocate.py
+++ b/lib/spack/spack/relocate.py
@@ -12,7 +12,6 @@ from typing import List, Optional
import macholib.mach_o
import macholib.MachO
-import llnl.util.filesystem as fs
import llnl.util.lang
import llnl.util.tty as tty
from llnl.util.lang import memoized
@@ -25,6 +24,7 @@ import spack.spec
import spack.store
import spack.util.elf as elf
import spack.util.executable as executable
+import spack.util.filesystem as ssys
import spack.util.path
from .relocate_text import BinaryFilePrefixReplacer, TextFilePrefixReplacer
@@ -664,7 +664,7 @@ def is_binary(filename):
Returns:
True or False
"""
- m_type, _ = fs.mime_type(filename)
+ m_type, _ = ssys.mime_type(filename)
msg = "[{0}] -> ".format(filename)
if m_type == "application":
@@ -692,7 +692,7 @@ def fixup_macos_rpath(root, filename):
True if fixups were applied, else False
"""
abspath = os.path.join(root, filename)
- if fs.mime_type(abspath) != ("application", "x-mach-binary"):
+ if ssys.mime_type(abspath) != ("application", "x-mach-binary"):
return False
# Get Mach-O header commands
diff --git a/lib/spack/spack/util/filesystem.py b/lib/spack/spack/util/filesystem.py
new file mode 100644
index 0000000000..b296438fe8
--- /dev/null
+++ b/lib/spack/spack/util/filesystem.py
@@ -0,0 +1,102 @@
+# Copyright 2013-2024 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+
+"""
+Utilities for interacting with files,
+like those in llnl.util.filesystem, but which require logic from spack.util
+"""
+
+import glob
+import os
+import sys
+
+from llnl.util import tty
+from llnl.util.filesystem import join_path
+from llnl.util.lang import memoized
+
+from spack.util.executable import Executable, which
+
+
+def _ensure_file_on_win():
+ """Ensures the file command is available on Windows
+ If not, it is bootstrapped.
+ No-op on all other platforms"""
+ if sys.platform != "win32":
+ return
+ import spack.bootstrap
+
+ with spack.bootstrap.ensure_bootstrap_configuration():
+ spack.bootstrap.ensure_file_in_path_or_raise()
+
+
+@memoized
+def file_command(*args):
+ """Creates entry point to `file` system command with provided arguments"""
+ _ensure_file_on_win()
+ file_cmd = which("file", required=True)
+ for arg in args:
+ file_cmd.add_default_arg(arg)
+ return file_cmd
+
+
+@memoized
+def _get_mime_type():
+ """Generate method to call `file` system command to aquire mime type
+ for a specified path
+ """
+ if sys.platform == "win32":
+ # -h option (no-dereference) does not exist in Windows
+ return file_command("-b", "--mime-type")
+ else:
+ return file_command("-b", "-h", "--mime-type")
+
+
+def mime_type(filename):
+ """Returns the mime type and subtype of a file.
+
+ Args:
+ filename: file to be analyzed
+
+ Returns:
+ Tuple containing the MIME type and subtype
+ """
+ output = _get_mime_type()(filename, output=str, error=str).strip()
+ tty.debug("==> " + output)
+ type, _, subtype = output.partition("/")
+ return type, subtype
+
+
+def fix_darwin_install_name(path):
+ """Fix install name of dynamic libraries on Darwin to have full path.
+
+ There are two parts of this task:
+
+ 1. Use ``install_name('-id', ...)`` to change install name of a single lib
+ 2. Use ``install_name('-change', ...)`` to change the cross linking between
+ libs. The function assumes that all libraries are in one folder and
+ currently won't follow subfolders.
+
+ Parameters:
+ path (str): directory in which .dylib files are located
+ """
+ libs = glob.glob(join_path(path, "*.dylib"))
+ for lib in libs:
+ # fix install name first:
+ install_name_tool = Executable("install_name_tool")
+ install_name_tool("-id", lib, lib)
+ otool = Executable("otool")
+ long_deps = otool("-L", lib, output=str).split("\n")
+ deps = [dep.partition(" ")[0][1::] for dep in long_deps[2:-1]]
+ # fix all dependencies:
+ for dep in deps:
+ for loc in libs:
+ # We really want to check for either
+ # dep == os.path.basename(loc) or
+ # dep == join_path(builddir, os.path.basename(loc)),
+ # but we don't know builddir (nor how symbolic links look
+ # in builddir). We thus only compare the basenames.
+ if os.path.basename(dep) == os.path.basename(loc):
+ install_name_tool("-change", dep, loc, lib)
+ break