Source code for spack.util.filesystem

# 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 edit_in_place_through_temporary_file
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()


[docs] @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")
[docs] 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
[docs] 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(os.path.join(path, "*.dylib")) install_name_tool = Executable("install_name_tool") otool = Executable("otool") for lib in libs: args = ["-id", lib] 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): args.extend(("-change", dep, loc)) break with edit_in_place_through_temporary_file(lib) as tmp: install_name_tool(*args, tmp)