#!/usr/bin/env python
+"""Generates config files for Android file system properties.
+This script is used for generating configuration files for configuring
+Android filesystem properties. Internally, its composed of a plug-able
+interface to support the understanding of new input and output parameters.
+
+Run the help for a list of supported plugins and their capabilities.
+
+Further documentation can be found in the README.
+"""
+
+import argparse
import ConfigParser
import re
import sys
+import textwrap
+
+
+# Lowercase generator used to be inline with @staticmethod.
+class generator(object): # pylint: disable=invalid-name
+ """A decorator class to add commandlet plugins.
+
+ Used as a decorator to classes to add them to
+ the internal plugin interface. Plugins added
+ with @generator() are automatically added to
+ the command line.
+
+ For instance, to add a new generator
+ called foo and have it added just do this:
+
+ @generator("foo")
+ class FooGen(object):
+ ...
+ """
+ _generators = {}
+
+ def __init__(self, gen):
+ """
+ Args:
+ gen (str): The name of the generator to add.
+
+ Raises:
+ ValueError: If there is a similarly named generator already added.
+
+ """
+ self._gen = gen
+
+ if gen in generator._generators:
+ raise ValueError('Duplicate generator name: ' + gen)
+
+ generator._generators[gen] = None
+
+ def __call__(self, cls):
+
+ generator._generators[self._gen] = cls()
+ return cls
+
+ @staticmethod
+ def get():
+ """Gets the list of generators.
+
+ Returns:
+ The list of registered generators.
+ """
+ return generator._generators
+
+
+class AID(object):
+ """This class represents an Android ID or an AID.
+
+ Attributes:
+ identifier (str): The identifier name for a #define.
+ value (str) The User Id (uid) of the associate define.
+ found (str) The file it was found in, can be None.
+ normalized_value (str): Same as value, but base 10.
+ """
+
+ def __init__(self, identifier, value, found):
+ """
+ Args:
+ identifier: The identifier name for a #define <identifier>.
+ value: The value of the AID, aka the uid.
+ found (str): The file found in, not required to be specified.
+
+ Raises:
+ ValueError: if value is not a valid string number as processed by
+ int(x, 0)
+ """
+ self.identifier = identifier
+ self.value = value
+ self.found = found
+ self.normalized_value = str(int(value, 0))
+
+
+class FSConfig(object):
+ """Represents a filesystem config array entry.
+
+ Represents a file system configuration entry for specifying
+ file system capabilities.
+
+ Attributes:
+ mode (str): The mode of the file or directory.
+ user (str): The uid or #define identifier (AID_SYSTEM)
+ group (str): The gid or #define identifier (AID_SYSTEM)
+ caps (str): The capability set.
+ filename (str): The file it was found in.
+ """
+
+ def __init__(self, mode, user, group, caps, path, filename):
+ """
+ Args:
+ mode (str): The mode of the file or directory.
+ user (str): The uid or #define identifier (AID_SYSTEM)
+ group (str): The gid or #define identifier (AID_SYSTEM)
+ caps (str): The capability set as a list.
+ filename (str): The file it was found in.
+ """
+ self.mode = mode
+ self.user = user
+ self.group = group
+ self.caps = caps
+ self.path = path
+ self.filename = filename
+
+
+class FSConfigFileParser(object):
+ """Parses a config.fs ini format file.
+
+ This class is responsible for parsing the config.fs ini format files.
+ It collects and checks all the data in these files and makes it available
+ for consumption post processed.
+ """
+ # from system/core/include/private/android_filesystem_config.h
+ _AID_OEM_RESERVED_RANGES = [
+ (2900, 2999),
+ (5000, 5999),
+ ]
+
+ _AID_MATCH = re.compile('AID_[a-zA-Z]+')
+
+ def __init__(self, config_files):
+ """
+ Args:
+ config_files ([str]): The list of config.fs files to parse.
+ Note the filename is not important.
+ """
+
+ self._files = []
+ self._dirs = []
+ self._aids = []
+
+ self._seen_paths = {}
+ # (name to file, value to aid)
+ self._seen_aids = ({}, {})
+
+ self._config_files = config_files
+
+ for config_file in self._config_files:
+ self._parse(config_file)
+
+ def _parse(self, file_name):
+ """Parses and verifies config.fs files. Internal use only.
+
+ Args:
+ file_name (str): The config.fs (PythonConfigParser file format)
+ file to parse.
+
+ Raises:
+ Anything raised by ConfigParser.read()
+ """
+
+ # Separate config parsers for each file found. If you use
+ # read(filenames...) later files can override earlier files which is
+ # not what we want. Track state across files and enforce with
+ # _handle_dup(). Note, strict ConfigParser is set to true in
+ # Python >= 3.2, so in previous versions same file sections can
+ # override previous
+ # sections.
+ config = ConfigParser.ConfigParser()
+ config.read(file_name)
-GENERATED = '''
-/*
- * THIS IS AN AUTOGENERATED FILE! DO NOT MODIFY
- */
-'''
+ for section in config.sections():
-INCLUDE = '#include <private/android_filesystem_config.h>'
+ if FSConfigFileParser._AID_MATCH.match(
+ section) and config.has_option(section, 'value'):
+ FSConfigFileParser._handle_dup('AID', file_name, section,
+ self._seen_aids[0])
+ self._seen_aids[0][section] = file_name
+ self._handle_aid(file_name, section, config)
+ else:
+ FSConfigFileParser._handle_dup('path', file_name, section,
+ self._seen_paths)
+ self._seen_paths[section] = file_name
+ self._handle_path(file_name, section, config)
+
+ # sort entries:
+ # * specified path before prefix match
+ # ** ie foo before f*
+ # * lexicographical less than before other
+ # ** ie boo before foo
+ # Given these paths:
+ # paths=['ac', 'a', 'acd', 'an', 'a*', 'aa', 'ac*']
+ # The sort order would be:
+ # paths=['a', 'aa', 'ac', 'acd', 'an', 'ac*', 'a*']
+ # Thus the fs_config tools will match on specified paths before
+ # attempting prefix, and match on the longest matching prefix.
+ self._files.sort(key=FSConfigFileParser._file_key)
+
+ # sort on value of (file_name, name, value, strvalue)
+ # This is only cosmetic so AIDS are arranged in ascending order
+ # within the generated file.
+ self._aids.sort(key=lambda item: item.normalized_value)
+
+ def _handle_aid(self, file_name, section_name, config):
+ """Verifies an AID entry and adds it to the aid list.
+
+ Calls sys.exit() with a descriptive message of the failure.
+
+ Args:
+ file_name (str): The filename of the config file being parsed.
+ section_name (str): The section name currently being parsed.
+ config (ConfigParser): The ConfigParser section being parsed that
+ the option values will come from.
+ """
+
+ def error_message(msg):
+ """Creates an error message with current parsing state."""
+ return '{} for: "{}" file: "{}"'.format(msg, section_name,
+ file_name)
+
+ value = config.get(section_name, 'value')
+
+ if not value:
+ sys.exit(error_message('Found specified but unset "value"'))
-DEFINE_NO_DIRS = '#define NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS\n'
-DEFINE_NO_FILES = '#define NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_FILES\n'
+ try:
+ aid = AID(section_name, value, file_name)
+ except ValueError:
+ sys.exit(
+ error_message('Invalid "value", not aid number, got: \"%s\"' %
+ value))
-DEFAULT_WARNING = '#warning No device-supplied android_filesystem_config.h, using empty default.'
+ # Values must be within OEM range.
+ if not any(lower <= int(aid.value, 0) <= upper
+ for (lower, upper
+ ) in FSConfigFileParser._AID_OEM_RESERVED_RANGES):
+ emsg = '"value" not in valid range %s, got: %s'
+ emsg = emsg % (str(FSConfigFileParser._AID_OEM_RESERVED_RANGES),
+ value)
+ sys.exit(error_message(emsg))
-NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS_ENTRY = '{ 00000, AID_ROOT, AID_ROOT, 0, "system/etc/fs_config_dirs" },'
-NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_FILES_ENTRY = '{ 00000, AID_ROOT, AID_ROOT, 0, "system/etc/fs_config_files" },'
+ # use the normalized int value in the dict and detect
+ # duplicate definitions of the same value
+ if aid.normalized_value in self._seen_aids[1]:
+ # map of value to aid name
+ aid = self._seen_aids[1][aid.normalized_value]
-IFDEF_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS = '#ifdef NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS'
-ENDIF = '#endif'
+ # aid name to file
+ file_name = self._seen_aids[0][aid]
-OPEN_FILE_STRUCT = 'static const struct fs_path_config android_device_files[] = {'
-OPEN_DIR_STRUCT = 'static const struct fs_path_config android_device_dirs[] = {'
-CLOSE_FILE_STRUCT = '};'
+ emsg = 'Duplicate AID value "%s" found on AID: "%s".' % (
+ value, self._seen_aids[1][aid.normalized_value])
+ emsg += ' Previous found in file: "%s."' % file_name
+ sys.exit(error_message(emsg))
-GENERIC_DEFINE = "#define %s\t%s"
+ self._seen_aids[1][aid.normalized_value] = section_name
-FILE_COMMENT = '// Defined in file: \"%s\"'
+ # Append aid tuple of (AID_*, base10(value), _path(value))
+ # We keep the _path version of value so we can print that out in the
+ # generated header so investigating parties can identify parts.
+ # We store the base10 value for sorting, so everything is ascending
+ # later.
+ self._aids.append(aid)
-# from system/core/include/private/android_filesystem_config.h
-AID_OEM_RESERVED_RANGES = [
- (2900, 2999),
- (5000, 5999),
-]
+ def _handle_path(self, file_name, section_name, config):
+ """Add a file capability entry to the internal list.
+ Handles a file capability entry, verifies it, and adds it to
+ to the internal dirs or files list based on path. If it ends
+ with a / its a dir. Internal use only.
-AID_MATCH = re.compile('AID_[a-zA-Z]+')
+ Calls sys.exit() on any validation error with message set.
-def handle_aid(file_name, section_name, config, aids, seen_aids):
- value = config.get(section_name, 'value')
+ Args:
+ file_name (str): The current name of the file being parsed.
+ section_name (str): The name of the section to parse.
+ config (str): The config parser.
+ """
- errmsg = '%s for: \"' + section_name + '" file: \"' + file_name + '\"'
+ mode = config.get(section_name, 'mode')
+ user = config.get(section_name, 'user')
+ group = config.get(section_name, 'group')
+ caps = config.get(section_name, 'caps')
- if not value:
- raise Exception(errmsg % 'Found specified but unset "value"')
+ errmsg = ('Found specified but unset option: \"%s" in file: \"' +
+ file_name + '\"')
- v = convert_int(value)
- if not v:
- raise Exception(errmsg % ('Invalid "value", not a number, got: \"%s\"' % value))
+ if not mode:
+ sys.exit(errmsg % 'mode')
- # Values must be within OEM range
- if not any(lower <= v <= upper for (lower, upper) in AID_OEM_RESERVED_RANGES):
- s = '"value" not in valid range %s, got: %s'
- s = s % (str(AID_OEM_RESERVED_RANGES), value)
- raise Exception(errmsg % s)
+ if not user:
+ sys.exit(errmsg % 'user')
- # use the normalized int value in the dict and detect
- # duplicate definitions of the same vallue
- v = str(v)
- if v in seen_aids[1]:
- # map of value to aid name
- a = seen_aids[1][v]
+ if not group:
+ sys.exit(errmsg % 'group')
- # aid name to file
- f = seen_aids[0][a]
+ if not caps:
+ sys.exit(errmsg % 'caps')
- s = 'Duplicate AID value "%s" found on AID: "%s".' % (value, seen_aids[1][v])
- s += ' Previous found in file: "%s."' % f
- raise Exception(errmsg % s)
+ caps = caps.split()
- seen_aids[1][v] = section_name
+ tmp = []
+ for cap in caps:
+ try:
+ # test if string is int, if it is, use as is.
+ int(cap, 0)
+ tmp.append('(' + cap + ')')
+ except ValueError:
+ tmp.append('(1ULL << CAP_' + cap.upper() + ')')
- # Append a tuple of (AID_*, base10(value), str(value))
- # We keep the str version of value so we can print that out in the
- # generated header so investigating parties can identify parts.
- # We store the base10 value for sorting, so everything is ascending
- # later.
- aids.append((file_name, section_name, v, value))
+ caps = tmp
-def convert_int(num):
+ if len(mode) == 3:
+ mode = '0' + mode
try:
- if num.startswith('0x'):
- return int(num, 16)
- elif num.startswith('0b'):
- return int(num, 2)
- elif num.startswith('0'):
- return int(num, 8)
- else:
- return int(num, 10)
+ int(mode, 8)
except ValueError:
- pass
- return None
+ sys.exit('Mode must be octal characters, got: "%s"' % mode)
+
+ if len(mode) != 4:
+ sys.exit('Mode must be 3 or 4 characters, got: "%s"' % mode)
+
+ caps_str = '|'.join(caps)
+
+ entry = FSConfig(mode, user, group, caps_str, section_name, file_name)
+ if section_name[-1] == '/':
+ self._dirs.append(entry)
+ else:
+ self._files.append(entry)
+
+ @property
+ def files(self):
+ """Get the list of FSConfig file entries.
+
+ Returns:
+ a list of FSConfig() objects for file paths.
+ """
+ return self._files
+
+ @property
+ def dirs(self):
+ """Get the list of FSConfig dir entries.
+
+ Returns:
+ a list of FSConfig() objects for directory paths.
+ """
+ return self._dirs
+
+ @property
+ def aids(self):
+ """Get the list of AID entries.
+
+ Returns:
+ a list of AID() objects.
+ """
+ return self._aids
+
+ @staticmethod
+ def _file_key(fs_config):
+ """Used as the key paramter to sort.
+
+ This is used as a the function to the key parameter of a sort.
+ it wraps the string supplied in a class that implements the
+ appropriate __lt__ operator for the sort on path strings. See
+ StringWrapper class for more details.
+
+ Args:
+ fs_config (FSConfig): A FSConfig entry.
+
+ Returns:
+ A StringWrapper object
+ """
+
+ # Wrapper class for custom prefix matching strings
+ class StringWrapper(object):
+ """Wrapper class used for sorting prefix strings.
+
+ The algorithm is as follows:
+ - specified path before prefix match
+ - ie foo before f*
+ - lexicographical less than before other
+ - ie boo before foo
+
+ Given these paths:
+ paths=['ac', 'a', 'acd', 'an', 'a*', 'aa', 'ac*']
+ The sort order would be:
+ paths=['a', 'aa', 'ac', 'acd', 'an', 'ac*', 'a*']
+ Thus the fs_config tools will match on specified paths before
+ attempting prefix, and match on the longest matching prefix.
+ """
+
+ def __init__(self, path):
+ """
+ Args:
+ path (str): the path string to wrap.
+ """
+ self.is_prefix = path[-1] == '*'
+ if self.is_prefix:
+ self.path = path[:-1]
+ else:
+ self.path = path
+
+ def __lt__(self, other):
+
+ # if were both suffixed the smallest string
+ # is 'bigger'
+ if self.is_prefix and other.is_prefix:
+ result = len(self.path) > len(other.path)
+ # If I am an the suffix match, im bigger
+ elif self.is_prefix:
+ result = False
+ # If other is the suffix match, he's bigger
+ elif other.is_prefix:
+ result = True
+ # Alphabetical
+ else:
+ result = self.path < other.path
+ return result
-def handle_path(file_name, section_name, config, files, dirs):
+ return StringWrapper(fs_config.path)
- mode = config.get(section_name, 'mode')
- user = config.get(section_name, 'user')
- group = config.get(section_name, 'group')
- caps = config.get(section_name, 'caps')
+ @staticmethod
+ def _handle_dup(name, file_name, section_name, seen):
+ """Tracks and detects duplicates. Internal use only.
- errmsg = 'Found specified but unset option: \"%s" in file: \"' + file_name + '\"'
+ Calls sys.exit() on a duplicate.
- if not mode:
- raise Exception(errmsg % 'mode')
+ Args:
+ name (str): The name to use in the error reporting. The pretty
+ name for the section.
+ file_name (str): The file currently being parsed.
+ section_name (str): The name of the section. This would be path
+ or identifier depending on what's being parsed.
+ seen (dict): The dictionary of seen things to check against.
+ """
+ if section_name in seen:
+ dups = '"' + seen[section_name] + '" and '
+ dups += file_name
+ sys.exit('Duplicate %s "%s" found in files: %s' %
+ (name, section_name, dups))
- if not user:
- raise Exception(errmsg % 'user')
+ seen[section_name] = file_name
- if not group:
- raise Exception(errmsg % 'group')
- if not caps:
- raise Exception(errmsg % 'caps')
+class BaseGenerator(object):
+ """Interface for Generators.
- caps = caps.split()
+ Base class for generators, generators should implement
+ these method stubs.
+ """
- tmp = []
- for x in caps:
- if convert_int(x):
- tmp.append('(' + x + ')')
- else:
- tmp.append('(1ULL << CAP_' + x.upper() + ')')
+ def add_opts(self, opt_group):
+ """Used to add per-generator options to the command line.
- caps = tmp
+ Args:
+ opt_group (argument group object): The argument group to append to.
+ See the ArgParse docs for more details.
+ """
- path = '"' + section_name + '"'
+ raise NotImplementedError("Not Implemented")
- if len(mode) == 3:
- mode = '0' + mode
+ def __call__(self, args):
+ """This is called to do whatever magic the generator does.
- try:
- int(mode, 8)
- except:
- raise Exception('Mode must be octal characters, got: "' + mode + '"')
+ Args:
+ args (dict): The arguments from ArgParse as a dictionary.
+ ie if you specified an argument of foo in add_opts, access
+ it via args['foo']
+ """
- if len(mode) != 4:
- raise Exception('Mode must be 3 or 4 characters, got: "' + mode + '"')
+ raise NotImplementedError("Not Implemented")
- caps = '|'.join(caps)
+@generator('fsconfig')
+class FSConfigGen(BaseGenerator):
+ """Generates the android_filesystem_config.h file.
- x = [ mode, user, group, caps, section_name ]
- if section_name[-1] == '/':
- dirs.append((file_name, x))
- else:
- files.append((file_name, x))
+ Output is used in generating fs_config_files and fs_config_dirs.
+ """
-def handle_dup(name, file_name, section_name, seen):
- if section_name in seen:
- dups = '"' + seen[section_name] + '" and '
- dups += file_name
- raise Exception('Duplicate ' + name + ' "' + section_name + '" found in files: ' + dups)
+ _GENERATED = textwrap.dedent("""\
+ /*
+ * THIS IS AN AUTOGENERATED FILE! DO NOT MODIFY
+ */
+ """)
-def parse(file_name, files, dirs, aids, seen_paths, seen_aids):
+ _INCLUDE = '#include <private/android_filesystem_config.h>'
- config = ConfigParser.ConfigParser()
- config.read(file_name)
+ _DEFINE_NO_DIRS = '#define NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS'
+ _DEFINE_NO_FILES = '#define NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_FILES'
- for s in config.sections():
+ _DEFAULT_WARNING = (
+ '#warning No device-supplied android_filesystem_config.h,'
+ ' using empty default.')
- if AID_MATCH.match(s) and config.has_option(s, 'value'):
- handle_dup('AID', file_name, s, seen_aids[0])
- seen_aids[0][s] = file_name
- handle_aid(file_name, s, config, aids, seen_aids)
- else:
- handle_dup('path', file_name, s, seen_paths)
- seen_paths[s] = file_name
- handle_path(file_name, s, config, files, dirs)
+ # Long names.
+ # pylint: disable=invalid-name
+ _NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS_ENTRY = (
+ '{ 00000, AID_ROOT, AID_ROOT, 0,'
+ '"system/etc/fs_config_dirs" },')
-def generate(files, dirs, aids):
- print GENERATED
- print INCLUDE
- print
+ _NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_FILES_ENTRY = (
+ '{ 00000, AID_ROOT, AID_ROOT, 0,'
+ '"system/etc/fs_config_files" },')
- are_dirs = len(dirs) > 0
- are_files = len(files) > 0
- are_aids = len(aids) > 0
+ _IFDEF_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS = (
+ '#ifdef NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS')
+ # pylint: enable=invalid-name
- if are_aids:
- for a in aids:
- # use the preserved str value
- print FILE_COMMENT % a[0]
- print GENERIC_DEFINE % (a[1], a[2])
+ _ENDIF = '#endif'
- print
+ _OPEN_FILE_STRUCT = (
+ 'static const struct fs_path_config android_device_files[] = {')
+
+ _OPEN_DIR_STRUCT = (
+ 'static const struct fs_path_config android_device_dirs[] = {')
+
+ _CLOSE_FILE_STRUCT = '};'
+
+ _GENERIC_DEFINE = "#define %s\t%s"
+
+ _FILE_COMMENT = '// Defined in file: \"%s\"'
- if not are_dirs:
- print DEFINE_NO_DIRS
+ def add_opts(self, opt_group):
- if not are_files:
- print DEFINE_NO_FILES
+ opt_group.add_argument(
+ 'fsconfig', nargs='+', help='The list of fsconfig files to parse')
- if not are_files and not are_dirs and not are_aids:
- print DEFAULT_WARNING
- return
+ def __call__(self, args):
- if are_files:
- print OPEN_FILE_STRUCT
- for tup in files:
- f = tup[0]
- c = tup[1]
- c[4] = '"' + c[4] + '"'
- c = '{ ' + ' ,'.join(c) + ' },'
- print FILE_COMMENT % f
- print ' ' + c
+ parser = FSConfigFileParser(args['fsconfig'])
+ FSConfigGen._generate(parser.files, parser.dirs, parser.aids)
+
+ @staticmethod
+ def _to_fs_entry(fs_config):
+ """
+ Given an FSConfig entry, converts it to a proper
+ array entry for the array entry.
+
+ { mode, user, group, caps, "path" },
+
+ Args:
+ fs_config (FSConfig): The entry to convert to
+ a valid C array entry.
+ """
+
+ # Get some short names
+ mode = fs_config.mode
+ user = fs_config.user
+ group = fs_config.group
+ fname = fs_config.filename
+ caps = fs_config.caps
+ path = fs_config.path
+
+ fmt = '{ %s, %s, %s, %s, "%s" },'
+
+ expanded = fmt % (mode, user, group, caps, path)
+
+ print FSConfigGen._FILE_COMMENT % fname
+ print ' ' + expanded
+
+ @staticmethod
+ def _generate(files, dirs, aids):
+ """Generates an OEM android_filesystem_config.h header file to stdout.
+
+ Args:
+ files ([FSConfig]): A list of FSConfig objects for file entries.
+ dirs ([FSConfig]): A list of FSConfig objects for directory
+ entries.
+ aids ([AIDS]): A list of AID objects for Android Id entries.
+ """
+ print FSConfigGen._GENERATED
+ print FSConfigGen._INCLUDE
+ print
+
+ are_dirs = len(dirs) > 0
+ are_files = len(files) > 0
+ are_aids = len(aids) > 0
+
+ if are_aids:
+ for aid in aids:
+ # use the preserved _path value
+ print FSConfigGen._FILE_COMMENT % aid.found
+ print FSConfigGen._GENERIC_DEFINE % (aid.identifier, aid.value)
+
+ print
if not are_dirs:
- print IFDEF_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS
- print ' ' + NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS_ENTRY
- print ENDIF
- print CLOSE_FILE_STRUCT
-
- if are_dirs:
- print OPEN_DIR_STRUCT
- for d in dirs:
- f[4] = '"' + f[4] + '"'
- d = '{ ' + ' ,'.join(d) + ' },'
- print ' ' + d
-
- print CLOSE_FILE_STRUCT
-
-def file_key(x):
-
- # Wrapper class for custom prefix matching strings
- class S(object):
- def __init__(self, str):
-
- self.orig = str
- self.is_prefix = str[-1] == '*'
- if self.is_prefix:
- self.str = str[:-1]
- else:
- self.str = str
-
- def __lt__(self, other):
-
- # if were both suffixed the smallest string
- # is 'bigger'
- if self.is_prefix and other.is_prefix:
- b = len(self.str) > len(other.str)
- # If I am an the suffix match, im bigger
- elif self.is_prefix:
- b = False
- # If other is the suffix match, he's bigger
- elif other.is_prefix:
- b = True
- # Alphabetical
- else:
- b = self.str < other.str
- return b
+ print FSConfigGen._DEFINE_NO_DIRS + '\n'
+
+ if not are_files:
+ print FSConfigGen._DEFINE_NO_FILES + '\n'
+
+ if not are_files and not are_dirs and not are_aids:
+ print FSConfigGen._DEFAULT_WARNING
+ return
+
+ if are_files:
+ print FSConfigGen._OPEN_FILE_STRUCT
+ for fs_config in files:
+ FSConfigGen._to_fs_entry(fs_config)
+
+ if not are_dirs:
+ print FSConfigGen._IFDEF_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS
+ print(
+ ' ' +
+ FSConfigGen._NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS_ENTRY)
+ print FSConfigGen._ENDIF
+ print FSConfigGen._CLOSE_FILE_STRUCT
+
+ if are_dirs:
+ print FSConfigGen._OPEN_DIR_STRUCT
+ for dir_entry in dirs:
+ FSConfigGen._to_fs_entry(dir_entry)
+
+ print FSConfigGen._CLOSE_FILE_STRUCT
- return S(x[4])
def main():
+ """Main entry point for execution."""
+
+ opt_parser = argparse.ArgumentParser(
+ description='A tool for parsing fsconfig config files and producing' +
+ 'digestable outputs.')
+ subparser = opt_parser.add_subparsers(help='generators')
+
+ gens = generator.get()
+
+ # for each gen, instantiate and add them as an option
+ for name, gen in gens.iteritems():
+
+ generator_option_parser = subparser.add_parser(name, help=gen.__doc__)
+ generator_option_parser.set_defaults(which=name)
+
+ opt_group = generator_option_parser.add_argument_group(name +
+ ' options')
+ gen.add_opts(opt_group)
+
+ args = opt_parser.parse_args()
+
+ args_as_dict = vars(args)
+ which = args_as_dict['which']
+ del args_as_dict['which']
+
+ gens[which](args_as_dict)
- files = []
- dirs = []
- aids = []
- seen_paths = {}
-
- # (name to file, value to aid)
- seen_aids = ({}, {})
-
- for x in sys.argv[1:]:
- parse(x, files, dirs, aids, seen_paths, seen_aids)
-
- # sort entries:
- # * specified path before prefix match
- # ** ie foo before f*
- # * lexicographical less than before other
- # ** ie boo before foo
- # Given these paths:
- # paths=['ac', 'a', 'acd', 'an', 'a*', 'aa', 'ac*']
- # The sort order would be:
- # paths=['a', 'aa', 'ac', 'acd', 'an', 'ac*', 'a*']
- # Thus the fs_config tools will match on specified paths before attempting
- # prefix, and match on the longest matching prefix.
- files.sort(key= lambda x: file_key(x[1]))
-
- # sort on value of (file_name, name, value, strvalue)
- # This is only cosmetic so AIDS are arranged in ascending order
- # within the generated file.
- aids.sort(key=lambda x: x[2])
-
- generate(files, dirs, aids)
if __name__ == '__main__':
main()