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