blob: 35d8dc928e62236d68134daf2d8c248b7aa04419 [file] [log] [blame]
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3"""Create a new variant of an existing base board
4
5This program will call all of the scripts that create the various pieces
6of a new variant. For example to create a new variant of the hatch base
7board, the following scripts are called:
8
9* third_party/coreboot/util/mainboard/google/hatch/create_coreboot_variant.sh
10* platform/dev/contrib/variant/create_coreboot_config.sh
11* private-overlays/baseboard-hatch-private/sys-boot/
12 * coreboot-private-files-hatch/files/add_fitimage.sh
13 * coreboot-private-files-hatch/asset_generation/gen_fit_image.sh
14 * Outside the chroot, because it uses WINE to run the FIT tools
15* platform/dev/contrib/variant/create_initial_ec_image.sh
16* platform/dev/contrib/variant/add_variant_to_yaml.sh
17* private-overlays/overlay-hatch-private/chromeos-base/
18 * chromeos-config-bsp-hatch-private/add_variant.sh
19
20Once the scripts are done, the following repos have changes
21
22* third_party/coreboot
23* third_party/chromiumos-overlay
24* private-overlays/baseboard-hatch-private
25* platform/ec
26* private-overlays/overlay-hatch-private
27* overlays
28
29Copyright 2019 The Chromium OS Authors. All rights reserved.
30Use of this source code is governed by a BSD-style license that can be
31found in the LICENSE file.
32"""
33
34from __future__ import print_function
35import argparse
36import logging
37import os
38import re
39import shutil
40import subprocess
41import sys
42import variant_status
43
44
45def main():
46 """Create a new variant of an existing base board
47
48 This program automates the creation of a new variant of an existing
49 base board by calling various scripts that clone the base board, modify
50 files for the new variant, stage commits, and upload to gerrit.
51
52 Note that one of the following is required:
53 * --continue
54 * --board=BOARD --variant=VARIANT [--bug=BUG]
55 """
56 board, variant, bug, continue_flag = get_args()
57
58 if not check_flags(board, variant, bug, continue_flag):
59 return False
60
61 status = get_status(board, variant, bug, continue_flag)
62 if status is None:
63 return False
64
65 status.load()
66
67 while status.stage is not None:
68 status.save()
69 if not perform_stage(status):
70 logging.debug('perform_stage returned False; exiting ...')
71 return False
72
73 move_to_next_stage(status)
74
75 return True
76
77
78def get_args():
79 """Parse the command-line arguments
80
81 There doesn't appear to be a way to specify that --continue is
82 mutually exclusive with --board, --variant, and --bug. As a result,
83 all arguments are optional, and another function will apply the logic
84 to check if there is an illegal combination of arguments.
85
86 Returns a list of:
87 board Name of the base board
88 variant Name of the variant being created
89 bug Text for bug number, if any ('None' otherwise)
90 continue_flag Flag if --continue was specified
91 """
92 parser = argparse.ArgumentParser(
93 description=main.__doc__,
94 formatter_class=argparse.RawTextHelpFormatter)
95 parser.add_argument('--board', type=str, help='Name of the base board')
96 parser.add_argument(
97 '--variant', type=str, help='Name of the new variant to create')
98 parser.add_argument(
99 '--bug', type=str, help='Bug number to reference in commits')
100 parser.add_argument(
101 '--continue', action='store_true',
102 dest='continue_flag', help='Continue the process from where it paused')
103 parser.add_argument(
104 '--verbose', action='store_true',
105 dest='verbose_flag', help='Enable verbose output of progress')
106 args = parser.parse_args()
107
108 if args.verbose_flag:
109 logging.basicConfig(level=logging.DEBUG)
110 else:
111 logging.basicConfig(level=logging.INFO)
112
113 board = args.board
114 if board is not None:
115 board = board.lower()
116
117 variant = args.variant
118 if variant is not None:
119 variant = variant.lower()
120
121 bug = args.bug or 'None'
122
123 return (board, variant, bug, args.continue_flag)
124
125
126def check_flags(board, variant, bug, continue_flag):
127 """Check the flags to ensure no invalid combinations
128
129 We allow any of the following:
130 --continue
131 --board=board_name --variant=variant_name
132 --board=board_name --variant=variant_name --bug=bug_text
133
134 The argument parser does have the functionality to represent the
135 combination of --board and --variant as a single mutually-exclusive
136 argument, so we have to use this function to do the checking.
137
138 Params:
139 board Name of the base board
140 variant Name of the variant being created
141 bug Text for bug number, if any ('None' otherwise)
142 continue_flag Flag if --continue was specified
143
144 Returns:
145 True if the arguments are acceptable, False otherwise
146 """
147 if continue_flag:
148 if board is not None or variant is not None:
149 logging.error('--continue cannot have other options')
150 return False
151
152 if bug != 'None':
153 logging.error('--continue cannot have other options')
154 return False
155 else:
156 if board is None:
157 logging.error('--board must be specified')
158 return False
159
160 if variant is None:
161 logging.error('--variant must be specified')
162 return False
163
164 return True
165
166
167def file_exists(filepath, filename):
168 """Determine if a path and file exists
169
170 Params:
171 filepath Path where build outputs should be found, e.g.
172 /build/hatch/firmware
173 filename File that should exist in that path, e.g.
174 image-sushi.bin
175
176 Returns:
177 True if file exists in that path, False otherwise
178 """
179 fullname = os.path.join(filepath, filename)
180 return os.path.exists(fullname) and os.path.isfile(fullname)
181
182
183def get_status(board, variant, bug, continue_flag):
184 """Create the status file or get the previous status
185
186 This program can stop at several places as we have to wait for CLs
187 to work through CQ or be upstreamed into the chromiumos tree, so just
188 like a git cherry-pick, there is a --continue option to pick up where
189 you left off.
190
191 If the --continue flag is present, make sure that the status file
192 exists, and fail if it doesn't.
193
194 If the --continue flag is not present, then create the status file
195 with the board, variant, and bug details. If the status file already
196 exists, this is an error case.
197
198 The function returns an object with several fields:
199 * board - the name of the baseboard, e.g. 'hatch'
200 * variant - the name of the variant, e.g. 'sushi'
201 * bug - optional text for a bug ID, used in the git commit messages.
202 Could be 'None' (as text, not the python None), or something like
203 'b:12345' for buganizer, or 'chromium:12345'
204 * workon - list of packages that will need `cros_workon start` before
205 we can `emerge`. Each function can add package names to this list.
206 * emerge - list of packages that we need to `emerge` at the end. Each
207 functions can add package names to this list.
208 * stage - internal state tracking, what stage of the variant creation
209 we are at.
210 * yaml_file - internal, just the name of the file where all this data
211 gets saved.
212
213 These data might come from the status file (because we read it), or
214 they might be the initial values after we created the file (because
215 it did not already exist).
216
217 Params:
218 board Name of the base board
219 variant Name of the variant being created
220 bug Text for bug number, if any ('None' otherwise)
221 continue_flag Flag if --continue was specified
222
223 Returns:
224 variant_status object that points to the yaml file
225 """
226 status = variant_status.variant_status()
227 if continue_flag:
228 if not status.yaml_file_exists():
229 logging.error(
230 'new_variant is not in progress; nothing to --continue')
231 return None
232 else:
233 if status.yaml_file_exists():
234 logging.error(
235 'new_variant already in progress; did you forget --continue?')
236 return None
237
238 status.board = board
239 status.variant = variant
240 status.bug = bug
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700241
242 if board not in ['hatch', 'volteer']:
243 print('Unsupported baseboard "' + board + '"')
244 sys.exit(1)
245
246 # Depending on the board, we can have different values here
247 if board == 'hatch':
248 status.stage = 'cb_variant'
249 status.stage_list = [CB_VARIANT, CB_CONFIG, ADD_FIT, GEN_FIT,
250 COMMIT_FIT, EC_IMAGE, EC_BUILDALL, ADD_YAML, BUILD_YAML,
251 EMERGE, PUSH, UPLOAD, FIND, CQ_DEPEND, CLEAN_UP]
252
253 if board == 'volteer':
254 # TODO(pfagerburg) this list of stages will change
255 status.stage = 'cb_variant'
256 status.stage_list = [CB_VARIANT, CB_CONFIG, ADD_FIT, GEN_FIT,
257 COMMIT_FIT, EC_IMAGE, EC_BUILDALL, ADD_YAML, BUILD_YAML,
258 EMERGE, PUSH, UPLOAD, FIND, CQ_DEPEND, CLEAN_UP]
259
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700260 status.save()
261
262 return status
263
264
265# Constants for the stages, so we don't have to worry about misspelling them
266# pylint: disable=bad-whitespace
267# Allow extra spaces around = so that we can line things up nicely
268CB_VARIANT = 'cb_variant'
269CB_CONFIG = 'cb_config'
270ADD_FIT = 'add_fit'
271GEN_FIT = 'gen_fit'
272COMMIT_FIT = 'commit_fit'
273EC_IMAGE = 'ec_image'
274EC_BUILDALL = 'ec_buildall'
275ADD_YAML = 'add_yaml'
276BUILD_YAML = 'build_yaml'
277EMERGE = 'emerge'
278PUSH = 'push'
279UPLOAD = 'upload'
280FIND = 'find'
281CQ_DEPEND = 'cq_depend'
282CLEAN_UP = 'clean_up'
283# pylint: enable=bad-whitespace
284
285
286def perform_stage(status):
287 """Call the appropriate function for the current stage
288
289 Params:
290 st dictionary that provides details including
291 the board name, variant name, and bug ID
292
293 Returns:
294 True if the stage succeeded, False if it failed
295 """
296 # Function to call based on the stage
297 dispatch = {
298 CB_VARIANT: create_coreboot_variant,
299 CB_CONFIG: create_coreboot_config,
300 ADD_FIT: add_fitimage,
301 GEN_FIT: gen_fit_image_outside_chroot,
302 COMMIT_FIT: commit_fitimage,
303 EC_IMAGE: create_initial_ec_image,
304 EC_BUILDALL: ec_buildall,
305 ADD_YAML: add_variant_to_yaml,
306 BUILD_YAML: build_yaml,
307 EMERGE: emerge_all,
308 PUSH: push_coreboot,
309 UPLOAD: upload_CLs,
310 FIND: find_coreboot_upstream,
311 CQ_DEPEND: add_cq_depends,
312 CLEAN_UP: clean_up,
313 }
314
315 if status.stage not in dispatch:
316 logging.error('Unknown stage "%s", aborting...', status.stage)
317 sys.exit(1)
318
319 return dispatch[status.stage](status)
320
321
322def move_to_next_stage(status):
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700323 """Move to the next stage in the list
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700324
325 Params:
326 status variant_status object tracking our board, variant, etc.
327 """
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700328 if status.stage not in status.stage_list:
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700329 logging.error('Unknown stage "%s", aborting...', status.stage)
330 sys.exit(1)
331
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700332 idx = status.stage_list.index(status.stage)
333 if idx == len(status.stage_list)-1:
334 status.stage = None
335 else:
336 status.stage = status.stage_list[idx+1]
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700337
338
339def run_process(args, *, cwd=None, env=None):
340 """Wrapper for subprocess.run that will produce debug-level messages
341
342 Params:
343 LImited subset, same as for subprocess.run
344
345 Returns:
346 Return value from subprocess.run
347 """
348 logging.debug('Run %s', str(args))
349 retval = subprocess.run(args, cwd=cwd, env=env).returncode
350 logging.debug('process returns %s', str(retval))
351 return retval
352
353
354def cros_workon(status, action):
355 """Call cros_workon for all the 9999 ebuilds we'll be touching
356
357 TODO(pfagerburg) detect 9999 ebuild to know if we have to workon the package
358
359 Params:
360 status variant_status object tracking our board, variant, etc.
361 action 'start' or 'stop'
362
363 Returns:
364 True if the call to cros_workon was successful, False if failed
365 """
366
367 # Build up the command from all the packages in the list
368 workon_cmd = ['cros_workon', '--board=' + status.board, action] + status.workon
369 return run_process(workon_cmd) == 0
370
371
372def create_coreboot_variant(status):
373 """Create source files for a new variant of the base board in coreboot
374
375 This function calls create_coreboot_variant.sh to set up a new variant
376 of the base board.
377
378 Params:
379 status variant_status object tracking our board, variant, etc.
380
381 Returns:
382 True if everything succeeded, False if something failed
383 """
384 logging.info('Running stage create_coreboot_variant')
385 status.workon += ['coreboot', 'libpayload', 'vboot_reference',
386 'depthcharge']
387 # Despite doing the workon here, we don't add this to emerge, because
388 # without the configuration (create_coreboot_config), `emerge coreboot`
389 # won't build the new variant.
390 status.emerge += ['libpayload', 'vboot_reference', 'depthcharge',
391 'chromeos-bootimage']
392
393 create_coreboot_variant_sh = os.path.join(
394 os.path.expanduser('~/trunk/src/third_party/coreboot'),
Paul Fagerburg7b516d72019-12-20 13:13:41 -0700395 'util/mainboard/google/create_coreboot_variant.sh')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700396 return run_process(
397 [create_coreboot_variant_sh,
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700398 status.board,
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700399 status.variant,
400 status.bug]) == 0
401
402
403def create_coreboot_config(status):
404 """Create a coreboot configuration for a new variant
405
406 This function calls create_coreboot_config.sh, which will make a copy
407 of coreboot.${BOARD} into coreboot.${VARIANT}.
408
409 Params:
410 status variant_status object tracking our board, variant, etc.
411
412 Returns:
413 True if the script and test build succeeded, False if something failed
414 """
415 logging.info('Running stage create_coreboot_config')
416 status.emerge += ['coreboot']
417 create_coreboot_config_sh = os.path.expanduser(
418 '~/trunk/src/platform/dev/contrib/variant/create_coreboot_config.sh')
419 return run_process(
420 [create_coreboot_config_sh,
421 status.board,
422 status.variant,
423 status.bug]) == 0
424
425
426def add_fitimage(status):
427 """Add the source files for a fitimage for the new variant
428
429 This function calls add_fitimage.sh to create a new XSL file for the
430 variant's fitimage, which can override settings from the base board's XSL.
431 When this is done, the user will have to build the fitimage by running
432 gen_fit_image.sh outside of the chroot (and outside of this program's
433 control) because gen_fit_image.sh uses WINE, which is not installed in
434 the chroot. (There is a linux version of FIT, but it requires Open GL,
435 which is also not installed in the chroot.)
436
437 Params:
438 status variant_status object tracking our board, variant, etc.
439
440 Returns:
441 True if the script succeeded, False otherwise
442 """
443 logging.info('Running stage add_fitimage')
444 status.workon += ['intel-cmlfsp']
445 status.emerge += ['intel-cmlfsp']
446 add_fitimage_sh = os.path.expanduser(os.path.join(
447 '~/trunk/src/private-overlays',
448 'baseboard-' + status.board + '-private',
449 'sys-boot',
450 'coreboot-private-files-' + status.board,
451 'files/add_fitimage.sh'))
452 return run_process(
453 [add_fitimage_sh,
454 status.variant,
455 status.bug]) == 0
456
457
458def gen_fit_image_outside_chroot(status):
459 """Tell the user to run gen_fit_image.sh outside the chroot
460
461 As noted for add_Fitimage(), gen_fit_image.sh cannot run inside the
462 chroot. This function tells the user to run gen_fit_image.sh in
463 their normal environment, and then come back (--continue) when that
464 is done.
465
466 Params:
467 status variant_status object tracking our board, variant, etc.
468
469 Returns:
470 True
471 """
472 logging.info('Running stage gen_fit_image_outside_chroot')
473 fit_image_files = check_fit_image_files(status)
474 # If the list is empty, then `not` of the list is True, so the files
475 # we need are all present and we can continue.
476 if not fit_image_files:
477 return True
478
479 logging.info('The following files need to be generated:')
480 for filename in fit_image_files:
481 logging.info('* %s', filename)
482 logging.info('The fitimage sources are ready for gen_fit_image.sh to process.')
483 logging.info('gen_fit_image.sh cannot run inside the chroot. Please open a new terminal')
484 logging.info('window, change to the directory where gen_fit_image.sh is located, and run')
485 logging.info('./gen_fit_image.sh %s [location of FIT] -b', status.variant)
486 logging.info('Then re-start this program with --continue.')
487 logging.info('If your chroot is based in ~/chromiumos, then the folder you want is')
488 logging.info('~/chromiumos/src/private-overlays/baseboard-%s-private/sys-boot'
489 '/coreboot-private-files-%s/asset_generation', status.board, status.board)
490 return False
491
492
493def check_fit_image_files(status):
494 """Check if the fitimage has been generated
495
496 This function is not called directly as a stage, and so it doesn't need
497 to produce any error messages to the user (except with --verbose).
498 gen_fit_image_outside_chroot will call this function to see if the
499 fitimage files exist, and if not, then that function will print the
500 message about how the user needs to run gen_fit_image.sh outside the
501 chroot.
502
503 Params:
504 status variant_status object tracking our board, variant, etc.
505
506 Returns:
507 List of files that *DO NOT* exist and need to be created, [] if
508 all files are present.
509 """
510 fitimage_dir = os.path.expanduser(os.path.join(
511 '~/trunk/src/private-overlays',
512 'baseboard-' + status.board + '-private',
513 'sys-boot',
514 'coreboot-private-files-' + status.board,
515 'asset_generation/outputs'))
516 logging.debug('fitimage_dir = "%s"', fitimage_dir)
517
518 files = []
519 if not file_exists(fitimage_dir, 'fitimage-' + status.variant + '.bin'):
520 files.append('fitimage-' + status.variant + '.bin')
521
522 if not file_exists(fitimage_dir,
523 'fitimage-' + status.variant + '-versions.txt'):
524 files.append('fitimage-' + status.variant + '-versions.txt')
525
526 if not file_exists(fitimage_dir, 'fit.log'):
527 files.append('fit.log')
528
529 return files
530
531
532def move_fitimage_file(fitimage_dir, filename):
533 """Move fitimage files from create-place to commit-place
534
535 commit_fitimage needs to move the fitimage files from the place where
536 they were created to a different directory in the tree. This utility
537 function handles joining paths and calling a file move function.
538
539 Params:
540 fitimage_dir Directory where the fitimage files are
541 filename Name of the file being moved
542
543 Returns:
544 True if the move succeeded, False if it failed
545 """
546 src_dir = os.path.join(fitimage_dir, 'asset_generation/outputs')
547 src = os.path.join(src_dir, filename)
548 dest_dir = os.path.join(fitimage_dir, 'files')
549 dest = os.path.join(dest_dir, filename)
550 # If src does not exist and dest does, the move is already done => success!
551 if not file_exists(src_dir, filename) and file_exists(dest_dir, filename):
552 logging.debug('move "%s", "%s" unnecessary because dest exists and'
553 ' src does not exist', src, dest)
554 return True
555
556 logging.debug('move "%s", "%s"', src, dest)
557 return shutil.move(src, dest)
558
559
560def commit_fitimage(status):
561 """Move the fitimage files and add them to a git commit
562
563 This function moves the fitimage binary and -versions files from
564 asset_generation/outputs to files/ and then adds those files and
565 fit.log to the existing git commit.
566
567 Params:
568 status variant_status object tracking our board, variant, etc.
569
570 Returns:
571 True if the copy, git add, and git commit --amend all succeeded.
572 False if something failed.
573 """
574 logging.info('Running stage commit_fitimage')
575 fitimage_dir = os.path.expanduser(os.path.join(
576 '~/trunk/src/private-overlays',
577 'baseboard-' + status.board + '-private',
578 'sys-boot',
579 'coreboot-private-files-' + status.board))
580 logging.debug('fitimage_dir = "%s"', fitimage_dir)
581
582 # The copy operation will check that the source file exists, so no
583 # need to check separately.
584 if not move_fitimage_file(fitimage_dir,
585 'fitimage-' + status.variant + '.bin'):
586 logging.error('Moving fitimage binary failed')
587 return False
588
589 if not move_fitimage_file(fitimage_dir,
590 'fitimage-' + status.variant + '-versions.txt'):
591 logging.error('Moving fitimage versions.txt failed')
592 return False
593
594 if run_process(
595 ['git', 'add',
596 'asset_generation/outputs/fit.log',
597 'files/fitimage-' + status.variant + '.bin',
598 'files/fitimage-' + status.variant + '-versions.txt'
599 ],
600 cwd=fitimage_dir) != 0:
601 return False
602
603 return run_process(['git', 'commit', '--amend', '--no-edit'],
604 cwd=fitimage_dir) == 0
605
606
607def create_initial_ec_image(status):
608 """Create an EC image for the variant as a clone of the base board
609
610 This function calls create_initial_ec_image.sh, which will clone the
611 base board to create the variant. The shell script will build the
612 EC code for the variant, but the repo upload hook insists that we
613 have done a `make buildall` before it will allow an upload, so this
614 function does the buildall.
615
616 Params:
617 status variant_status object tracking our board, variant, etc.
618
619 Returns:
620 True if the script and test build succeeded, False if something failed
621 """
622 logging.info('Running stage create_initial_ec_image')
623 status.workon += ['chromeos-ec']
624 status.emerge += ['chromeos-ec']
625 create_initial_ec_image_sh = os.path.expanduser(
626 '~/trunk/src/platform/dev/contrib/variant/create_initial_ec_image.sh')
627 if run_process(
628 [create_initial_ec_image_sh,
629 status.board,
630 status.variant,
631 status.bug]) != 0:
632 return False
633
634 # create_initial_ec_image.sh will build the ec.bin for this variant
635 # if successful.
636 ec = os.path.expanduser('~/trunk/src/platform/ec')
637 logging.debug('ec = "%s"', ec)
638 ec_bin = 'build/' + status.variant + '/ec.bin'
639 logging.debug('ec.bin = "%s"', ec_bin)
640
641 return file_exists(ec, ec_bin)
642
643
644def ec_buildall(status):
645 """Do a make buildall -j for the EC, which is required for repo upload
646
647 The upload hook checks to ensure that the entire EC codebase builds
648 without error, so we have to run make buildall -j before uploading.
649
650 Params:
651 status variant_status object tracking our board, variant, etc.
652
653 Returns:
654 True if the script and test build succeeded, False if something failed
655 """
656 logging.info('Running stage ec_buildall')
657 del status # unused parameter
658 ec = os.path.expanduser('~/trunk/src/platform/ec')
659 logging.debug('ec = "%s"', ec)
660 return run_process(['make', 'buildall', '-j'], cwd=ec) == 0
661
662
663def add_variant_to_yaml(status):
664 """Add the new variant to the public and private model.yaml files
665
666 This function calls add_variant_to_yaml.sh (the public yaml) and
667 add_variant.sh (the private yaml) to add the new variant to
668 the yaml files.
669
670 Params:
671 st dictionary that provides details including
672 the board name, variant name, and bug ID
673
674 Returns:
675 True if the scripts and build succeeded, False is something failed
676 """
677 logging.info('Running stage add_variant_to_yaml')
678 status.workon += ['chromeos-config-bsp-hatch-private']
679 status.emerge += ['chromeos-config', 'chromeos-config-bsp',
680 'chromeos-config-bsp-hatch', 'chromeos-config-bsp-hatch-private',
681 'coreboot-private-files', 'coreboot-private-files-hatch']
682 add_variant_to_yaml_sh = os.path.expanduser(
683 '~/trunk/src/platform/dev/contrib/variant/add_variant_to_yaml.sh')
684 if run_process(
685 [add_variant_to_yaml_sh,
686 status.board,
687 status.variant,
688 status.bug
689 ]) != 0:
690 return False
691
692 add_variant_sh = os.path.expanduser(os.path.join(
693 '~/trunk/src/private-overlays',
694 'overlay-' + status.board + '-private',
695 'chromeos-base',
696 'chromeos-config-bsp-' + status.board + '-private',
697 'add_variant.sh'))
698 if run_process(
699 [add_variant_sh,
700 status.variant,
701 status.bug
702 ]) != 0:
703 return False
704
705 return True
706
707
708def build_yaml(status):
709 """Build config files from the yaml files
710
711 This function builds the yaml files into the JSON and C code that
712 mosys and other tools use, then verifies that the new variant's name
713 shows up in all of the output files.
714
715 Params:
716 status variant_status object tracking our board, variant, etc.
717
718 Returns:
719 True if the scripts and build succeeded, False is something failed
720 """
721 logging.info('Running stage build_yaml')
722 if run_process(
723 ['emerge-' + status.board,
724 'chromeos-config-bsp-' + status.board,
725 'chromeos-config-bsp-' + status.board + '-private',
726 'chromeos-config-bsp',
727 'chromeos-config']) != 0:
728 return False
729
730 # Check generated files for occurences of the variant name.
731 # Each file should have at least one occurence, so use `grep -c` to
732 # count the occurrences of the variant name in each file.
733 # The results will be something like this:
734 # config.json:10
735 # yaml/config.c:6
736 # yaml/config.yaml:27
737 # yaml/model.yaml:6
738 # yaml/private-model.yaml:10
739 # If the variant name doesn't show up in the file, then the count
740 # will be 0, so we would see, e.g.
741 # config.json:0
742 # We gather the output from grep, decode as UTF-8, split along newlines,
743 # and then look for any of the strings ending in :0. If none of them
744 # match, then we're good, but if even one of them ends with :0 then
745 # there was a problem with generating the files from the yaml.
746 chromeos_config = '/build/' + status.board + '/usr/share/chromeos-config'
747 logging.debug('chromeos_config = "%s"', chromeos_config)
748 # Can't use run because we need to capture the output instead
749 # of a status code.
750 grep = subprocess.check_output(
751 ['grep',
752 '-c',
753 status.variant,
754 'config.json',
755 'yaml/config.c',
756 'yaml/config.yaml',
757 'yaml/model.yaml',
758 'yaml/private-model.yaml'], cwd=chromeos_config)
759 # Convert from byte string to ASCII
760 grep = grep.decode('utf-8')
761 # Split into array of individual lines
762 grep = grep.split('\n')
763 return not bool([s for s in grep if re.search(r':0$', s)])
764
765
766def emerge_all(status):
767 """Build the coreboot BIOS and EC code for the new variant
768
769 Params:
770 status variant_status object tracking our board, variant, etc.
771
772 Returns:
773 True if the build succeeded, False if something failed
774 """
775 logging.info('Running stage emerge_all')
776 cros_workon(status, 'start')
777 environ = os.environ.copy()
778 environ['FW_NAME'] = status.variant
779 # Build up the command for emerge from all the packages in the list
780 emerge_cmd_and_params = ['emerge-' + status.board] + status.emerge
781 if run_process(emerge_cmd_and_params, env=environ) != 0:
782 return False
783
784 cros_workon(status, 'stop')
785 build_path = '/build/' + status.board + '/firmware'
786 logging.debug('build_path = "%s"', build_path)
787 if not file_exists(build_path, 'image-' + status.variant + '.bin'):
788 logging.error('emerge failed because image-%s.bin does not exist',
789 status.variant)
790 return False
791
792 if not file_exists(build_path, 'image-' + status.variant + '.dev.bin'):
793 logging.error('emerge failed because image-%s.dev.bin does not exist',
794 status.variant)
795 return False
796
797 if not file_exists(build_path, 'image-' + status.variant + '.net.bin'):
798 logging.error('emerge failed because image-%s.net.bin does not exist',
799 status.variant)
800 return False
801
802 if not file_exists(build_path, 'image-' + status.variant + '.serial.bin'):
803 logging.error('emerge failed because image-%s.serial.bin does not exist',
804 status.variant)
805 return False
806
807 return True
808
809
810def push_coreboot(status):
811 """Push the coreboot CL to coreboot.org
812
813 Params:
814 status variant_status object tracking our board, variant, etc.
815
816 Returns:
817 True if the build succeeded, False if something failed
818 """
819 logging.info('Running stage push_coreboot')
820 del status # unused parameter
821 logging.error('TODO (pfagerburg): implement push_coreboot')
822 return True
823
824
825def upload_CLs(status):
826 """Upload all CLs to chromiumos
827
828 Params:
829 status variant_status object tracking our board, variant, etc.
830
831 Returns:
832 True if the build succeeded, False if something failed
833 """
834 logging.info('Running stage upload_CLs')
835 del status # unused parameter
836 logging.error('TODO (pfagerburg): implement upload_CLs')
837 return True
838
839
840def find_coreboot_upstream(status):
841 """Find the coreboot CL after it has been upstreamed to chromiumos
842
843 Params:
844 status variant_status object tracking our board, variant, etc.
845
846 Returns:
847 True if the build succeeded, False if something failed
848 """
849 logging.info('Running stage find_coreboot_upstream')
850 del status # unused parameter
851 logging.error('TODO (pfagerburg): implement find_coreboot_upstream')
852 return True
853
854
855def add_cq_depends(status):
856 """Add Cq-Depends to all of the CLs in chromiumos
857
858 The CL in coreboot needs to be pushed to coreboot.org, get merged,
859 and then get upstreamed into the chromiumos tree before the other
860 CLs can cq-depend on it and pass CQ.
861
862 Params:
863 status variant_status object tracking our board, variant, etc.
864
865 Returns:
866 True if the build succeeded, False if something failed
867 """
868 logging.info('Running stage add_cq_depends')
869 del status # unused parameter
870 logging.error('TODO (pfagerburg): implement add_cq_depends')
871 return True
872
873
874def clean_up(status):
875 """Final clean-up, including delete the status file
876
877 Params:
878 status variant_status object tracking our board, variant, etc.
879
880 Returns:
881 True
882 """
883 logging.info('Running stage clean_up')
884 status.rm()
885 return True
886
887
888if __name__ == '__main__':
889 sys.exit(not int(main()))