diff --git a/bin/spack b/bin/spack index 454a9a5b2d..cc9450ade7 100755 --- a/bin/spack +++ b/bin/spack @@ -156,8 +156,8 @@ def main(): import spack.util.debug as debug debug.register_interrupt_handler() - from spack.yaml_version_check import check_yaml_versions - check_yaml_versions() + # Run any available pre-run hooks + spack.hooks.pre_run() spack.spack_working_dir = working_dir if args.mock: diff --git a/lib/spack/spack/hooks/__init__.py b/lib/spack/spack/hooks/__init__.py index ff4ebc2e57..6454a865b6 100644 --- a/lib/spack/spack/hooks/__init__.py +++ b/lib/spack/spack/hooks/__init__.py @@ -64,16 +64,19 @@ class HookRunner(object): def __init__(self, hook_name): self.hook_name = hook_name - def __call__(self, pkg): + def __call__(self, *args, **kwargs): for module in all_hook_modules(): if hasattr(module, self.hook_name): hook = getattr(module, self.hook_name) if hasattr(hook, '__call__'): - hook(pkg) + hook(*args, **kwargs) + # # Define some functions that can be called to fire off hooks. # +pre_run = HookRunner('pre_run') + pre_install = HookRunner('pre_install') post_install = HookRunner('post_install') diff --git a/lib/spack/spack/hooks/case_consistency.py b/lib/spack/spack/hooks/case_consistency.py new file mode 100644 index 0000000000..faf38f7ae3 --- /dev/null +++ b/lib/spack/spack/hooks/case_consistency.py @@ -0,0 +1,101 @@ +############################################################################## +# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://github.com/llnl/spack +# Please also see the LICENSE file for our notice and the LGPL. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License (as +# published by the Free Software Foundation) version 2.1, February 1999. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and +# conditions of the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +############################################################################## +from __future__ import absolute_import +import os +import re +import platform + +from llnl.util.filesystem import * + +import spack +from spack.util.executable import * + + +def pre_run(): + if platform.system() != "Darwin": + return + + git_case_consistency_check(spack.repo.get_repo('builtin').packages_path) + + +def git_case_consistency_check(path): + """Re-sync case of files in a directory with git. + + On case-insensitive but case-preserving filesystems like Mac OS X, + Git doesn't properly rename files that only had their case changed. + + This checks files in a directory against git and does a + case-restoring rename (actually two renames, e.g.:: + + name -> tmp -> NAME + + We use this in Spack to ensure package directories are named + correctly. + + TODO: this check can probably be removed once package names have been + TODO: lowercase for a long while. + + """ + with working_dir(path): + # Don't bother fixing case if Spack isn't in a git repository + git = which('git') + if not git: + return + + try: + git_filenames = git('ls-tree', '--name-only', 'HEAD', output=str) + git_filenames = set(re.split(r'\s+', git_filenames.strip())) + except ProcessError: + return # Ignore errors calling git + + lower_to_mixed = {} + for fn in git_filenames: + lower = fn.lower() + mixed = lower_to_mixed.setdefault(lower, []) + mixed.append(fn) + + # Iterate through all actual files and make sure their names are + # the same as corresponding names in git + actual_filenames = os.listdir('.') + for actual in actual_filenames: + lower = actual.lower() + + # not tracked by git + if lower not in lower_to_mixed: + continue + + # Don't know what to do with multiple matches + if len(lower_to_mixed[lower]) != 1: + continue + + # Skip if case is already correct + git_name = lower_to_mixed[lower][0] + if git_name == actual: + continue + + # restore case with two renames + tmp_name = actual + '.spack.tmp' + os.rename(actual, tmp_name) + os.rename(tmp_name, git_name) diff --git a/lib/spack/spack/yaml_version_check.py b/lib/spack/spack/hooks/yaml_version_check.py similarity index 98% rename from lib/spack/spack/yaml_version_check.py rename to lib/spack/spack/hooks/yaml_version_check.py index 2c5b511d7f..a4b38198bc 100644 --- a/lib/spack/spack/yaml_version_check.py +++ b/lib/spack/spack/hooks/yaml_version_check.py @@ -31,7 +31,7 @@ import spack.config -def check_yaml_versions(): +def pre_run(): check_compiler_yaml_version()