run_operations.py (5750B)
1 # This Source Code Form is subject to the terms of the Mozilla Public 2 # License, v. 2.0. If a copy of the MPL was not distributed with this 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 import os 5 import platform 6 import subprocess 7 import sys 8 from enum import Enum, auto 9 10 # This is a collection of helper functions that use the subprocess.run 11 # command to execute commands. In the future, if we have cases where 12 # we find common python functionality in utilizing the data returned 13 # from these functions, that functionality can live here for easy reuse 14 # between other python scripts in dom/media/webrtc/third_party_build. 15 16 17 # must run 'hg' with HGPLAIN=1 to ensure aliases don't interfere with 18 # command output. 19 env = os.environ.copy() 20 env["HGPLAIN"] = "1" 21 22 23 def run_hg(cmd): 24 cmd_list = cmd.split(" ") 25 res = subprocess.run( 26 cmd_list, 27 capture_output=True, 28 text=True, 29 env=env, 30 check=False, 31 ) 32 if res.returncode != 0: 33 print( 34 f"Hit return code {res.returncode} running '{cmd}'. Aborting.", 35 file=sys.stderr, 36 ) 37 print(res.stderr) 38 sys.exit(1) 39 stdout = res.stdout.strip() 40 return [] if len(stdout) == 0 else stdout.split("\n") 41 42 43 def run_git(cmd, working_dir): 44 cmd_list = cmd.split(" ") 45 res = subprocess.run( 46 cmd_list, 47 capture_output=True, 48 text=True, 49 cwd=working_dir, 50 check=False, 51 ) 52 if res.returncode != 0: 53 print( 54 f"Hit return code {res.returncode} running '{cmd}'. Aborting.", 55 file=sys.stderr, 56 ) 57 print(res.stderr) 58 sys.exit(1) 59 stdout = res.stdout.strip() 60 return [] if len(stdout) == 0 else stdout.split("\n") 61 62 63 def run_shell(cmd, capture_output=True): 64 res = subprocess.run( 65 cmd, 66 shell=True, 67 capture_output=capture_output, 68 text=True, 69 check=False, 70 ) 71 if res.returncode != 0: 72 print( 73 f"Hit return code {res.returncode} running '{cmd}'. Aborting.", 74 file=sys.stderr, 75 ) 76 if capture_output: 77 print(res.stderr) 78 sys.exit(1) 79 output_lines = [] 80 if capture_output: 81 stdout = res.stdout.strip() 82 output_lines = [] if len(stdout) == 0 else stdout.split("\n") 83 84 return output_lines 85 86 87 def get_last_line(file_path): 88 # technique from https://stackoverflow.com/questions/46258499/how-to-read-the-last-line-of-a-file-in-python 89 with open(file_path, "rb") as f: 90 try: # catch OSError in case of a one line file 91 f.seek(-2, os.SEEK_END) 92 while f.read(1) != b"\n": 93 f.seek(-2, os.SEEK_CUR) 94 except OSError: 95 f.seek(0) 96 return f.readline().decode().strip() 97 98 99 def update_resume_state(state, resume_state_filename): 100 with open(resume_state_filename, "w") as ofile: 101 ofile.write(state) 102 ofile.write("\n") 103 104 105 class ErrorHelp: 106 def __init__(self): 107 self.prefix = None 108 self.error_help = None 109 self.postfix = None 110 111 def set_prefix(self, prefix): 112 self.prefix = prefix 113 114 def set_postfix(self, postfix): 115 self.postfix = postfix 116 117 def set_help(self, error_help): 118 self.error_help = error_help 119 120 def print_help(self): 121 if self.prefix is not None: 122 print(self.prefix) 123 if self.error_help is not None: 124 print(self.error_help) 125 if self.postfix is not None: 126 print(self.postfix) 127 128 129 class RepoType(Enum): 130 HG = auto() 131 GIT = auto() 132 133 134 def detect_repo_type(): 135 if os.path.exists(".git"): 136 return RepoType.GIT 137 elif os.path.exists(".hg"): 138 return RepoType.HG 139 return None 140 141 142 def git_status(working_dir, search_path="."): 143 # We don't use run_git above because it strips all leading 144 # whitespace, which causes issues with the output of 145 # git status --porcelain since leading spaces matter in the output. 146 # For example, the following two lines mean different things: 147 # M file1.txt 148 # M file2.txt 149 # file2.txt is staged, while file1.txt is modified, but unstaged. 150 cmd = f"git --no-optional-locks status --porcelain {search_path}" 151 cmd_list = cmd.split(" ") 152 res = subprocess.run( 153 cmd_list, 154 capture_output=True, 155 text=True, 156 cwd=working_dir, 157 check=False, 158 ) 159 if res.returncode != 0: 160 print( 161 f"Hit return code {res.returncode} running '{cmd}'. Aborting.", 162 file=sys.stderr, 163 ) 164 print(res.stderr) 165 sys.exit(1) 166 stdout = res.stdout.strip("\n") 167 return [] if len(stdout) == 0 else stdout.split("\n") 168 169 170 def check_repo_status(repo_type): 171 if not isinstance(repo_type, RepoType): 172 print("check_repo_status requires type RepoType") 173 raise TypeError 174 if repo_type == RepoType.GIT: 175 return git_status(".", "third_party/libwebrtc") 176 else: 177 return run_hg("hg status third_party/libwebrtc") 178 179 180 def is_mac_os(): 181 return platform.system() == "Darwin" 182 183 184 def git_get_config(config_name, working_dir): 185 # using run_shell rather than run_git because a config name that is 186 # unset returns a non-zero error code. 187 stdout_lines = run_shell(f"cd {working_dir} ; git config {config_name} || true") 188 line_cnt = len(stdout_lines) 189 if line_cnt > 1: 190 print(f"Error: {config_name} returned multiple values ({line_cnt})") 191 sys.exit(1) 192 elif line_cnt == 1: 193 return stdout_lines[0] 194 return None 195 196 197 def git_is_config_set(config_name, working_dir): 198 config_val = git_get_config(config_name, working_dir) 199 if config_val is not None and config_val == "true": 200 return True 201 return False