client.py (5718B)
1 #!/usr/bin/python 2 # This Source Code Form is subject to the terms of the Mozilla Public 3 # License, v. 2.0. If a copy of the MPL was not distributed with this 4 # file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 6 HG_EXCLUSIONS = [".hg", ".hgignore", ".hgtags"] 7 8 import glob 9 import os 10 import shutil 11 import sys 12 from optparse import OptionParser 13 from subprocess import check_call 14 15 topsrcdir = os.path.dirname(__file__) 16 if topsrcdir == "": 17 topsrcdir = "." 18 19 20 def check_call_noisy(cmd, *args, **kwargs): 21 print("Executing command:", cmd) 22 check_call(cmd, *args, **kwargs) 23 24 25 def do_hg_pull(dir, repository, hg): 26 fulldir = os.path.join(topsrcdir, dir) 27 # clone if the dir doesn't exist, pull if it does 28 if not os.path.exists(fulldir): 29 check_call_noisy([hg, "clone", repository, fulldir]) 30 else: 31 cmd = [hg, "pull", "-u", "-R", fulldir] 32 if repository is not None: 33 cmd.append(repository) 34 check_call_noisy(cmd) 35 check_call([ 36 hg, 37 "parent", 38 "-R", 39 fulldir, 40 "--template=Updated to revision {node}.\n", 41 ]) 42 43 44 def do_hg_replace(dir, repository, tag, exclusions, hg): 45 """ 46 Replace the contents of dir with the contents of repository, except for 47 files matching exclusions. 48 """ 49 fulldir = os.path.join(topsrcdir, dir) 50 if os.path.exists(fulldir): 51 shutil.rmtree(fulldir) 52 53 assert not os.path.exists(fulldir) 54 check_call_noisy([hg, "clone", "-u", tag, repository, fulldir]) 55 56 for thing in exclusions: 57 for excluded in glob.iglob(os.path.join(fulldir, thing)): 58 if os.path.isdir(excluded): 59 shutil.rmtree(excluded) 60 else: 61 os.remove(excluded) 62 63 64 def toggle_trailing_blank_line(depname): 65 """If the trailing line is empty, then we'll delete it. 66 Otherwise we'll add a blank line.""" 67 lines = open(depname, "rb").readlines() 68 if not lines: 69 print("unexpected short file", file=sys.stderr) 70 return 71 72 if not lines[-1].strip(): 73 # trailing line is blank, removing it 74 open(depname, "wb").writelines(lines[:-1]) 75 else: 76 # adding blank line 77 open(depname, "ab").write(b"\n") 78 79 80 def get_trailing_blank_line_state(depname): 81 lines = open(depname).readlines() 82 if not lines: 83 print("unexpected short file", file=sys.stderr) 84 return "no blank line" 85 86 if not lines[-1].strip(): 87 return "has blank line" 88 return "no blank line" 89 90 91 def update_nspr_or_nss(tag, depfile, destination, hgpath): 92 destination = destination.rstrip("/") 93 permanent_patch_dir = destination + "/patches" 94 temporary_patch_dir = destination + ".patches" 95 if os.path.exists(temporary_patch_dir): 96 print("please clean up leftover directory " + temporary_patch_dir) 97 sys.exit(2) 98 warn_if_patch_exists(permanent_patch_dir) 99 # protect patch directory from being removed by do_hg_replace 100 if os.path.exists(permanent_patch_dir): 101 shutil.move(permanent_patch_dir, temporary_patch_dir) 102 # now update the destination 103 print("reverting to HG version of %s to get its blank line state" % depfile) 104 check_call_noisy([options.hg, "revert", depfile]) 105 old_state = get_trailing_blank_line_state(depfile) 106 print("old state of %s is: %s" % (depfile, old_state)) 107 do_hg_replace(destination, hgpath, tag, HG_EXCLUSIONS, options.hg) 108 new_state = get_trailing_blank_line_state(depfile) 109 print("new state of %s is: %s" % (depfile, new_state)) 110 if old_state == new_state: 111 print("toggling blank line in: ", depfile) 112 toggle_trailing_blank_line(depfile) 113 tag_file = destination + "/TAG-INFO" 114 with open(tag_file, "w") as f: 115 f.write(tag) 116 # move patch directory back to a subdirectory 117 if os.path.exists(temporary_patch_dir): 118 shutil.move(temporary_patch_dir, permanent_patch_dir) 119 120 121 def warn_if_patch_exists(path): 122 # If the given patch directory exists and contains at least one file, 123 # then print warning and wait for the user to acknowledge. 124 if os.path.isdir(path) and os.listdir(path): 125 print("========================================") 126 print("WARNING: At least one patch file exists") 127 print("in directory: " + path) 128 print("You must manually re-apply all patches") 129 print("after this script has completed!") 130 print("========================================") 131 input("Press Enter to continue...") 132 return 133 134 135 o = OptionParser(usage="client.py [options] update_nspr tagname | update_nss tagname") 136 o.add_option( 137 "--skip-mozilla", 138 dest="skip_mozilla", 139 action="store_true", 140 default=False, 141 help="Obsolete", 142 ) 143 144 o.add_option( 145 "--hg", 146 dest="hg", 147 default=os.environ.get("HG", "hg"), 148 help="The location of the hg binary", 149 ) 150 o.add_option( 151 "--repo", dest="repo", help="the repo to update from (default: upstream repo)" 152 ) 153 154 try: 155 options, args = o.parse_args() 156 action = args[0] 157 except IndexError: 158 o.print_help() 159 sys.exit(2) 160 161 if action in ("checkout", "co"): 162 print("Warning: client.py checkout is obsolete.", file=sys.stderr) 163 pass 164 elif action in ("update_nspr"): 165 (tag,) = args[1:] 166 depfile = "nsprpub/config/prdepend.h" 167 if not options.repo: 168 options.repo = "https://hg.mozilla.org/projects/nspr" 169 update_nspr_or_nss(tag, depfile, "nsprpub", options.repo) 170 elif action in ("update_nss"): 171 (tag,) = args[1:] 172 depfile = "security/nss/coreconf/coreconf.dep" 173 if not options.repo: 174 options.repo = "https://hg.mozilla.org/projects/nss" 175 update_nspr_or_nss(tag, depfile, "security/nss", options.repo) 176 else: 177 o.print_help() 178 sys.exit(2)