1 #!/usr/bin/env python 2 3 # Copyright 2017 Google Inc. 4 # 5 # Use of this source code is governed by a BSD-style license that can be 6 # found in the LICENSE file. 7 8 import fnmatch 9 import multiprocessing 10 import os 11 import subprocess 12 import sys 13 14 ''' 15 If called with arguments, this script will verify that those headers are 16 self-sufficient and idempotent. 17 18 Otherwise, test all checked-in headers except for those in the ignore list. 19 ''' 20 21 public_header_args = [ 22 '-Iinclude/core', 23 '-Iinclude/config', 24 '-Iinclude/android', 25 '-Iinclude/codec', 26 '-Iinclude/effects', 27 '-Iinclude/gpu', 28 '-Iinclude/gpu/gl', 29 '-Iinclude/pathops', 30 '-Iinclude/ports', 31 '-Iinclude/private', 32 '-Iinclude/svg', 33 '-Iinclude/utils', 34 '-Iinclude/utils/mac', 35 '-Iinclude/views', 36 '-Ithird_party/vulkan', 37 ] 38 39 all_header_args = [ 40 '-Iinclude/core', 41 '-Iinclude/config', 42 '-Iinclude/android', 43 '-Iinclude/c', 44 '-Iinclude/codec', 45 '-Iinclude/effects', 46 '-Iinclude/gpu', 47 '-Iinclude/gpu/gl', 48 '-Iinclude/pathops', 49 '-Iinclude/ports', 50 '-Iinclude/private', 51 '-Iinclude/svg', 52 '-Iinclude/utils', 53 '-Iinclude/utils/mac', 54 '-Iinclude/views', 55 '-Isrc/codec', 56 '-Isrc/core', 57 '-Isrc/effects', 58 '-Isrc/effects/gradients', 59 '-Isrc/fonts', 60 '-Isrc/gpu', 61 '-Isrc/image', 62 '-Isrc/images', 63 '-Isrc/lazy', 64 '-Isrc/opts', 65 '-Isrc/pathops', 66 '-Isrc/ports', 67 '-Isrc/sfnt', 68 '-Isrc/shaders', 69 '-Isrc/sksl', 70 '-Isrc/utils', 71 '-Isrc/utils/win', 72 '-Isrc/xml', 73 '-Igm', 74 '-Itests', 75 '-Itools', 76 '-Itools/debugger', 77 '-Itools/flags', 78 '-Itools/gpu', 79 '-Itools/timer', 80 '-Ithird_party/etc1', 81 '-Ithird_party/externals/jsoncpp/include', 82 '-Ithird_party/externals/libjpeg-turbo', 83 '-Ithird_party/externals/sfntly/cpp/src', 84 '-Ithird_party/externals/zlib', 85 '-Ithird_party/gif', 86 '-Ithird_party/vulkan', 87 ] 88 89 ignore = [ 90 '*/lex.*.h', 91 '*/osmesa_wrapper.h', 92 'debugger/QT/*', 93 'example/*', 94 'experimental/*', 95 'include/config/*', 96 'include/core/SkPostConfig.h', 97 'include/gpu/vk/*', 98 'include/ports/SkFontMgr_android.h', 99 'include/ports/SkFontMgr_fontconfig.h', 100 'include/ports/SkTypeface_win.h', 101 'include/private/*_impl.h', 102 'include/utils/mac/SkCGUtils.h', 103 'include/views/SkOSWindow_*.h', 104 'src/c/sk_c_from_to.h', 105 'src/core/*Template.h', 106 'src/core/SkBitmapProcState_*.h', 107 'src/core/SkFDot6Constants.h', 108 'src/core/SkLinearBitmapPipeline.h', 109 'src/core/SkLinearBitmapPipeline_*.h', 110 'src/core/SkUnPreMultiplyPriv.h', 111 'src/gpu/vk/*', 112 'src/opts/*_SSE2.h', 113 'src/opts/*_SSSE3.h', 114 'src/opts/*_neon.h', 115 'src/opts/*_sse.h', 116 'src/opts/Sk4px_*.h', 117 'src/ports/*', 118 'src/utils/*_win.h', 119 'src/utils/win/*', 120 'src/views/*', 121 'third_party/*', 122 'tools/fiddle/*', 123 'tools/viewer/*', 124 ] 125 126 # test header for self-sufficiency and idempotency. 127 # Returns a string containing errors, or None iff there are no errors. 128 def compile_header(header): 129 args = ([] if fnmatch.fnmatch(header, 'include/c/*') else 130 public_header_args if fnmatch.fnmatch(header, 'include/*') else 131 all_header_args) 132 cmd = ['c++', '--std=c++11'] + args + [ '-o', '/dev/null', '-c', '-x', 'c++', '-'] 133 proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, 134 stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 135 proc.stdin.write('#include "%s"\n#include "%s"\n' % (header, header)) 136 proc.stdin.close() 137 errors = proc.stdout.read().strip() 138 if proc.wait() != 0 or len(errors) > 0: 139 return '\n\033[7m ERROR: %s \033[0m\n%s\n\n' % (header, errors) 140 return None 141 142 # for h in headers: 143 # compile_header(h) 144 # ...Except use a multiprocessing pool. 145 # Exit at first error. 146 def compile_headers(headers): 147 class N: good = True 148 # N.good is a global scoped to this function to make a print_and_exit_if() a closure 149 pool = multiprocessing.Pool() 150 def print_and_exit_if(r): 151 if r is not None: 152 sys.stdout.write(r) 153 N.good = False 154 pool.terminate() 155 for path in headers: 156 assert os.path.exists(path) 157 pool.apply_async(compile_header, args=(path, ), callback=print_and_exit_if) 158 pool.close() 159 pool.join() 160 if N.good: 161 sys.stdout.write('all good :)\n') 162 else: 163 exit(1) 164 165 166 def main(argv): 167 skia_dir = os.path.join(os.path.dirname(__file__), os.pardir) 168 if len(argv) > 1: 169 paths = [os.path.relpath(os.path.abspath(arg), skia_dir) for arg in argv[1:]] 170 os.chdir(skia_dir) 171 else: 172 os.chdir(skia_dir) 173 paths = [path for path in subprocess.check_output(['git', 'ls-files']).splitlines() 174 if path.endswith('.h') 175 and not any(fnmatch.fnmatch(path, pattern) for pattern in ignore)] 176 compile_headers(paths) 177 178 179 if __name__ == '__main__': 180 main(sys.argv) 181 182