Home | History | Annotate | Download | only in gyp
      1 #!/usr/bin/env python
      2 #
      3 # Copyright 2013 The Chromium Authors. All rights reserved.
      4 # Use of this source code is governed by a BSD-style license that can be
      5 # found in the LICENSE file.
      6 
      7 """Creates a TOC file from a Java jar.
      8 
      9 The TOC file contains the non-package API of the jar. This includes all
     10 public/protected/package classes/functions/members and the values of static
     11 final variables (members with package access are kept because in some cases we
     12 have multiple libraries with the same package, particularly test+non-test). Some
     13 other information (major/minor javac version) is also included.
     14 
     15 This TOC file then can be used to determine if a dependent library should be
     16 rebuilt when this jar changes. I.e. any change to the jar that would require a
     17 rebuild, will have a corresponding change in the TOC file.
     18 """
     19 
     20 import optparse
     21 import os
     22 import re
     23 import sys
     24 import zipfile
     25 
     26 from util import build_utils
     27 from util import md5_check
     28 
     29 
     30 def GetClassesInZipFile(zip_file):
     31   classes = []
     32   files = zip_file.namelist()
     33   for f in files:
     34     if f.endswith('.class'):
     35       # f is of the form org/chromium/base/Class$Inner.class
     36       classes.append(f.replace('/', '.')[:-6])
     37   return classes
     38 
     39 
     40 def CallJavap(classpath, classes):
     41   javap_cmd = [
     42       'javap',
     43       '-package',  # Show public/protected/package.
     44       # -verbose is required to get constant values (which can be inlined in
     45       # dependents).
     46       '-verbose',
     47       '-J-XX:NewSize=4m',
     48       '-classpath', classpath
     49       ] + classes
     50   return build_utils.CheckOutput(javap_cmd)
     51 
     52 
     53 def ExtractToc(disassembled_classes):
     54   # javap output is structured by indent (2-space) levels.
     55   good_patterns = [
     56       '^[^ ]', # This includes all class signatures.
     57       '^  SourceFile:',
     58       '^  minor version:',
     59       '^  major version:',
     60       '^  Constant value:',
     61       '^  public ',
     62       '^  protected ',
     63       ]
     64   bad_patterns = [
     65       '^const #', # Matches the constant pool (i.e. literals used in the class).
     66     ]
     67 
     68   def JavapFilter(line):
     69     return (re.match('|'.join(good_patterns), line) and
     70         not re.match('|'.join(bad_patterns), line))
     71   toc = filter(JavapFilter, disassembled_classes.split('\n'))
     72 
     73   return '\n'.join(toc)
     74 
     75 
     76 def UpdateToc(jar_path, toc_path):
     77   classes = GetClassesInZipFile(zipfile.ZipFile(jar_path))
     78   toc = ''
     79   if len(classes) != 0:
     80     javap_output = CallJavap(classpath=jar_path, classes=classes)
     81     toc = ExtractToc(javap_output)
     82 
     83   with open(toc_path, 'w') as tocfile:
     84     tocfile.write(toc)
     85 
     86 
     87 def DoJarToc(options):
     88   jar_path = options.jar_path
     89   toc_path = options.toc_path
     90   record_path = '%s.md5.stamp' % toc_path
     91   md5_check.CallAndRecordIfStale(
     92       lambda: UpdateToc(jar_path, toc_path),
     93       record_path=record_path,
     94       input_paths=[jar_path],
     95       force=not os.path.exists(toc_path),
     96       )
     97   build_utils.Touch(toc_path, fail_if_missing=True)
     98 
     99 
    100 def main():
    101   parser = optparse.OptionParser()
    102   build_utils.AddDepfileOption(parser)
    103 
    104   parser.add_option('--jar-path', help='Input .jar path.')
    105   parser.add_option('--toc-path', help='Output .jar.TOC path.')
    106   parser.add_option('--stamp', help='Path to touch on success.')
    107 
    108   options, _ = parser.parse_args()
    109 
    110   if options.depfile:
    111     build_utils.WriteDepfile(
    112         options.depfile,
    113         build_utils.GetPythonDependencies())
    114 
    115   DoJarToc(options)
    116 
    117   if options.depfile:
    118     build_utils.WriteDepfile(
    119         options.depfile,
    120         build_utils.GetPythonDependencies())
    121 
    122   if options.stamp:
    123     build_utils.Touch(options.stamp)
    124 
    125 
    126 if __name__ == '__main__':
    127   sys.exit(main())
    128