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 |