jamesa/generate_oby.py
changeset 5 842a773e65f2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jamesa/generate_oby.py	Wed Nov 04 17:40:17 2009 +0000
@@ -0,0 +1,212 @@
+# Copyright (c) 2009 Symbian Foundation Ltd
+# This component and the accompanying materials are made available
+# under the terms of the License "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Symbian Foundation Ltd - initial contribution.
+# 
+# Contributors:
+#
+# Description:
+# Create a barebones OBY file from a dependency report text file.
+
+"""Take a report generated by get_deps.py and attempt to create an OBY
+file to build a ROM with the dependency list.
+"""
+
+from build_graph import without_comments
+from optparse import OptionParser
+
+import os
+import re
+import sys
+import logging
+
+__author__ = 'James Aley'
+__email__ = 'jamesa@symbian.org'
+__version__ = '1.0'
+
+# Logging config
+_LOG_FORMAT = '%(levelname)s: %(message)s'
+logging.basicConfig(format=_LOG_FORMAT, level=logging.WARNING, stream=sys.stdout)
+
+# Regexes for bld.inf parsing
+_RE_EXPORT_SECTION = '\\s*PRJ_EXPORTS\\s*'
+_RE_OTHER_SECTION = '\\s*PRJ_[a-z]+\\s*'
+_RE_IBY_OBY = '\\s*([^\\s]+\\.[oi]by)\\s+.*'
+_p_export_section = re.compile(_RE_EXPORT_SECTION, re.I)
+_p_other_section = re.compile(_RE_OTHER_SECTION, re.I)
+_p_iby_oby = re.compile(_RE_IBY_OBY, re.I)
+
+# OBY output templates
+_OBY_HEADER = """// OBY file generated by genereate_oby.py.
+// The following includes are derived from the dependency report: %s
+
+"""
+
+_OBY_INCLUDE = """
+// Required for: %s
+%s
+"""
+
+_OBY_UNRESOLVED = """
+
+// The following appear to be exported by this component, 
+// but were not found under the include directory:
+%s
+"""
+
+_OBY_NO_EXPORTS = """
+
+// The following components are required in your dependency graph, but
+// they appear not to export an IBY/OBY file. Your ROM will likely not
+// build until you locate the correct include files for these.
+%s
+"""
+
+_INCLUDE_TEMPLATE = '#include <%s>'
+
+def bld_inf_list(report_path):
+    """Returna list of bld.inf files from the report
+    """
+    bld_list = []
+    report_file = None
+    try:
+        report_file = open(report_path)
+    except IOError, e:
+        logging.critical('Could not open report: %s' % (repr(e), ))
+        exit(1)
+    return filter(lambda x: x and not x.isspace(), [line.strip() for line in without_comments(report_file)])
+
+def get_paths(bld_inf_file):
+    """Returns a list of referenced OBY or IBY files from a bld.inf file.
+    bld_inf_file is an open file handle, which will not be closed by this
+    function.
+    """
+    oby_iby = []
+    export_section = False
+    for line in without_comments(bld_inf_file):
+        if export_section:
+            match_iby_oby = _p_iby_oby.search(line)
+            if match_iby_oby:
+                file_name = match_iby_oby.groups()[0].strip()
+                oby_iby.append(file_name)
+            else:
+                match_other_section = _p_other_section.search(line)
+                if match_other_section:
+                    export_section = False
+        else:
+            match_export_section = _p_export_section.search(line)
+            if match_export_section:
+                export_section = True
+    obys = filter(lambda x: x.lower().endswith('.oby'), oby_iby)
+    if len(obys) > 0:
+        return obys
+    return oby_iby
+
+def rom_file_list(bld_inf_paths):
+    """Iterate through a list of bld.inf file paths and extra the references
+    to OBY or IBY files where appropriate (OBY takes precedence). Return a
+    dictionary of relevant files in the format:
+        { 'component_bld_inf' : [ iby_file_list] }
+    """
+    obys_ibys = {}
+    for path in bld_inf_paths:
+        bld_inf_file = None
+        try:
+            bld_inf_file = open(path)
+        except IOError, e:
+            logging.error('Unable to open bld.inf file: %s' % (repr(e), ))
+            continue
+        rom_file_paths = get_paths(bld_inf_file)
+        obys_ibys[path] = rom_file_paths
+        bld_inf_file.close()
+    return obys_ibys
+
+def iby_map(iby_dict, dir_name, file_names):
+     """Searches for the specified IBY/OBY file under the include_root path.
+     Returns the absolute path to the IBY/OBY if it was found, otherwise a blank string.
+     """
+     for component in iby_dict.keys():
+         # Map the file names for each component IBY file to a matching
+         # file name under the export directory, if it exists, otherwise
+         # keep the existing name for now - it might be matched later.
+         file_names = map(lambda x: x.lower(), file_names)
+         component_ibys = map(lambda x: os.path.basename(x).lower() in file_names \
+                                  and os.path.join(dir_name, os.path.basename(x)) \
+                                  or x, \
+                                  iby_dict[component])
+         iby_dict[component] = component_ibys
+
+def write_oby(out_path, iby_map, input_path, include_root):
+    """Write an OBY file to include the required IBY and OBY files for this
+    ROM specification, given by iby_map.
+    """
+    out_file = None
+    try:
+        out_file = open(out_path, 'w')
+    except IOError, e:
+        logging.critical('Unable to write OBY file: %s' % repr(e))
+        exit(1)
+
+    # Write the header with the input file name included
+    out_file.write(_OBY_HEADER % (input_path, ))
+
+    exports = filter(lambda x: len(iby_map[x]) > 0, iby_map.keys())
+    no_exports = filter(lambda x: len(iby_map[x]) == 0, iby_map.keys())
+
+    # Write the includes and missing exports
+    for component in exports:
+        iby_list = iby_map[component]
+        exported = filter(lambda x: x.startswith(include_root), iby_list)
+        # Need relative paths for include, but os.path.relpath is added
+        # in Python 2.6, which isn't supported by other Symbian tools
+        # at present :-(
+        exported = map(lambda x: x[len(include_root) + 1:], exported)
+        exported.sort()
+        
+        missing = filter(lambda x: not x.startswith(include_root), iby_list)
+        missing = map(lambda x: os.path.basename(x), missing)
+        missing.sort()
+        
+        # Write the IBY file includes for this component
+        out_file.write(_OBY_INCLUDE % (component, '\n'.join([_INCLUDE_TEMPLATE % (iby, ) for iby in exported]), ))
+        
+        # Write the missing IBY reports
+        if len(missing) > 0:
+            out_file.write(_OBY_UNRESOLVED % ('\n'.join(['// %s' % (missed, ) for missed in missing]), ))
+
+    # Write report for the IBY that appear not to export any ROM include files
+    out_file.write(_OBY_NO_EXPORTS % ('\n'.join(['// %s' % (path,) for path in no_exports]), ))
+    out_file.close()
+
+# Main
+if __name__ == '__main__':
+    # Options config
+    parser = OptionParser()
+    parser.set_description(__doc__)
+    parser.add_option('-r', '--report', dest='report_path', 
+                      help='File name for the report generated by get_deps.py', 
+                      metavar='INPUT_FILE', default='dependencies.txt')
+    parser.add_option('-o', '--output', dest='output_file',
+                      help='OBY ouput file to write to.',
+                      metavar='OUT_FILE', default='generated.oby')
+    parser.add_option('-i', '--include_root', dest='include_root',
+                      help='Environment ROM inlcude root.',
+                      metavar='INCLUDE_ROOT', 
+                      default=os.path.sep.join(['epoc32', 'rom']))
+    (options, args) = parser.parse_args()
+
+    # Read the report and get a list of bld.inf files, then convert to
+    # a dictionary of bld_inf -> [IBY file list] mappings.
+    bld_infs = bld_inf_list(options.report_path)
+    bld_iby_map = rom_file_list(bld_infs)
+
+    # Walk the include tree to find the exported IBYs.
+    os.path.walk(options.include_root, iby_map, bld_iby_map)
+
+    # Write the OBY file
+    write_oby(options.output_file, bld_iby_map, options.report_path, options.include_root)
+