Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Delta Between Two Patch Sets: build.py

Issue 29609559: Issue 6021 - Refactoring build.py (Closed) Base URL: https://hg.adblockplus.org/buildtools/file/79688f4a4aff
Left Patch Set: Addressing Wladimir's comments Created Nov. 17, 2017, 11:30 a.m.
Right Patch Set: Fixing a regression in 'docs' Created Nov. 20, 2017, 9:17 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « no previous file | tests/test_packagerWebExt.py » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 # This Source Code Form is subject to the terms of the Mozilla Public 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 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/. 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 4
5 from __future__ import print_function
6
7 import argparse 5 import argparse
6 import logging
8 import os 7 import os
8 import re
9 import shutil
10 import subprocess
9 import sys 11 import sys
10 import re
11 import subprocess
12 import shutil
13 from functools import partial 12 from functools import partial
14 from StringIO import StringIO 13 from StringIO import StringIO
15 from zipfile import ZipFile 14 from zipfile import ZipFile
16 15
17 KNOWN_PLATFORMS = {'gecko', 'chrome', 'generic', 'edge'} 16 KNOWN_PLATFORMS = {'chrome', 'gecko', 'edge', 'generic'}
18 17
19 MAIN_PARSER = argparse.ArgumentParser( 18 MAIN_PARSER = argparse.ArgumentParser(
20 description=__doc__, 19 description=__doc__,
21 formatter_class=argparse.RawDescriptionHelpFormatter) 20 formatter_class=argparse.RawDescriptionHelpFormatter)
22 21
23 SUB_PARSERS = MAIN_PARSER.add_subparsers(title='Commands', dest='action', 22 SUB_PARSERS = MAIN_PARSER.add_subparsers(title='Commands', dest='action',
24 metavar='[command]') 23 metavar='[command]')
24
25 ALL_COMMANDS = []
25 26
26 27
27 def make_argument(*args, **kwargs): 28 def make_argument(*args, **kwargs):
28 def _make_argument(*args, **kwargs): 29 def _make_argument(*args, **kwargs):
29 parser = kwargs.pop('parser') 30 parser = kwargs.pop('parser')
30 parser.add_argument(*args, **kwargs) 31 parser.add_argument(*args, **kwargs)
31 32
32 return partial(_make_argument, *args, **kwargs) 33 return partial(_make_argument, *args, **kwargs)
33 34
34 35
35 def argparse_command(valid_platforms=None, arguments=()): 36 def argparse_command(valid_platforms=None, arguments=()):
36 def wrapper(func): 37 def wrapper(func):
37 def func_wrapper(*args, **kwargs): 38 def func_wrapper(*args, **kwargs):
38 return func(*args, **kwargs) 39 return func(*args, **kwargs)
39 40
40 short_desc, long_desc = func.__doc__.split(os.linesep + os.linesep, 1) 41 short_desc, long_desc = func.__doc__.split(os.linesep + os.linesep, 1)
41 42
42 new_parser = SUB_PARSERS.add_parser( 43 ALL_COMMANDS.append({
43 func.__name__, 44 'name': func.__name__,
44 description=long_desc, 45 'description': long_desc,
45 formatter_class=argparse.RawDescriptionHelpFormatter, 46 'help_text': short_desc,
46 help=short_desc, 47 'valid_platforms': valid_platforms or KNOWN_PLATFORMS,
47 ) 48 'function': func,
48 49 'arguments': arguments,
49 new_parser.__valid_platforms = valid_platforms or KNOWN_PLATFORMS 50 })
50
51 for argument in arguments:
52 argument(parser=new_parser)
53
54 new_parser.set_defaults(function=func)
55 return func_wrapper 51 return func_wrapper
56 52
57 return wrapper 53 return wrapper
58 54
59 55
60 def collect_platforms(base_dir): 56 def make_subcommand(name, description, help_text, function, arguments):
61 """Construct valid platforms for each subcommand. 57 new_parser = SUB_PARSERS.add_parser(
58 name, description=description, help=help_text,
59 formatter_class=argparse.RawDescriptionHelpFormatter,
60 )
61
62 for argument in arguments:
63 argument(parser=new_parser)
64
65 new_parser.set_defaults(function=function)
66 return new_parser
67
68
69 def build_available_subcommands(base_dir):
70 """Build subcommands, which are available for the repository in base_dir.
62 71
63 Search 'base_dir' for existing metadata.<type> files and make <type> an 72 Search 'base_dir' for existing metadata.<type> files and make <type> an
64 avaible choice for the subcommand, intersected with their respective valid 73 avaible choice for the subcommands, intersected with their respective valid
65 platforms. 74 platforms.
66 """ 75
67 if collect_platforms._result is not None: 76 If no valid platform is found for a subcommand, it get's omitted.
77 """
78 if build_available_subcommands._result is not None:
68 # Tests might run this code multiple times, make sure the collection 79 # Tests might run this code multiple times, make sure the collection
69 # of platforms is only run once. 80 # of platforms is only run once.
70 return collect_platforms._result 81 return build_available_subcommands._result
71 82
72 types = set() 83 types = set()
73 for p in KNOWN_PLATFORMS: 84 for p in KNOWN_PLATFORMS:
74 if os.path.exists(os.path.join(base_dir, 'metadata.' + p)): 85 if os.path.exists(os.path.join(base_dir, 'metadata.' + p)):
75 types.add(p) 86 types.add(p)
76 87
77 if len(types) == 0: 88 if len(types) == 0:
78 print('No metadata file found in this repository, a metadata file ' 89 logging.error('No metadata file found in this repository. Expecting '
79 'metadata.* is required.', file=sys.stderr) 90 'one or more of {} to be present.'.format(
Wladimir Palant 2017/11/17 11:51:58 This message is confusing now, with only known pla
tlucas 2017/11/17 13:32:04 Done.
80 collect_platforms._result = False 91 ', '.join('metadata.' + p for p in KNOWN_PLATFORMS)))
92 build_available_subcommands._result = False
81 return False 93 return False
82 94
83 for _, sub_parser in SUB_PARSERS.choices.items(): 95 for command_params in ALL_COMMANDS:
Wladimir Palant 2017/11/17 11:51:58 If you don't need the keys, why iterate over items
tlucas 2017/11/17 13:32:04 This will be obsolete with addressing the below co
84 platforms = types.intersection(sub_parser.__valid_platforms) 96 platforms = types.intersection(command_params.pop('valid_platforms'))
85 if len(platforms) > 1: 97 if len(platforms) > 1:
86 sub_parser.add_argument('-t', '--type', dest='platform', 98 command_params['arguments'] += (
87 choices=platforms) 99 make_argument('-t', '--type', dest='platform', required=True,
88 else: 100 choices=platforms),
101 )
102 make_subcommand(**command_params)
103 elif len(platforms) == 1:
104 sub_parser = make_subcommand(**command_params)
89 sub_parser.set_defaults(platform=platforms.pop()) 105 sub_parser.set_defaults(platform=platforms.pop())
Wladimir Palant 2017/11/17 11:51:58 What about len(platforms) == 0?
tlucas 2017/11/17 13:32:04 This was not trivial - but i came up with a soluti
90 106
91 collect_platforms._result = True 107 build_available_subcommands._result = True
92 return True 108 return True
93 109
94 110
95 collect_platforms._result = None 111 build_available_subcommands._result = None
tlucas 2017/11/17 11:36:45 I did not see the necessity to introduce a fully f
96 112
97 113
98 @argparse_command( 114 @argparse_command(
99 valid_platforms={'gecko', 'chrome', 'edge'}, 115 valid_platforms={'chrome', 'gecko', 'edge'},
100 arguments=( 116 arguments=(
101 make_argument( 117 make_argument(
102 '-b', '--build-num', dest='build_num', 118 '-b', '--build-num', dest='build_num',
103 help=('Use given build number (if omitted the build number will ' 119 help=('Use given build number (if omitted the build number will '
104 'be retrieved from Mercurial)')), 120 'be retrieved from Mercurial)')),
105 make_argument( 121 make_argument(
106 '-k', '--key', dest='key_file', 122 '-k', '--key', dest='key_file',
107 help=('File containing private key and certificates required to ' 123 help=('File containing private key and certificates required to '
108 'sign the package')), 124 'sign the package')),
109 make_argument( 125 make_argument(
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after
273 locale_config = read_locale_config(base_dir, platform, metadata) 289 locale_config = read_locale_config(base_dir, platform, metadata)
274 290
275 import buildtools.localeTools as localeTools 291 import buildtools.localeTools as localeTools
276 localeTools.getTranslations(locale_config, basename, project_key) 292 localeTools.getTranslations(locale_config, basename, project_key)
277 293
278 294
279 @argparse_command( 295 @argparse_command(
280 valid_platforms={'chrome'}, 296 valid_platforms={'chrome'},
281 arguments=( 297 arguments=(
282 make_argument('target_dir'), 298 make_argument('target_dir'),
283 make_argument('-q', '--quiet', help='Suppress JsDoc output'), 299 make_argument('-q', '--quiet', help='Suppress JsDoc output',
300 action='store_true', default=False),
284 ) 301 )
285 ) 302 )
286 def docs(base_dir, target_dir, quiet, platform, **kwargs): 303 def docs(base_dir, target_dir, quiet, platform, **kwargs):
287 """ 304 """
288 Generate documentation (requires node.js). 305 Generate documentation (requires node.js).
289 306
290 Generate documentation files and write them into the specified directory. 307 Generate documentation files and write them into the specified directory.
291 """ 308 """
292 source_dir = os.path.join(base_dir, 'lib') 309 source_dir = os.path.join(base_dir, 'lib')
293 310
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
345 Note: If you are not the project owner then you probably don't want to run 362 Note: If you are not the project owner then you probably don't want to run
346 this! 363 this!
347 364
348 Run release automation: create downloads for the new version, tag source 365 Run release automation: create downloads for the new version, tag source
349 code repository as well as downloads and buildtools repository. 366 code repository as well as downloads and buildtools repository.
350 """ 367 """
351 if downloads_repository is None: 368 if downloads_repository is None:
352 downloads_repository = os.path.join(base_dir, os.pardir, 'downloads') 369 downloads_repository = os.path.join(base_dir, os.pardir, 'downloads')
353 370
354 if platform == 'chrome' and key_file is None: 371 if platform == 'chrome' and key_file is None:
355 raise ValueError('You must specify a key file for this release') 372 logging.error('You must specify a key file for this release')
373 return
356 374
357 import buildtools.releaseAutomation as releaseAutomation 375 import buildtools.releaseAutomation as releaseAutomation
358 releaseAutomation.run(base_dir, platform, version, key_file, 376 releaseAutomation.run(base_dir, platform, version, key_file,
359 downloads_repository) 377 downloads_repository)
360 378
361 379
362 @argparse_command(valid_platforms={'chrome'}) 380 @argparse_command(valid_platforms={'chrome'})
363 def updatepsl(base_dir, **kwargs): 381 def updatepsl(base_dir, **kwargs):
364 """Update Public Suffix List. 382 """Update Public Suffix List.
365 383
366 Downloads Public Suffix List (see http://publicsuffix.org/) and generates 384 Downloads Public Suffix List (see http://publicsuffix.org/) and generates
367 lib/publicSuffixList.js from it. 385 lib/publicSuffixList.js from it.
368 """ 386 """
369 import buildtools.publicSuffixListUpdater as publicSuffixListUpdater 387 import buildtools.publicSuffixListUpdater as publicSuffixListUpdater
370 publicSuffixListUpdater.updatePSL(base_dir) 388 publicSuffixListUpdater.updatePSL(base_dir)
371 389
372 390
373 def process_args(base_dir, *args): 391 def process_args(base_dir, *args):
374 if collect_platforms(base_dir): 392 if build_available_subcommands(base_dir):
375 MAIN_PARSER.set_defaults(base_dir=base_dir) 393 MAIN_PARSER.set_defaults(base_dir=base_dir)
376 394
377 # If no args are provided, this module is run directly from the command 395 # If no args are provided, this module is run directly from the command
378 # line. argparse will take care of consuming sys.argv. 396 # line. argparse will take care of consuming sys.argv.
379 arguments = MAIN_PARSER.parse_args(args if len(args) > 0 else None) 397 arguments = MAIN_PARSER.parse_args(args if len(args) > 0 else None)
380 398
381 function = arguments.function 399 function = arguments.function
382 del arguments.function 400 del arguments.function
383 function(**vars(arguments)) 401 function(**vars(arguments))
LEFTRIGHT

Powered by Google App Engine
This is Rietveld