| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2015 The Chromium Authors. All rights reserved. | 2 # Copyright 2015 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """MB - the Meta-Build wrapper around GYP and GN | 6 """MB - the Meta-Build wrapper around GYP and GN |
| 7 | 7 |
| 8 MB is a wrapper script for GYP and GN that can be used to generate build files | 8 MB is a wrapper script for GYP and GN that can be used to generate build files |
| 9 for sets of canned configurations and analyze them. | 9 for sets of canned configurations and analyze them. |
| 10 """ | 10 """ |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 118 subp.add_argument('target', nargs=1, | 118 subp.add_argument('target', nargs=1, |
| 119 help='ninja target to generate the isolate for') | 119 help='ninja target to generate the isolate for') |
| 120 subp.set_defaults(func=self.CmdIsolate) | 120 subp.set_defaults(func=self.CmdIsolate) |
| 121 | 121 |
| 122 subp = subps.add_parser('lookup', | 122 subp = subps.add_parser('lookup', |
| 123 help='look up the command for a given config or ' | 123 help='look up the command for a given config or ' |
| 124 'builder') | 124 'builder') |
| 125 AddCommonOptions(subp) | 125 AddCommonOptions(subp) |
| 126 subp.set_defaults(func=self.CmdLookup) | 126 subp.set_defaults(func=self.CmdLookup) |
| 127 | 127 |
| 128 subp = subps.add_parser('run', | 128 subp = subps.add_parser( |
| 129 help='build and run the isolated version of a ' | 129 'run', |
| 130 'binary') | 130 help='build and run the isolated version of a ' |
| 131 'binary', |
| 132 formatter_class=argparse.RawDescriptionHelpFormatter) |
| 133 subp.description = ( |
| 134 'Build, isolate, and run the given binary with the command line\n' |
| 135 'listed in the isolate. You may pass extra arguments after the\n' |
| 136 'target; use "--" if the extra arguments need to include switches.\n' |
| 137 '\n' |
| 138 'Examples:\n' |
| 139 '\n' |
| 140 ' % tools/mb/mb.py run -m chromium.linux -b "Linux Builder" \\\n' |
| 141 ' //out/Default content_browsertests\n' |
| 142 '\n' |
| 143 ' % tools/mb/mb.py run out/Default content_browsertests\n' |
| 144 '\n' |
| 145 ' % tools/mb/mb.py run out/Default content_browsertests -- \\\n' |
| 146 ' --test-launcher-retry-limit=0' |
| 147 '\n' |
| 148 ) |
| 149 |
| 131 AddCommonOptions(subp) | 150 AddCommonOptions(subp) |
| 132 subp.add_argument('-j', '--jobs', dest='jobs', type=int, | 151 subp.add_argument('-j', '--jobs', dest='jobs', type=int, |
| 133 help='Number of jobs to pass to ninja') | 152 help='Number of jobs to pass to ninja') |
| 134 subp.add_argument('--no-build', dest='build', default=True, | 153 subp.add_argument('--no-build', dest='build', default=True, |
| 135 action='store_false', | 154 action='store_false', |
| 136 help='Do not build, just isolate and run') | 155 help='Do not build, just isolate and run') |
| 137 subp.add_argument('path', nargs=1, | 156 subp.add_argument('path', nargs=1, |
| 138 help='path to generate build into') | 157 help=('path to generate build into (or use).' |
| 158 ' This can be either a regular path or a ' |
| 159 'GN-style source-relative path like ' |
| 160 '//out/Default.')) |
| 139 subp.add_argument('target', nargs=1, | 161 subp.add_argument('target', nargs=1, |
| 140 help='ninja target to build and run') | 162 help='ninja target to build and run') |
| 163 subp.add_argument('extra_args', nargs='*', |
| 164 help=('extra args to pass to the isolate to run. Use ' |
| 165 '"--" as the first arg if you need to pass ' |
| 166 'switches')) |
| 141 subp.set_defaults(func=self.CmdRun) | 167 subp.set_defaults(func=self.CmdRun) |
| 142 | 168 |
| 143 subp = subps.add_parser('validate', | 169 subp = subps.add_parser('validate', |
| 144 help='validate the config file') | 170 help='validate the config file') |
| 145 subp.add_argument('-f', '--config-file', metavar='PATH', | 171 subp.add_argument('-f', '--config-file', metavar='PATH', |
| 146 default=self.default_config, | 172 default=self.default_config, |
| 147 help='path to config file ' | 173 help='path to config file ' |
| 148 '(default is //tools/mb/mb_config.pyl)') | 174 '(default is //tools/mb/mb_config.pyl)') |
| 149 subp.set_defaults(func=self.CmdValidate) | 175 subp.set_defaults(func=self.CmdValidate) |
| 150 | 176 |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 246 if ret: | 272 if ret: |
| 247 return ret | 273 return ret |
| 248 ret = self.RunGNIsolate(vals) | 274 ret = self.RunGNIsolate(vals) |
| 249 if ret: | 275 if ret: |
| 250 return ret | 276 return ret |
| 251 else: | 277 else: |
| 252 ret = self.Build('%s_run' % target) | 278 ret = self.Build('%s_run' % target) |
| 253 if ret: | 279 if ret: |
| 254 return ret | 280 return ret |
| 255 | 281 |
| 256 ret, _, _ = self.Run([ | 282 cmd = [ |
| 257 self.executable, | 283 self.executable, |
| 258 self.PathJoin('tools', 'swarming_client', 'isolate.py'), | 284 self.PathJoin('tools', 'swarming_client', 'isolate.py'), |
| 259 'run', | 285 'run', |
| 260 '-s', | 286 '-s', |
| 261 self.ToSrcRelPath('%s/%s.isolated' % (build_dir, target))], | 287 self.ToSrcRelPath('%s/%s.isolated' % (build_dir, target)), |
| 262 force_verbose=False, buffer_output=False) | 288 ] |
| 289 if self.args.extra_args: |
| 290 cmd += ['--'] + self.args.extra_args |
| 291 |
| 292 ret, _, _ = self.Run(cmd, force_verbose=False, buffer_output=False) |
| 263 | 293 |
| 264 return ret | 294 return ret |
| 265 | 295 |
| 266 def CmdValidate(self, print_ok=True): | 296 def CmdValidate(self, print_ok=True): |
| 267 errs = [] | 297 errs = [] |
| 268 | 298 |
| 269 # Read the file to make sure it parses. | 299 # Read the file to make sure it parses. |
| 270 self.ReadConfigFile() | 300 self.ReadConfigFile() |
| 271 | 301 |
| 272 # Build a list of all of the configs referenced by builders. | 302 # Build a list of all of the configs referenced by builders. |
| (...skipping 476 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 749 | 779 |
| 750 build_dir = self.args.path[0] | 780 build_dir = self.args.path[0] |
| 751 target = self.args.target[0] | 781 target = self.args.target[0] |
| 752 target_name = self.GNTargetName(target) | 782 target_name = self.GNTargetName(target) |
| 753 command, extra_files = self.GetIsolateCommand(target, vals, gn_isolate_map) | 783 command, extra_files = self.GetIsolateCommand(target, vals, gn_isolate_map) |
| 754 | 784 |
| 755 label = gn_isolate_map[target_name]['label'] | 785 label = gn_isolate_map[target_name]['label'] |
| 756 cmd = self.GNCmd('desc', build_dir, extra_args=[label, 'runtime_deps']) | 786 cmd = self.GNCmd('desc', build_dir, extra_args=[label, 'runtime_deps']) |
| 757 ret, out, _ = self.Call(cmd) | 787 ret, out, _ = self.Call(cmd) |
| 758 if ret: | 788 if ret: |
| 789 if out: |
| 790 self.Print(out) |
| 759 return ret | 791 return ret |
| 760 | 792 |
| 761 runtime_deps = out.splitlines() | 793 runtime_deps = out.splitlines() |
| 762 | 794 |
| 763 self.WriteIsolateFiles(build_dir, command, target, runtime_deps, | 795 self.WriteIsolateFiles(build_dir, command, target, runtime_deps, |
| 764 extra_files) | 796 extra_files) |
| 765 | 797 |
| 766 ret, _, _ = self.Run([ | 798 ret, _, _ = self.Run([ |
| 767 self.executable, | 799 self.executable, |
| 768 self.PathJoin('tools', 'swarming_client', 'isolate.py'), | 800 self.PathJoin('tools', 'swarming_client', 'isolate.py'), |
| (...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 940 | 972 |
| 941 return cmdline, extra_files | 973 return cmdline, extra_files |
| 942 | 974 |
| 943 def ToAbsPath(self, build_path, *comps): | 975 def ToAbsPath(self, build_path, *comps): |
| 944 return self.PathJoin(self.chromium_src_dir, | 976 return self.PathJoin(self.chromium_src_dir, |
| 945 self.ToSrcRelPath(build_path), | 977 self.ToSrcRelPath(build_path), |
| 946 *comps) | 978 *comps) |
| 947 | 979 |
| 948 def ToSrcRelPath(self, path): | 980 def ToSrcRelPath(self, path): |
| 949 """Returns a relative path from the top of the repo.""" | 981 """Returns a relative path from the top of the repo.""" |
| 950 # TODO: Support normal paths in addition to source-absolute paths. | 982 if path.startswith('//'): |
| 951 assert(path.startswith('//')) | 983 return path[2:].replace('/', self.sep) |
| 952 return path[2:].replace('/', self.sep) | 984 return self.RelPath(path, self.chromium_src_dir) |
| 953 | 985 |
| 954 def ParseGYPConfigPath(self, path): | 986 def ParseGYPConfigPath(self, path): |
| 955 rpath = self.ToSrcRelPath(path) | 987 rpath = self.ToSrcRelPath(path) |
| 956 output_dir, _, _ = rpath.rpartition(self.sep) | 988 output_dir, _, _ = rpath.rpartition(self.sep) |
| 957 return output_dir | 989 return output_dir |
| 958 | 990 |
| 959 def GYPCmd(self, output_dir, vals): | 991 def GYPCmd(self, output_dir, vals): |
| 960 gyp_defines = vals['gyp_defines'] | 992 gyp_defines = vals['gyp_defines'] |
| 961 goma_dir = self.args.goma_dir | 993 goma_dir = self.args.goma_dir |
| 962 | 994 |
| (...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1144 print_env('GYP_CROSSCOMPILE') | 1176 print_env('GYP_CROSSCOMPILE') |
| 1145 print_env('GYP_DEFINES') | 1177 print_env('GYP_DEFINES') |
| 1146 | 1178 |
| 1147 if cmd[0] == self.executable: | 1179 if cmd[0] == self.executable: |
| 1148 cmd = ['python'] + cmd[1:] | 1180 cmd = ['python'] + cmd[1:] |
| 1149 self.Print(*[shell_quoter(arg) for arg in cmd]) | 1181 self.Print(*[shell_quoter(arg) for arg in cmd]) |
| 1150 | 1182 |
| 1151 def PrintJSON(self, obj): | 1183 def PrintJSON(self, obj): |
| 1152 self.Print(json.dumps(obj, indent=2, sort_keys=True)) | 1184 self.Print(json.dumps(obj, indent=2, sort_keys=True)) |
| 1153 | 1185 |
| 1154 def Print(self, *args, **kwargs): | 1186 def GNTargetName(self, target): |
| 1155 # This function largely exists so it can be overridden for testing. | 1187 return target[:-len('_apk')] if target.endswith('_apk') else target |
| 1156 print(*args, **kwargs) | |
| 1157 | 1188 |
| 1158 def Build(self, target): | 1189 def Build(self, target): |
| 1159 build_dir = self.ToSrcRelPath(self.args.path[0]) | 1190 build_dir = self.ToSrcRelPath(self.args.path[0]) |
| 1160 ninja_cmd = ['ninja', '-C', build_dir] | 1191 ninja_cmd = ['ninja', '-C', build_dir] |
| 1161 if self.args.jobs: | 1192 if self.args.jobs: |
| 1162 ninja_cmd.extend(['-j', '%d' % self.args.jobs]) | 1193 ninja_cmd.extend(['-j', '%d' % self.args.jobs]) |
| 1163 ninja_cmd.append(target) | 1194 ninja_cmd.append(target) |
| 1164 ret, _, _ = self.Run(ninja_cmd, force_verbose=False, buffer_output=False) | 1195 ret, _, _ = self.Run(ninja_cmd, force_verbose=False, buffer_output=False) |
| 1165 return ret | 1196 return ret |
| 1166 | 1197 |
| (...skipping 29 matching lines...) Expand all Loading... |
| 1196 | 1227 |
| 1197 def ExpandUser(self, path): | 1228 def ExpandUser(self, path): |
| 1198 # This function largely exists so it can be overridden for testing. | 1229 # This function largely exists so it can be overridden for testing. |
| 1199 return os.path.expanduser(path) | 1230 return os.path.expanduser(path) |
| 1200 | 1231 |
| 1201 def Exists(self, path): | 1232 def Exists(self, path): |
| 1202 # This function largely exists so it can be overridden for testing. | 1233 # This function largely exists so it can be overridden for testing. |
| 1203 return os.path.exists(path) | 1234 return os.path.exists(path) |
| 1204 | 1235 |
| 1205 def Fetch(self, url): | 1236 def Fetch(self, url): |
| 1206 | 1237 # This function largely exists so it can be overridden for testing. |
| 1207 f = urllib2.urlopen(url) | 1238 f = urllib2.urlopen(url) |
| 1208 contents = f.read() | 1239 contents = f.read() |
| 1209 f.close() | 1240 f.close() |
| 1210 return contents | 1241 return contents |
| 1211 | 1242 |
| 1212 def GNTargetName(self, target): | |
| 1213 return target[:-len('_apk')] if target.endswith('_apk') else target | |
| 1214 | |
| 1215 def MaybeMakeDirectory(self, path): | 1243 def MaybeMakeDirectory(self, path): |
| 1216 try: | 1244 try: |
| 1217 os.makedirs(path) | 1245 os.makedirs(path) |
| 1218 except OSError, e: | 1246 except OSError, e: |
| 1219 if e.errno != errno.EEXIST: | 1247 if e.errno != errno.EEXIST: |
| 1220 raise | 1248 raise |
| 1221 | 1249 |
| 1222 def PathJoin(self, *comps): | 1250 def PathJoin(self, *comps): |
| 1223 # This function largely exists so it can be overriden for testing. | 1251 # This function largely exists so it can be overriden for testing. |
| 1224 return os.path.join(*comps) | 1252 return os.path.join(*comps) |
| 1225 | 1253 |
| 1254 def Print(self, *args, **kwargs): |
| 1255 # This function largely exists so it can be overridden for testing. |
| 1256 print(*args, **kwargs) |
| 1257 |
| 1226 def ReadFile(self, path): | 1258 def ReadFile(self, path): |
| 1227 # This function largely exists so it can be overriden for testing. | 1259 # This function largely exists so it can be overriden for testing. |
| 1228 with open(path) as fp: | 1260 with open(path) as fp: |
| 1229 return fp.read() | 1261 return fp.read() |
| 1230 | 1262 |
| 1263 def RelPath(self, path, start='.'): |
| 1264 # This function largely exists so it can be overriden for testing. |
| 1265 return os.path.relpath(path, start) |
| 1266 |
| 1231 def RemoveFile(self, path): | 1267 def RemoveFile(self, path): |
| 1232 # This function largely exists so it can be overriden for testing. | 1268 # This function largely exists so it can be overriden for testing. |
| 1233 os.remove(path) | 1269 os.remove(path) |
| 1234 | 1270 |
| 1235 def RemoveDirectory(self, abs_path): | 1271 def RemoveDirectory(self, abs_path): |
| 1236 if self.platform == 'win32': | 1272 if self.platform == 'win32': |
| 1237 # In other places in chromium, we often have to retry this command | 1273 # In other places in chromium, we often have to retry this command |
| 1238 # because we're worried about other processes still holding on to | 1274 # because we're worried about other processes still holding on to |
| 1239 # file handles, but when MB is invoked, it will be early enough in the | 1275 # file handles, but when MB is invoked, it will be early enough in the |
| 1240 # build that their should be no other processes to interfere. We | 1276 # build that their should be no other processes to interfere. We |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1283 # Then check to see if the arg contains any metacharacters other than | 1319 # Then check to see if the arg contains any metacharacters other than |
| 1284 # double quotes; if it does, quote everything (including the double | 1320 # double quotes; if it does, quote everything (including the double |
| 1285 # quotes) for safety. | 1321 # quotes) for safety. |
| 1286 if any(a in UNSAFE_FOR_CMD for a in arg): | 1322 if any(a in UNSAFE_FOR_CMD for a in arg): |
| 1287 arg = ''.join('^' + a if a in ALL_META_CHARS else a for a in arg) | 1323 arg = ''.join('^' + a if a in ALL_META_CHARS else a for a in arg) |
| 1288 return arg | 1324 return arg |
| 1289 | 1325 |
| 1290 | 1326 |
| 1291 if __name__ == '__main__': | 1327 if __name__ == '__main__': |
| 1292 sys.exit(main(sys.argv[1:])) | 1328 sys.exit(main(sys.argv[1:])) |
| OLD | NEW |