1# -*- coding: utf-8 -*- 2 3#------------------------------------------------------------------------- 4# drawElements Quality Program utilities 5# -------------------------------------- 6# 7# Copyright 2015 The Android Open Source Project 8# 9# Licensed under the Apache License, Version 2.0 (the "License"); 10# you may not use this file except in compliance with the License. 11# You may obtain a copy of the License at 12# 13# http://www.apache.org/licenses/LICENSE-2.0 14# 15# Unless required by applicable law or agreed to in writing, software 16# distributed under the License is distributed on an "AS IS" BASIS, 17# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18# See the License for the specific language governing permissions and 19# limitations under the License. 20# 21#------------------------------------------------------------------------- 22 23import os 24import subprocess 25import sys 26 27TEXT_FILE_EXTENSION = [ 28 ".bat", 29 ".c", 30 ".cfg", 31 ".cmake", 32 ".cpp", 33 ".css", 34 ".h", 35 ".hh", 36 ".hpp", 37 ".html", 38 ".inl", 39 ".java", 40 ".js", 41 ".m", 42 ".mk", 43 ".mm", 44 ".py", 45 ".rule", 46 ".sh", 47 ".test", 48 ".txt", 49 ".xml", 50 ".xsl", 51 ] 52 53BINARY_FILE_EXTENSION = [ 54 ".bin", 55 ".png", 56 ".pkm", 57 ".xcf", 58 ".nspv", 59 ".h264", 60 ".h265", 61 ".ivf", 62 ".obu", 63 ".mp4", 64 ".yuv" 65] 66 67def isTextFile (filePath): 68 # Special case for a preprocessor test file that uses a non-ascii/utf8 encoding 69 if filePath.endswith("preprocessor.test"): 70 return False 71 # Special case for clang-format which is a baked binary from clang 72 if filePath.endswith("clang-format"): 73 return False 74 75 ext = os.path.splitext(filePath)[1] 76 if ext in TEXT_FILE_EXTENSION: 77 return True 78 if ext in BINARY_FILE_EXTENSION: 79 return False 80 81 # Analyze file contents, zero byte is the marker for a binary file 82 f = open(filePath, "rb") 83 84 TEST_LIMIT = 1024 85 nullFound = False 86 numBytesTested = 0 87 88 byte = f.read(1) 89 while byte and numBytesTested < TEST_LIMIT: 90 if byte == "\0": 91 nullFound = True 92 break 93 94 byte = f.read(1) 95 numBytesTested += 1 96 97 f.close() 98 return not nullFound 99 100def getProjectPath (): 101 # File system hierarchy is fixed 102 scriptDir = os.path.dirname(os.path.abspath(__file__)) 103 projectDir = os.path.normpath(os.path.join(scriptDir, "../..")) 104 return projectDir 105 106def git (*args): 107 process = subprocess.Popen(['git'] + list(args), cwd=getProjectPath(), stdout=subprocess.PIPE) 108 output = process.communicate()[0] 109 if process.returncode != 0: 110 raise Exception("Failed to execute '%s', got %d" % (str(args), process.returncode)) 111 return output 112 113def getAbsolutePathPathFromProjectRelativePath (projectRelativePath): 114 return os.path.normpath(os.path.join(getProjectPath(), projectRelativePath)) 115 116def getChangedFiles (): 117 # Added, Copied, Moved, Renamed 118 output = git('diff', '--cached', '--name-only', '-z', '--diff-filter=ACMR') 119 if not output: 120 return [] 121 relativePaths = output.decode().split('\0')[:-1] # remove trailing '' 122 return [getAbsolutePathPathFromProjectRelativePath(path) for path in relativePaths] 123 124def getFilesChangedSince (commit): 125 # Get all the files changed since a given commit 126 output = git('diff', '--name-only', '-U0', '-z', '--no-color', '--no-relative', '--diff-filter=ACMR', commit) 127 relativePaths = output.decode().split('\0')[:-1] # remove trailing '' 128 return [getAbsolutePathPathFromProjectRelativePath(path) for path in relativePaths] 129 130def getFilesCurrentlyDirty (): 131 # Get all the files currently dirty and uncommitted 132 return getFilesChangedSince('HEAD') 133 134def getFilesModifiedSinceLastCommit (): 135 # Try to get only the modified files. In a shallow clone with depth 1, 136 # HEAD^ doesn't exist, so we have no choice but to return all the files. 137 try: 138 return getFilesChangedSince('HEAD^') 139 except: 140 return getAllProjectFiles() 141 142def getAllProjectFiles (): 143 output = git('ls-files', '--cached', '-z').decode() 144 relativePaths = output.split('\0')[:-1] # remove trailing '' 145 return [getAbsolutePathPathFromProjectRelativePath(path) for path in relativePaths] 146 147def runCommand (command): 148 process = runCommandAsync(command) 149 waitAsyncCommand(process, command) 150 151def runCommandAsync (command): 152 try: 153 return subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 154 except OSError as e: 155 raise RuntimeError('Failed to run command "%s": %s' % (' '.join(command), e.strerror)) 156 157def waitAsyncCommand (process, command): 158 (out, err) = process.communicate() 159 if process.returncode == 0: 160 return out 161 else: 162 print('Failed to run command "%s": %s' % (' '.join(command), err)) 163 sys.exit(process.returncode) 164