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