summaryrefslogtreecommitdiff
path: root/src/checkenv_resolver
diff options
context:
space:
mode:
Diffstat (limited to 'src/checkenv_resolver')
-rwxr-xr-xsrc/checkenv_resolver300
1 files changed, 300 insertions, 0 deletions
diff --git a/src/checkenv_resolver b/src/checkenv_resolver
new file mode 100755
index 0000000..6ebabaf
--- /dev/null
+++ b/src/checkenv_resolver
@@ -0,0 +1,300 @@
+#!/usr/bin/env python
+# Copyright (c) 2015, Joseph Hunkeler <jhunk at stsci.edu>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+try:
+ import argparse
+except ImportError:
+ print("Please install argparse.")
+ exit(1)
+
+import os
+import sys
+from string import Template
+
+
+class PackageException(Exception):
+ pass
+
+class Package(object):
+ def __init__(self, filename, load=True):
+ self.valid_keywords = [
+ 'Type',
+ 'Requires',
+ 'Precedes',
+ 'Synopsis',
+ 'Description',
+ 'Environment',
+ 'LdLibrary',
+ 'IncPath',
+ 'Root',
+ 'Path',
+ 'ManPath',
+ 'Default',
+ 'Source'
+ ]
+ self.filename = os.path.join(ENVCONFIG_PATH, filename)
+ self.exists = False
+ self.name = ''
+ self.name_internal = ''
+ self.description = ''
+ self.dependencies = []
+ self.precedence = []
+ self.priority = 0 # 0 - 99
+ self.shell = ''
+ self.env = {}
+ self.script = ''
+ self.invisible = False
+ self.mtime = 0
+ self.data = {}
+ self.verbose = False
+
+ if not load:
+ return
+
+ self.preload()
+
+ def __repr__(self):
+ return self.name
+
+ def preload(self):
+ if os.path.exists(self.filename):
+ self.exists = True
+
+ self.name = os.path.basename(self.filename.replace(' ', '_'))
+
+ if self.filename is not None \
+ and self.exists:
+ self.data = self.load()
+ self.get_requirements()
+ self.get_precedence()
+
+ def get_requirements(self):
+ if not self.data:
+ PackageException('{0}: Package data not loaded.'.format(self.name))
+
+ if 'Requires' in self.data:
+ for next_req in self.data['Requires']:
+ try:
+ req = Package(next_req)
+ except:
+ continue
+ self.dependencies.append(req)
+ if not req.exists and self.verbose:
+ print("Requirement warning, {0}: {1} does not exist".format(self.name, os.path.basename(req.filename)))
+
+ def get_precedence(self):
+ if not self.data:
+ raise PackageException('{0}: Package data not loaded.'.format(self.name))
+
+ if 'Precedes' in self.data:
+ for next_req in self.data['Precedes']:
+ req = Package(next_req, load=False)
+ self.precedence.append(req)
+ if not req.exists and self.verbose:
+ print("Precedence warning, {0}: {1} does not exist".format(self.name, os.path.basename(req.filename)))
+
+
+ def load(self):
+ pairs = []
+ comment = '#'
+ delimiter = ':'
+ keyword = ''
+ value = ''
+
+ with open(self.filename, 'r') as f:
+ for line in f.readlines():
+ line = line.strip()
+ if not line:
+ continue
+ if not line.find(delimiter):
+ continue
+ if line.startswith(comment):
+ continue
+
+ keyword = line[0:line.find(delimiter)]
+ if keyword not in self.valid_keywords:
+ continue
+ if keyword == 'Source':
+ continue
+
+ value = line[line.find(delimiter) + 1:].strip()
+ pairs.append([keyword, value])
+
+ # Do source block
+ with open(self.filename, 'r') as f:
+ value = ''
+ shell_type = ''
+ data_block = False
+
+ for line in f.readlines():
+ if line.startswith('Source:'):
+ data_block = True
+ shell_type = line[line.find(delimiter) + 1:line.rfind(delimiter)].strip()
+ line = line[line.rfind(delimiter) + 1:-1]
+ if data_block:
+ value += line
+ if line.startswith('"') and line.endswith('\\'):
+ data_block = False
+
+ pairs.append(['Source', value])
+ pairs.append(['Shell', shell_type])
+
+ pairs = dict(pairs)
+
+ # Alias the filename to be the same as "Root" value
+ if 'Root' in pairs:
+ pairs[self.name] = pairs['Root']
+
+ # Substitute configuration values
+ for key, value in pairs.items():
+ s = Template(value)
+ pairs[key] = s.safe_substitute(pairs)
+
+ if 'Requires' in pairs:
+ pairs['Requires'] = str(pairs['Requires']).split()
+
+ if 'Precedes' in pairs:
+ pairs['Precedes'] = str(pairs['Precedes']).split()
+
+ return dict(pairs)
+
+
+spaces = ''
+missing = ' Missing'.rjust(25, '.')
+def solver(p, style='dependency'):
+ ''' Recursive package dependency resolver
+ '''
+ flags = ''
+ tree = ' \\_'
+ resolver = p.dependencies
+
+ if args.no_graphics:
+ tree = ' '
+
+ if style != 'dependency':
+ resolver = p.precedence
+
+ # Wasn't planning on using a global here, but... recursion.
+ global spaces
+ global missing
+ for dep in resolver:
+ if not dep.exists:
+ flags = missing
+ print('{0} {1}{2:20s} {3}'.format(spaces, tree, dep.name, flags))
+ flags = ''
+ if dep.dependencies:
+ spaces += ' '
+ # Fall back in on yourself if there are more dependencies
+ # beyond the current 'dep'
+ solver(dep, style)
+ spaces = ''
+
+def show_package_map(packages, style='dependency'):
+ tree = '*--'
+ global missing
+
+ if args.no_graphics:
+ tree = ''
+
+ for pkg in pkgs:
+ flags = ''
+ if not pkg.exists:
+ flags = missing
+ print('{0}{1:22s} {2}'.format(tree, pkg.name, flags))
+ solver(pkg, style)
+ print('')
+
+
+if __name__ == '__main__':
+ args = None
+ ENVCONFIG_PATH = os.path.abspath('/usr/local/envconfig')
+ HOME = os.environ['HOME']
+ ENVRC = os.path.join(HOME, '.envrc')
+ pkgs = []
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-e', '--every', action='store_true', help='Generate map for all packages')
+ parser.add_argument('-r', '--requires', action='store_true', help='Omit package dependencies')
+ parser.add_argument('-p', '--precedence', action='store_true', help='Omit package precedence')
+ parser.add_argument('-w', '--warnings', action='store_true', help='No tree, only warnings')
+ parser.add_argument('-g', '--no-graphics', action='store_true', help='No tree characters')
+
+ args = parser.parse_args()
+
+
+ if args.every:
+ for r, d, fs in os.walk(ENVCONFIG_PATH):
+ for f in fs:
+ pkgs.append(Package(f))
+ else:
+ try:
+ with open(ENVRC, 'r') as fp:
+ for record in fp:
+ if record.startswith('#'):
+ continue
+
+ if record.startswith('Package:'):
+ _, _, record = record.partition(':')
+ else:
+ continue
+
+ record = record.strip()
+ pkgs.append(Package(record))
+ except OSError as e:
+ print(e.message)
+
+ if not args.requires and not args.precedence:
+ print('To see a summary of available options, issue -h or --help')
+ exit(0)
+
+ if args.warnings:
+ for pkg in pkgs:
+ pkg.verbose = True
+
+ if not pkg.exists:
+ # Normally the object performs its own checks to make sure
+ # it can read precedence, but we're overriding that functionality.
+ print('General failure, {0}: {1} does not exist'.format(pkg.name))
+
+ if args.requires:
+ pkg.get_requirements()
+ if args.precedence:
+ pkg.get_precedence()
+
+ exit(0)
+
+ if args.requires:
+ print("#### ####")
+ print("### Dependency Map ###")
+ print("#### ####")
+ show_package_map(pkgs)
+ print('')
+
+ if args.precedence:
+ print("#### ####")
+ print("### Precedence Map ###")
+ print("#### ####")
+ show_package_map(pkgs, style='precedence')
+ print('')
+
+ exit(0)