1#!/usr/bin/env python 2# Copyright 2015 The PDFium 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 6import datetime 7import glob 8import os 9import re 10import subprocess 11import sys 12 13 14def os_name(): 15 if sys.platform.startswith('linux'): 16 return 'linux' 17 if sys.platform.startswith('win'): 18 return 'win' 19 if sys.platform.startswith('darwin'): 20 return 'mac' 21 raise Exception('Confused, can not determine OS, aborting.') 22 23 24def RunCommand(cmd): 25 try: 26 subprocess.check_call(cmd) 27 return None 28 except subprocess.CalledProcessError as e: 29 return e 30 31 32def RunCommandPropagateErr(cmd, 33 stdout_has_errors=False, 34 exit_status_on_error=None): 35 """Run a command as a subprocess. 36 37 Errors in that subprocess are printed out if it returns an error exit code. 38 39 Args: 40 cmd: Command to run as a list of strings. 41 stdout_has_errors: Whether to print stdout instead of stderr on an error 42 exit. 43 exit_status_on_error: If specified, upon an error in the subprocess the 44 caller script exits immediately with the given status. 45 """ 46 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 47 output, err = p.communicate() 48 49 if p.returncode: 50 PrintErr('\nError when invoking "%s"' % ' '.join(cmd)) 51 if stdout_has_errors: 52 PrintErr(output) 53 54 PrintErr(err) 55 56 if exit_status_on_error is not None: 57 sys.exit(exit_status_on_error) 58 59 return None 60 61 return output 62 63 64# RunCommandExtractHashedFiles returns a tuple: (raised_exception, hashed_files) 65# It runs the given command. If it fails it will return an exception and None. 66# If it succeeds it will return None and the list of processed files extracted 67# from the output of the command. It expects lines in this format: 68# MD5:<path_to_image_file>:<md5_hash_in_hex> 69# The returned hashed_files is a list of (file_path, MD5-hash) pairs. 70def RunCommandExtractHashedFiles(cmd): 71 try: 72 output = subprocess.check_output(cmd, universal_newlines=True) 73 ret = [] 74 for line in output.split('\n'): 75 line = line.strip() 76 if line.startswith("MD5:"): 77 ret.append([x.strip() for x in line.lstrip("MD5:").rsplit(":", 1)]) 78 return None, ret 79 except subprocess.CalledProcessError as e: 80 return e, None 81 82 83class DirectoryFinder: 84 '''A class for finding directories and paths under either a standalone 85 checkout or a chromium checkout of PDFium.''' 86 87 def __init__(self, build_location): 88 # |build_location| is typically "out/Debug" or "out/Release". 89 # Expect |my_dir| to be .../pdfium/testing/tools. 90 self.my_dir = os.path.dirname(os.path.realpath(__file__)) 91 self.testing_dir = os.path.dirname(self.my_dir) 92 if (os.path.basename(self.my_dir) != 'tools' or 93 os.path.basename(self.testing_dir) != 'testing'): 94 raise Exception('Confused, can not find pdfium root directory, aborting.') 95 self.pdfium_dir = os.path.dirname(self.testing_dir) 96 # Find path to build directory. This depends on whether this is a 97 # standalone build vs. a build as part of a chromium checkout. For 98 # standalone, we expect a path like .../pdfium/out/Debug, but for 99 # chromium, we expect a path like .../src/out/Debug two levels 100 # higher (to skip over the third_party/pdfium path component under 101 # which chromium sticks pdfium). 102 self.base_dir = self.pdfium_dir 103 one_up_dir = os.path.dirname(self.base_dir) 104 two_up_dir = os.path.dirname(one_up_dir) 105 if (os.path.basename(two_up_dir) == 'src' and 106 os.path.basename(one_up_dir) == 'third_party'): 107 self.base_dir = two_up_dir 108 self.build_dir = os.path.join(self.base_dir, build_location) 109 self.os_name = os_name() 110 111 def ExecutablePath(self, name): 112 '''Finds compiled binaries under the build path.''' 113 result = os.path.join(self.build_dir, name) 114 if self.os_name == 'win': 115 result = result + '.exe' 116 return result 117 118 def ScriptPath(self, name): 119 '''Finds other scripts in the same directory as this one.''' 120 return os.path.join(self.my_dir, name) 121 122 def WorkingDir(self, other_components=''): 123 '''Places generated files under the build directory, not source dir.''' 124 result = os.path.join(self.build_dir, 'gen', 'pdfium') 125 if other_components: 126 result = os.path.join(result, other_components) 127 return result 128 129 def TestingDir(self, other_components=''): 130 '''Finds test files somewhere under the testing directory.''' 131 result = self.testing_dir 132 if other_components: 133 result = os.path.join(result, other_components) 134 return result 135 136 137def GetBooleanGnArg(arg_name, build_dir, verbose=False): 138 '''Extract the value of a boolean flag in args.gn''' 139 cwd = os.getcwd() 140 os.chdir(build_dir) 141 gn_args_output = subprocess.check_output( 142 ['gn', 'args', '.', '--list=%s' % arg_name, '--short']) 143 os.chdir(cwd) 144 arg_match_output = re.search('%s = (.*)' % arg_name, gn_args_output).group(1) 145 if verbose: 146 print >> sys.stderr, "Found '%s' for value of %s" % (arg_match_output, 147 arg_name) 148 return arg_match_output == 'true' 149 150 151def PrintWithTime(s): 152 """Prints s prepended by a timestamp.""" 153 print '[%s] %s' % (datetime.datetime.now().strftime("%Y%m%d %H:%M:%S"), s) 154 155 156def PrintErr(s): 157 """Prints s to stderr.""" 158 print >> sys.stderr, s 159