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