| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2013 The Native Client Authors. All rights reserved. | 2 # Copyright (c) 2013 The Native Client 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 """Manage partitioning of port builds. | 6 """Manage partitioning of port builds. |
| 7 | 7 |
| 8 Download historical data from the naclports builders, and use it to | 8 Download historical data from the naclports builders, and use it to |
| 9 partition all of the projects onto the builder shards so each shard builds in | 9 partition all of the projects onto the builder shards so each shard builds in |
| 10 about the same amount of time. | 10 about the same amount of time. |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 58 import urllib2 | 58 import urllib2 |
| 59 | 59 |
| 60 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) | 60 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) |
| 61 ROOT_DIR = os.path.dirname(SCRIPT_DIR) | 61 ROOT_DIR = os.path.dirname(SCRIPT_DIR) |
| 62 TOOLCHAINS = ('bionic', 'newlib', 'glibc', 'pnacl') | 62 TOOLCHAINS = ('bionic', 'newlib', 'glibc', 'pnacl') |
| 63 | 63 |
| 64 sys.path.append(os.path.join(ROOT_DIR, 'lib')) | 64 sys.path.append(os.path.join(ROOT_DIR, 'lib')) |
| 65 | 65 |
| 66 import naclports | 66 import naclports |
| 67 import naclports.source_package | 67 import naclports.source_package |
| 68 from naclports import Trace | 68 from naclports.util import LogVerbose |
| 69 | 69 |
| 70 | 70 |
| 71 class Error(naclports.Error): | 71 class Error(naclports.Error): |
| 72 pass | 72 pass |
| 73 | 73 |
| 74 | 74 |
| 75 def GetBuildOrder(projects): | 75 def GetBuildOrder(projects): |
| 76 rtn = [] | 76 rtn = [] |
| 77 packages = [naclports.source_package.CreatePackage(p) for p in projects] | 77 packages = [naclports.source_package.CreatePackage(p) for p in projects] |
| 78 for package in packages: | 78 for package in packages: |
| (...skipping 10 matching lines...) Expand all Loading... |
| 89 deps = GetBuildOrder(projects) | 89 deps = GetBuildOrder(projects) |
| 90 return set(deps) - set(projects) | 90 return set(deps) - set(projects) |
| 91 | 91 |
| 92 | 92 |
| 93 def DownloadDataFromBuilder(builder, build): | 93 def DownloadDataFromBuilder(builder, build): |
| 94 max_tries = 10 | 94 max_tries = 10 |
| 95 | 95 |
| 96 for _ in xrange(max_tries): | 96 for _ in xrange(max_tries): |
| 97 url = 'http://build.chromium.org/p/client.nacl.ports/json' | 97 url = 'http://build.chromium.org/p/client.nacl.ports/json' |
| 98 url += '/builders/%s/builds/%d' % (builder, build) | 98 url += '/builders/%s/builds/%d' % (builder, build) |
| 99 Trace('Downloading %s' % url) | 99 LogVerbose('Downloading %s' % url) |
| 100 f = urllib2.urlopen(url) | 100 f = urllib2.urlopen(url) |
| 101 try: | 101 try: |
| 102 data = json.loads(f.read()) | 102 data = json.loads(f.read()) |
| 103 text = data['text'] | 103 text = data['text'] |
| 104 if text == ['build', 'successful']: | 104 if text == ['build', 'successful']: |
| 105 Trace(' Success!') | 105 LogVerbose(' Success!') |
| 106 return data | 106 return data |
| 107 Trace(' Not successful, trying previous build.') | 107 LogVerbose(' Not successful, trying previous build.') |
| 108 finally: | 108 finally: |
| 109 f.close() | 109 f.close() |
| 110 build -= 1 | 110 build -= 1 |
| 111 | 111 |
| 112 raise Error('Unable to find a successful build:\nBuilder: %s\nRange: [%d, %d]' | 112 raise Error('Unable to find a successful build:\nBuilder: %s\nRange: [%d, %d]' |
| 113 % (builder, build - max_tries, build)) | 113 % (builder, build - max_tries, build)) |
| 114 | 114 |
| 115 | 115 |
| 116 class Project(object): | 116 class Project(object): |
| 117 def __init__(self, name): | 117 def __init__(self, name): |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 223 return parts | 223 return parts |
| 224 | 224 |
| 225 | 225 |
| 226 def LoadCanned(parts): | 226 def LoadCanned(parts): |
| 227 # Return an empty partition for the no-sharding case. | 227 # Return an empty partition for the no-sharding case. |
| 228 if parts == 1: | 228 if parts == 1: |
| 229 return [[]] | 229 return [[]] |
| 230 partitions = [] | 230 partitions = [] |
| 231 partition = [] | 231 partition = [] |
| 232 input_file = os.path.join(SCRIPT_DIR, 'partition%d.txt' % parts) | 232 input_file = os.path.join(SCRIPT_DIR, 'partition%d.txt' % parts) |
| 233 Trace("LoadCanned: %s" % input_file) | 233 LogVerbose("LoadCanned: %s" % input_file) |
| 234 with open(input_file) as fh: | 234 with open(input_file) as fh: |
| 235 for line in fh: | 235 for line in fh: |
| 236 if line.strip()[0] == '#': | 236 if line.strip()[0] == '#': |
| 237 continue | 237 continue |
| 238 if line.startswith(' '): | 238 if line.startswith(' '): |
| 239 partition.append(line[2:].strip()) | 239 partition.append(line[2:].strip()) |
| 240 else: | 240 else: |
| 241 if partition: | 241 if partition: |
| 242 partitions.append(partition) | 242 partitions.append(partition) |
| 243 partition = [] | 243 partition = [] |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 295 | 295 |
| 296 def PrintCanned(index, parts): | 296 def PrintCanned(index, parts): |
| 297 canned = GetCanned(index, parts) | 297 canned = GetCanned(index, parts) |
| 298 print(' '.join(canned)) | 298 print(' '.join(canned)) |
| 299 | 299 |
| 300 | 300 |
| 301 def GetCanned(index, parts): | 301 def GetCanned(index, parts): |
| 302 assert index >= 0 and index < parts, [index, parts] | 302 assert index >= 0 and index < parts, [index, parts] |
| 303 partitions = LoadCanned(parts) | 303 partitions = LoadCanned(parts) |
| 304 partitions = FixupCanned(partitions) | 304 partitions = FixupCanned(partitions) |
| 305 Trace("Found %d packages for shard %d" % (len(partitions[index]), index)) | 305 LogVerbose("Found %d packages for shard %d" % (len(partitions[index]), index)) |
| 306 return partitions[index] | 306 return partitions[index] |
| 307 | 307 |
| 308 | 308 |
| 309 def main(args): | 309 def main(args): |
| 310 parser = argparse.ArgumentParser() | 310 parser = argparse.ArgumentParser() |
| 311 parser.add_argument('--check', action='store_true', | 311 parser.add_argument('--check', action='store_true', |
| 312 help='check canned partition information is up-to-date.') | 312 help='check canned partition information is up-to-date.') |
| 313 parser.add_argument('-v', '--verbose', action='store_true', | 313 parser.add_argument('-v', '--verbose', action='store_true', |
| 314 help='Output extra information.') | 314 help='Output extra information.') |
| 315 parser.add_argument('-t', '--print-canned', type=int, | 315 parser.add_argument('-t', '--print-canned', type=int, |
| (...skipping 22 matching lines...) Expand all Loading... |
| 338 GetCanned(0, num_bots) | 338 GetCanned(0, num_bots) |
| 339 return | 339 return |
| 340 | 340 |
| 341 if options.print_canned is not None: | 341 if options.print_canned is not None: |
| 342 PrintCanned(options.print_canned, options.num_bots) | 342 PrintCanned(options.print_canned, options.num_bots) |
| 343 return | 343 return |
| 344 | 344 |
| 345 projects = Projects() | 345 projects = Projects() |
| 346 for bot in range(options.num_bots): | 346 for bot in range(options.num_bots): |
| 347 bot_name = '%s%d' % (options.bot_prefix, bot) | 347 bot_name = '%s%d' % (options.bot_prefix, bot) |
| 348 Trace('Attempting to add data from "%s"' % bot_name) | 348 LogVerbose('Attempting to add data from "%s"' % bot_name) |
| 349 projects.AddDataFromBuilder(bot_name, options.build_number) | 349 projects.AddDataFromBuilder(bot_name, options.build_number) |
| 350 projects.PostProcessDeps() | 350 projects.PostProcessDeps() |
| 351 | 351 |
| 352 parts = Partition(projects, options.num_parts) | 352 parts = Partition(projects, options.num_parts) |
| 353 for i, project_times in enumerate(parts): | 353 for i, project_times in enumerate(parts): |
| 354 print('builder %d (total: %d)' % (i, project_times.total_time)) | 354 print('builder %d (total: %d)' % (i, project_times.total_time)) |
| 355 project_names = project_times.TopologicallySortedProjectNames(projects) | 355 project_names = project_times.TopologicallySortedProjectNames(projects) |
| 356 print(' %s' % '\n '.join(project_names)) | 356 print(' %s' % '\n '.join(project_names)) |
| 357 | 357 |
| 358 times = list(sorted(part.total_time for part in parts)) | 358 times = list(sorted(part.total_time for part in parts)) |
| 359 difference = 0 | 359 difference = 0 |
| 360 for i in range(1, len(times)): | 360 for i in range(1, len(times)): |
| 361 difference += times[i] - times[i - 1] | 361 difference += times[i] - times[i - 1] |
| 362 print('Difference between total time of builders: %d' % difference) | 362 print('Difference between total time of builders: %d' % difference) |
| 363 | 363 |
| 364 | 364 |
| 365 if __name__ == '__main__': | 365 if __name__ == '__main__': |
| 366 try: | 366 try: |
| 367 sys.exit(main(sys.argv[1:])) | 367 sys.exit(main(sys.argv[1:])) |
| 368 except Error as e: | 368 except Error as e: |
| 369 sys.stderr.write("%s\n" % e) | 369 sys.stderr.write("%s\n" % e) |
| 370 sys.exit(1) | 370 sys.exit(1) |
| OLD | NEW |