diff options
Diffstat (limited to 'cpp/install/common/components.py')
-rwxr-xr-x | cpp/install/common/components.py | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/cpp/install/common/components.py b/cpp/install/common/components.py new file mode 100755 index 00000000000..13df10db731 --- /dev/null +++ b/cpp/install/common/components.py @@ -0,0 +1,320 @@ +# +# Defines Ice components +# + +import ConfigParser, sys, os, logging, fnmatch, os.path, shutil, re + +def listFileLists(): + """Information routine for getting lists of file lists from a components file""" + cfg = ConfigParser.SafeConfigParser() + cfg.read("./components/components.ini") + for f in cfg.sections(): + try: + if cfg.getint(f, "active") == 1: + for item, value in cfg.items(f): + if item.startswith("filelist"): + print value + except ConfigParser.NoOptionError: + continue + +class StageFileError: + """Thrown when there is a problem with the stage configuration file.""" + def __init__(self, msg = None): + self.msg = msg + + def __str__(self): + return repr(self.msg) + +class ComponentDefError: + """Indicates a component definition file has an error that provides + proper interpretation""" + + def __init__(self, msg = None): + self.msg = msg + + def __str__(self): + return repr(self.msg) + +class FileSpecError(ComponentDefError): + """Indicates a filespec component definition file has a syntactical + error""" + def __init__(self, msg = None): + ComponentDefError.__init__(self, msg) + +def recursiveListing(path): + """Provides a recursive directory listing based in path""" + result = [] + files = os.listdir(path) + for x in files: + fullpath = os.path.join(path, x); + if os.path.isdir(fullpath) and not os.path.islink(fullpath): + result.extend(recursiveListing(fullpath)) + else: + result.append(fullpath) + return result + +class FileSpecWorker: + def __init__(self, source, dest): + self.source = source + self.dest = dest + self.include = [] + self.exclude = [] + self.explicit = [] + + def add(self, filename): + parts = filename.split("=") + if len(parts) < 2: + # + # This line doesn"t have a separator and so assume its an + # explicit listing + # + self.explicit.append(filename) + return + + if parts[0].startswith("include"): + self.include.append(parts[1].strip()) + elif parts[0].startswith("exclude"): + self.exclude.append(parts[1].strip()) + elif parts[0].startswith("expect"): + pass + else: + raise FileSpecError("Line \'%s\' does not match filespec schema." % filename) + + def execute(self, fake = False): + """Copy all of the specified files.""" + recursiveIncludes = [] + recursiveExcludes = [] + + midmatchIncludes = [] + midmatchExcludes = [] + + localIncludes = [] + localExcludes = [] + + for f in self.include: + if f.startswith("**/"): + if f.endswith("/**"): + midmatchIncludes.append(".*%s.*" %f[3:len(f) -3].replace('/', '.')) + else: + recursiveIncludes.append(f[3:]) + else: + if f.endswith("/**"): + midmatchIncludes.append("%s.*" %f[0:len(f) -3].replace('/', '.')) + else: + localIncludes.append(f) + + for f in self.exclude: + if f.startswith("**/"): + if f.endswith("/**"): + midmatchExcludes.append(".*%s.*" % f[3:len(f) -3].replace('/', '.')) + else: + recursiveExcludes.append(f[3:]) + else: + if f.endswith("/**"): + midmatchExcludes.append("%s.*" %f[0:len(f) -3].replace('/', '.')) + else: + localExcludes.append(f) + + logging.debug('localIncludes: ' + str(localIncludes)) + logging.debug('localExcludes: ' + str(localExcludes)) + logging.debug('recursiveIncludes: ' + str(recursiveIncludes)) + logging.debug('recursiveExcludes: ' + str(recursiveExcludes)) + logging.debug('midmatchIncludes: ' + str(midmatchIncludes)) + logging.debug('midmatchExcludes: ' + str(midmatchExcludes)) + + fullListing = [] + result = [] + files = os.listdir(self.source) + + for f in files: + fullpath = os.path.join(self.source, f); + if os.path.isdir(fullpath) and not os.path.islink(fullpath): + fullListing.extend(recursiveListing(fullpath)) + continue + + for p in localIncludes + recursiveIncludes: + if fnmatch.fnmatch(f, p): + found = False + for x in localExcludes + recursiveExcludes: + if fnmatch.fnmatch(f, x): + found = True + break + if not found: result.append(f) + + inmatches = [] + for p in recursiveIncludes: + inmatches.extend(fnmatch.filter(fullListing, p)) + + inSet = set(inmatches) + + for p in midmatchIncludes: + r = re.compile(p) + inmatches = [] + for f in fullListing(f): + rel = f[len(self.source):].strip('\\/') + if not r.match(rel) == None: + inmatches.append(f) + inSet = inSet.union(set(inmatches)) + + outmatches = [] + for x in recursiveExcludes: + outmatches.extend(fnmatch.filter(fullListing, x)) + + outSet = set(outmatches) + + for x in midmatchExcludes: + r = re.compile(x) + outmatches = [] + for f in fullListing: + rel = f[len(self.source):].strip('\\/') + if not r.match(rel) == None: + outmatches.append(f) + outSet = outSet.union(set(outmatches)) + + + # + # Using sets is the "easiest" way to do this. If Python"s set + # implementation is/gets buggy then this needs to be written + # "longhand". + # + diff = inSet - outSet + result.extend(list(diff)) + + for i in range(0, len(result)): + if result[i].startswith(self.source): + result[i] = result[i][len(self.source):].strip('\\/') + + result.sort() + result.extend(self.explicit) + + if fake: + for f in result: + print "Copying %s from %s to %s" % (f, self.source, self.dest) + return + + if logging.getLogger().getEffectiveLevel() == logging.DEBUG: + logging.debug("Files to be copied:") + for f in result: + logging.debug(f) + + for f in result: + # + # an f, prefix means flatten. + # + flatten = False + current = f + if f.startswith('f,'): + flatten = True + current = current[2:] + + targetDirectory = self.dest + targetFile = os.path.basename(current) + if not flatten: + targetDirectory = os.path.join(self.dest, os.path.dirname(current)) + + if not os.path.exists(targetDirectory): + os.makedirs(targetDirectory) + + s = os.path.join(self.source, current) + d = os.path.join(targetDirectory, targetFile) + try: + shutil.copy2(s, d) + except IOError, e: + logging.info('Copying %s to %s failed: %s' % (s, d, str(e))) + raise + +def stage(filename, componentdir, stageDirectory, group, defaults): + cfg = ConfigParser.SafeConfigParser() + cfg.read(filename) + if not cfg.has_section(group): + raise StageFileError("Section %s is missing from component list file." % group) + + sections = [] + for entry in cfg.options(group): + section = cfg.get(group, entry) + if not cfg.has_section(section): + raise StageFileError("Section %s is missing from component list file." % section) + sections.append((entry, section)) + + for stageLoc, section in sections: + try: + currentBase = stageDirectory + for f in stageLoc.split('-'): + currentBase = os.path.join(currentBase, f) + + if not os.path.exists(currentBase): + os.makedirs(currentBase) + + elementCount = cfg.getint(section, "elements") + if elementCount < 0: + raise StageFileError("Section %s elements field is negative value" % section) + + for i in range(1, elementCount + 1): + try: + if cfg.has_option(section, "targets"): + target = cfg.get(section, "targets", False, defaults).strip() + + if "~" + defaults["target"] in target: + continue + elif not defaults["target"] in target: + continue + + source = cfg.get(section, "source%d" % i, False, defaults) + filelist = cfg.get(section, "filelist%d" % i, False, defaults) + dest = cfg.get(section, "dest%d" % i, False, defaults) + # + # Most configurations won"t have file templates. These are usually only used so that file lists can + # be reused but need to be slightly modified for each use case. + # + template = None + mapping = None + if cfg.has_option(section, "filetemplate%d" % i): + # + # We need to get the raw value! + # + template = cfg.get(section, "filetemplate%d" %i, True) + mapping = defaults + + filename = os.path.join(componentdir, filelist) + if not (os.path.exists(filename) and os.path.isfile(filename)): + raise StageFileError("Component file %s does not exist or is not a file" % filename) + + componentFile = file(filename, "r") + try: + worker = FileSpecWorker(source, os.path.join(currentBase, dest)) + for line in componentFile: + current = line.strip() + if line.startswith('#'): + continue + + if len(current) == 0: + continue + if not template == None: + mapping['name'] = current + computedName = template % mapping + logging.log(logging.DEBUG, 'Adding templatized name %s' % computedName) + worker.add(computedName) + else: + worker.add(current % defaults) + + if worker == None: + msg = "Component file %s is empty." % filename + logging.warning(msg) + else: + # + # XXX- fake is set to true while we are + # debugging. + # + worker.execute(False) + + finally: + componentFile.close() + + except ConfigParser.NoOptionError: + raise StageFileError("Section %s has invalid value for element %d" % (section, i)) + + except ConfigParser.NoOptionError: + raise StageFileError("Section %s has invalid or missing elements field" % section) + +if __name__ == "__main__": + print 'components' |