Home | History | Annotate | Download | only in ppapi
      1 #!/usr/bin/env python
      2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 """This script should be run manually on occasion to make sure the gyp file and
      7 the includes tests are up to date.
      8 
      9 It does the following:
     10  - Verifies that all source code is in ppapi.gyp
     11  - Verifies that all sources in ppapi.gyp really do exist
     12  - Generates tests/test_c_includes.c
     13  - Generates tests/test_cpp_includes.cc
     14 These tests are checked in to SVN.
     15 """
     16 # TODO(dmichael):  Make this script execute as a gyp action, move the include
     17 #                  tests to some 'generated' area, and remove them from version
     18 #                  control.
     19 
     20 import re
     21 import os
     22 import sys
     23 import posixpath
     24 
     25 # A simple regular expression that should match source files for C++ and C.
     26 SOURCE_FILE_RE = re.compile('.+\.(cc|c|h)$')
     27 
     28 # IGNORE_RE is a regular expression that matches directories which contain
     29 # source that we don't (currently) expect to be in ppapi.gyp.  This script will
     30 # not check whether source files under these directories are in the gyp file.
     31 # TODO(dmichael): Put examples back in the build.
     32 # TODO(brettw): Put proxy in the build when it's ready.
     33 IGNORE_RE = re.compile('^(examples|GLES2|proxy|tests\/clang).*')
     34 
     35 GYP_TARGETS_KEY = 'targets'
     36 GYP_SOURCES_KEY = 'sources'
     37 GYP_TARGET_NAME_KEY = 'target_name'
     38 
     39 
     40 # Return a set containing all source files found given an object read from a gyp
     41 # file.
     42 def GetAllGypSources(gyp_file_data):
     43   sources = set([])
     44   for target in gyp_file_data[GYP_TARGETS_KEY]:
     45     # Get a list of sources in the target that are not ignored, and 'normalize'
     46     # them.  The main reason for this is to turn the forward slashes in the gyp
     47     # file in to backslashes when the script is run on Windows.
     48     source_list = [posixpath.normpath(src) for src in target[GYP_SOURCES_KEY]
     49                    if not IGNORE_RE.match(src)]
     50     sources |= set(source_list)
     51   return sources
     52 
     53 
     54 # Search the directory named start_root and all its subdirectories for source
     55 # files.
     56 # Return a set containing the string names of all the source files found,
     57 # relative to start_root.
     58 def GetFileSources(start_root):
     59   file_set = set([])
     60   for root, dirs, files in os.walk(start_root):
     61     relative_root = os.path.relpath(root, start_root)
     62     if not IGNORE_RE.match(relative_root):
     63       for source in files:
     64         if SOURCE_FILE_RE.match(source):
     65           file_set |= set([os.path.join(relative_root, source)])
     66   return file_set
     67 
     68 
     69 # Make sure all source files are in the given gyp object (evaluated from a gyp
     70 # file), and that all source files listed in the gyp object exist in the
     71 # directory.
     72 def VerifyGypFile(gyp_file_data):
     73   gyp_sources = GetAllGypSources(gyp_file_data)
     74   file_sources = GetFileSources('.')
     75   in_gyp_not_file = gyp_sources - file_sources
     76   in_file_not_gyp = file_sources - gyp_sources
     77   if len(in_gyp_not_file):
     78     print 'Found source file(s) in ppapi.gyp but not in the directory:', \
     79       in_gyp_not_file
     80   if len(in_file_not_gyp):
     81     print 'Found source file(s) in the directory but not in ppapi.gyp:', \
     82       in_file_not_gyp
     83   error_count = len(in_gyp_not_file) + len(in_file_not_gyp)
     84   if error_count:
     85     sys.exit(error_count)
     86 
     87 
     88 def WriteLines(filename, lines):
     89   outfile = open(filename, 'w')
     90   for line in lines:
     91     outfile.write(line)
     92   outfile.write('\n')
     93 
     94 
     95 COPYRIGHT_STRING_C = \
     96 """/* Copyright (c) 2010 The Chromium Authors. All rights reserved.
     97  * Use of this source code is governed by a BSD-style license that can be
     98  * found in the LICENSE file.
     99  *
    100  * This test simply includes all the C headers to ensure they compile with a C
    101  * compiler.  If it compiles, it passes.
    102  */
    103 """
    104 
    105 COPYRIGHT_STRING_CC = \
    106 """// Copyright (c) 2010 The Chromium Authors. All rights reserved.
    107 // Use of this source code is governed by a BSD-style license that can be
    108 // found in the LICENSE file.
    109 //
    110 // This test simply includes all the C++ headers to ensure they compile with a
    111 // C++ compiler.  If it compiles, it passes.
    112 //
    113 """
    114 
    115 
    116 # Get the source file names out of the given gyp file data object (as evaluated
    117 # from a gyp file) for the given target name.  Return the string names in
    118 # sorted order.
    119 def GetSourcesForTarget(target_name, gyp_file_data):
    120   for target in gyp_file_data[GYP_TARGETS_KEY]:
    121     if target[GYP_TARGET_NAME_KEY] == target_name:
    122       sources = target[GYP_SOURCES_KEY]
    123       sources.sort()
    124       return sources
    125   print 'Warning: no target named ', target, ' found.'
    126   return []
    127 
    128 
    129 # Generate all_c_includes.h, which includes all C headers.  This is part of
    130 # tests/test_c_sizes.c, which includes all C API files to ensure that all
    131 # the headers in ppapi/c can be compiled with a C compiler, and also asserts
    132 # (with compile-time assertions) that all structs and enums are a particular
    133 # size.
    134 def GenerateCIncludeTest(gyp_file_data):
    135   c_sources = GetSourcesForTarget('ppapi_c', gyp_file_data)
    136   lines = [COPYRIGHT_STRING_C]
    137   lines.append('#ifndef PPAPI_TESTS_ALL_C_INCLUDES_H_\n')
    138   lines.append('#define PPAPI_TESTS_ALL_C_INCLUDES_H_\n\n')
    139   for source in c_sources:
    140     lines.append('#include "ppapi/' + source + '"\n')
    141   lines.append('\n#endif  /* PPAPI_TESTS_ALL_C_INCLUDES_H_ */\n')
    142   WriteLines('tests/all_c_includes.h', lines)
    143 
    144 
    145 # Generate all_cpp_includes.h, which is used by test_cpp_includes.cc to ensure
    146 # that all the headers in ppapi/cpp can be compiled with a C++ compiler.
    147 def GenerateCCIncludeTest(gyp_file_data):
    148   cc_sources = GetSourcesForTarget('ppapi_cpp_objects', gyp_file_data)
    149   header_re = re.compile('.+\.h$')
    150   lines = [COPYRIGHT_STRING_CC]
    151   lines.append('#ifndef PPAPI_TESTS_ALL_CPP_INCLUDES_H_\n')
    152   lines.append('#define PPAPI_TESTS_ALL_CPP_INCLUDES_H_\n\n')
    153   for source in cc_sources:
    154     if header_re.match(source):
    155       lines.append('#include "ppapi/' + source + '"\n')
    156   lines.append('\n#endif  // PPAPI_TESTS_ALL_CPP_INCLUDES_H_\n')
    157   WriteLines('tests/all_cpp_includes.h', lines)
    158 
    159 
    160 def main():
    161   ppapi_gyp_file_name = 'ppapi.gyp'
    162   gyp_file_contents = open(ppapi_gyp_file_name).read()
    163   gyp_file_data = eval(gyp_file_contents)
    164   VerifyGypFile(gyp_file_data)
    165   GenerateCIncludeTest(gyp_file_data)
    166   GenerateCCIncludeTest(gyp_file_data)
    167   return 0
    168 
    169 
    170 if __name__ == '__main__':
    171     sys.exit(main())
    172