blob: 11df2f5af188361276202758a0143d56e89976f1 [file] [log] [blame]
Jack Rosenthal543bb062020-06-05 15:30:09 -06001#!/usr/bin/env python3
2# Copyright 2020 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6# This file uses 2-space indentations.
7# pylint: disable=bad-indentation
8
9# This is contrib-quality code: not all functions/classes are
10# documented.
11# pylint: disable=missing-function-docstring
12# pylint: disable=missing-class-docstring
13# pylint: disable=class-missing-docstring
14
15# Classes make heavy-use of setattr to dynamically set the attributes
16# on an object. Disable this check which gets confused very
17# frequently.
18# pylint: disable=no-member
19
20"""Utility script to auto-convert a pre-unibuild board to unibuild."""
21
22import argparse
23import datetime
24import json
25import os
26import pathlib
27import re
28import shlex
29import subprocess
30import sys
31import tempfile
32# pylint: disable=import-error
33import yaml
34# pylint: enable=import-error
35
36
37make_defaults_search_and_destroy_re = re.compile(
38 r'(?:^\s*)*(?:^\s*#.*\s*)*^\s*USE="\s*\$\{?USE\}?\s*-unibuild\s*"\s*$',
39 re.MULTILINE)
40
41
42def log(message):
43 print('[{}] {}'.format(datetime.datetime.now(), message), file=sys.stderr)
44
45
46def prepend_all_lines(text, prepend):
47 return ''.join(
48 '{}{}\n'.format(prepend, line)
49 for line in text.splitlines())
50
51
52def gen_cros_copyright(line_comment='# '):
53 return prepend_all_lines(
54 """Copyright {} The Chromium OS Authors. All rights reserved.
Jack Rosenthalf72933c2020-06-24 13:37:50 -060055Use of this source code is governed by a BSD-style license that can be
Jack Rosenthal543bb062020-06-05 15:30:09 -060056found in the LICENSE file.""".format(datetime.datetime.now().strftime('%Y')),
57 line_comment)
58
59
60def yaml_str_representer(dumper, data):
61 style = None
62 tag = 'tag:yaml.org,2002:str'
63 if '\n' in data:
64 style = '|'
65 return dumper.represent_scalar(tag, data, style)
66
67
68yaml.add_representer(str, yaml_str_representer)
69
70
71def format_yaml(config):
72 conf_str = yaml.dump(config, indent=2, default_flow_style=False)
73 out = gen_cros_copyright()
74 out += """
75# This board only supports a single config, defined below, as it is a
76# migrated pre-unibuild device.
77device-config: &device_config\n"""
78 out += prepend_all_lines(conf_str, ' ')
79 out += """
80# Required dunder for chromeos-config to support a single device.
81chromeos:
82 devices:
83 - skus:
84 - config: *device_config\n"""
85 return out
86
87
88def generate_vpackage(depends):
89 return gen_cros_copyright() + """
90EAPI=7
91
Jack Rosenthal543bb062020-06-05 15:30:09 -060092DESCRIPTION="ChromeOS Unibuild Config virtual package"
93HOMEPAGE="https://chromium.googlesource.com/chromiumos/platform2/+/master/chromeos-config/README.md"
94
95LICENSE="BSD-Google"
96SLOT="0"
Jack Rosenthalbe2f1272020-06-10 09:31:09 -060097KEYWORDS="*"
Jack Rosenthal543bb062020-06-05 15:30:09 -060098
99DEPEND="%(depends)s"
100RDEPEND="${DEPEND}"
101""" % {
102 'depends':
103 (''.join('\n\t{}'.format(d) for d in depends) + '\n')
104 if len(depends) > 1 else
105 ''.join(depends)}
106
107
108def generate_bsp_ebuild(private=False):
109 return gen_cros_copyright() + """
110EAPI=7
111
112# cros_workon applies only to ebuild and files directory. Use the
113# canonical empty project.
114CROS_WORKON_PROJECT="chromiumos/infra/build/empty-project"
Jack Rosenthalbe2f1272020-06-10 09:31:09 -0600115CROS_WORKON_LOCALNAME="platform/empty-project"
Jack Rosenthal543bb062020-06-05 15:30:09 -0600116
117inherit cros-workon cros-unibuild
118
119DESCRIPTION="ChromeOS model configuration"
120HOMEPAGE="https://chromium.googlesource.com/chromiumos/platform2/+/master/chromeos-config/README.md"
121
122LICENSE="BSD-Google"
123SLOT="0"
124KEYWORDS="~*"
125
126src_install() {
127\tinstall%(maybe_private)s_model_files
128}
129""" % {'maybe_private': '_private' if private else ''}
130
131
132def generate_firmware_ebuild(board_name):
133 return gen_cros_copyright() + """
134# Change this version number when any change is made to model.yaml
135# in order to trigger an auto-revbump is required.
136# VERSION=REVBUMP-0.0.1
137
138EAPI=7
139CROS_WORKON_COMMIT=""
140CROS_WORKON_TREE=""
141CROS_WORKON_LOCALNAME="platform/firmware"
142CROS_WORKON_PROJECT="chromiumos/platform/firmware"
143CROS_BOARDS=( %(board_name)s )
144
145inherit cros-workon cros-firmware cros-unibuild
146
147DESCRIPTION="Chrome OS Firmware (%(board_name)s)"
148HOMEPAGE="http://src.chromium.org"
149LICENSE="BSD-Google"
150SLOT="0"
151KEYWORDS="~*"
152
153RDEPEND=""
154
155# Unified Builds firmware URL's are read from:
156# chromeos-base/chromeos-config-bsp-private/files/model.yaml
157# in this repository. Those config files output the SRC_URI's used by Portage.
158#
159# Update the model.yaml, then run this command from the
160# src/platform/dev/contrib directory:
161#
162# ./cros_update_firmware --board=%(board_name)s
163#
164# Verify the changes by running:
165# /build/%(board_name)s/usr/sbin/chromeos-firmwareupdate --manifest
166#
167# If this works then you can create a CL with your changes, which should include
168# the files:
169# chromeos-base/chromeos-config-bsp-private/files/model.yaml
170# chromeos-base/chromeos-firmware-%(board_name)s/Manifest
171# chromeos-base/chromeos-firmware-%(board_name)s/files/srcuris
172# chromeos-base/chromeos-firmware-%(board_name)s/chromeos-firmware-%(board_name)s-9999.ebuild
173cros-firmware_setup_source
174""" % {'board_name': board_name}
175
176
177def find_file(searchdir, name):
178 results = []
179 for root, _, files in os.walk(searchdir):
180 if name in files:
181 results.append(pathlib.Path(root) / name)
182 return results
183
184
185def find_one_file(searchdir, name):
186 results = find_file(searchdir, name)
187 assert len(results) == 1
188 return results.pop()
189
190
191def sh_getvar(script, varname):
192 script = script + ('\necho "${%s}"\n' % varname)
193 with tempfile.NamedTemporaryFile('w') as f:
194 f.write(script)
195 f.flush()
196 res = subprocess.run(['sh', f.name], stdout=subprocess.PIPE,
197 check=True, encoding='utf-8')
198 return res.stdout.strip() or None
199
200
Jack Rosenthalbe2f1272020-06-10 09:31:09 -0600201def write_file(fullpath, file_contents, make_ebuild_symlink=False):
Jack Rosenthal543bb062020-06-05 15:30:09 -0600202 os.makedirs(fullpath.parent, exist_ok=True)
203 log('Writing {}...'.format(fullpath))
204 with open(fullpath, 'w') as f:
205 f.write(file_contents)
Jack Rosenthalbe2f1272020-06-10 09:31:09 -0600206 if make_ebuild_symlink:
207 if not fullpath.name.endswith('.ebuild'):
208 raise ValueError(
209 'make_ebuild_symlink specified, but path does not look like an ebuild')
210 prefix, _, _ = fullpath.name.rpartition('.')
211 linkname = fullpath.parent / '{}-r1.ebuild'.format(prefix)
212 log('Creating symlink {} -> {}...'.format(linkname, fullpath))
213 os.symlink(fullpath.name, linkname)
Jack Rosenthal543bb062020-06-05 15:30:09 -0600214
215
216def generate_make_defaults(contents):
217 contents = make_defaults_search_and_destroy_re.sub('', contents)
218 contents += """
219# Enable chromeos-config.
220USE="${USE} unibuild"
221"""
222 return contents
223
224
225class CrosConfig:
226 def __init__(self, public_yaml_raw, private_yaml_raw):
227 with tempfile.NamedTemporaryFile(mode='w', delete=False) as merged_tempfile, \
228 tempfile.NamedTemporaryFile(mode='w') as public_yaml_tempfile, \
229 tempfile.NamedTemporaryFile(mode='w') as private_yaml_tempfile:
230 public_yaml_tempfile.write(public_yaml_raw)
231 public_yaml_tempfile.flush()
232
233 private_yaml_tempfile.write(private_yaml_raw)
234 private_yaml_tempfile.flush()
235
236 log('Merging and validating config schema...')
237 subprocess.run(['cros_config_schema', '-o', merged_tempfile.name,
238 '-m', public_yaml_tempfile.name,
239 private_yaml_tempfile.name], check=True)
240 self.merged_yaml = merged_tempfile.name
241
242 def run_host_command(self, *args):
243 return subprocess.run(['cros_config_host', '-c', self.merged_yaml]
244 + list(args),
245 check=True, encoding='utf-8',
246 stdout=subprocess.PIPE).stdout
247
248
249class BoardOverlays:
250 FIRMWARE_ATTRS = [
251 ('CROS_FIRMWARE_MAIN_IMAGE', 'bcs_main_ro'),
252 ('CROS_FIRMWARE_MAIN_RW_IMAGE', 'bcs_main_rw'),
253 ('CROS_FIRMWARE_EC_IMAGE', 'bcs_ec'),
254 ('CROS_FIRMWARE_PD_IMAGE', 'bcs_pd'),
255 ]
256
257 MAKE_DEFAULTS_ATTRS = [
258 ('EC_FIRMWARE', 'ec_firmwares'),
259 ('PD_FIRMWARE', 'pd_firmwares'),
260 ('EC_FIRMWARE_EXTRA', 'ec_firmware_extras'),
261 ('FPMCU_FIRMWARE', 'fpmcu_firmware'),
262 ('USE', 'use_flags'),
263 ]
264
265 def __init__(self, board_name, checkout, mosys_platform):
Jack Rosenthal19b40eb2020-06-24 15:38:45 -0600266 self.checkout = checkout
Jack Rosenthal543bb062020-06-05 15:30:09 -0600267 self.board_name = board_name
268 self.mosys_platform = mosys_platform
269 self.public_overlay = (checkout / 'src' / 'overlays'
270 / f'overlay-{board_name}')
271 log('Public overlay path: {}'.format(self.public_overlay))
272 self.private_overlay = (checkout / 'src' / 'private-overlays'
273 / f'overlay-{board_name}-private')
274 log('Private overlay path: {}'.format(self.private_overlay))
275
276 assert self.public_overlay.is_dir()
277 assert self.private_overlay.is_dir()
278
279 # Find the firmware ebuild
280 self.firmware_ebuild_path = find_one_file(
281 self.private_overlay, f'chromeos-firmware-{board_name}-9999.ebuild')
282 log('Firmware ebuild path: {}'.format(self.firmware_ebuild_path))
283
284 # Read the firmware attrs from it
285 for _, attr in self.FIRMWARE_ATTRS:
286 setattr(self, attr, None)
287
288 with open(self.firmware_ebuild_path) as f:
289 for line in f:
290 if '#' in line:
291 line, _, _ = line.partition('#')
292 line = line.strip()
293
294 for var, attr in self.FIRMWARE_ATTRS:
295 if line.startswith('{}='.format(var)):
296 _, _, value = line.partition('=')
297 value = value.replace('"', '').replace("'", '')
298 setattr(self, attr, value)
299
300 # Find make.defaults files
301 self.public_make_defaults_file = (
302 self.public_overlay / 'profiles' / 'base' / 'make.defaults')
303 self.private_make_defaults_file = (
304 self.private_overlay / 'profiles' / 'base' / 'make.defaults')
305
306 with open(self.public_make_defaults_file) as f:
307 self.public_make_defaults = f.read()
308 with open(self.private_make_defaults_file) as f:
309 self.private_make_defaults = f.read()
310
311 for var, attr in self.MAKE_DEFAULTS_ATTRS:
312 setattr(self, attr, set())
313 for script in (self.public_make_defaults, self.private_make_defaults):
314 value = sh_getvar(script, var)
315 if value:
316 for v in value.split():
317 getattr(self, attr).add(v)
318
319 if 'whiskers' in self.ec_firmware_extras:
320 self.ec_firmware_extras.remove('whiskers')
321 self.detachable_base_build_target = 'whiskers'
322 else:
323 self.detachable_base_build_target = None
324
325 self.ec_build_target = ' '.join(self.ec_firmwares) or None
326 self.ec_extras_build_target = sorted(list(self.ec_firmware_extras
327 | self.pd_firmwares)) or None
328
Jack Rosenthalbe2f1272020-06-10 09:31:09 -0600329 def write_file(self, overlay_flags, path, file_contents,
330 make_ebuild_symlink=False):
Jack Rosenthal543bb062020-06-05 15:30:09 -0600331 dirs = []
332 if overlay_flags & M_PUBLIC:
333 dirs += [self.public_overlay]
334 if overlay_flags & M_PRIVATE:
335 dirs += [self.private_overlay]
336 for d in dirs:
Jack Rosenthalbe2f1272020-06-10 09:31:09 -0600337 write_file(d / path, file_contents,
338 make_ebuild_symlink=make_ebuild_symlink)
Jack Rosenthal543bb062020-06-05 15:30:09 -0600339
340
341class Dut:
342 def __init__(self, hostname, checkout, port=22):
343 self.ssh_hostname = hostname
344
345 id_source = checkout / 'chromite' / 'ssh_keys' / 'testing_rsa'
346 with open(id_source, 'rb') as f:
347 id_contents = f.read()
348
349 with tempfile.NamedTemporaryFile(mode='wb', delete=False) as tmpfile:
350 tmpfile.write(id_contents)
351 self.ssh_identity = tmpfile.name
352
353 with tempfile.NamedTemporaryFile(delete=False) as tmpfile:
354 self.ssh_known_hosts_file = tmpfile.name
355
356 self.ssh_port = port
357
358 # Check connectivity.
359 log('Checking SSH connectivity to DUT...')
360 self.run_command(['/bin/true'])
361
362 # Linter is unaware that we set check=True in kwargs.
363 # pylint: disable=subprocess-run-check
364 def run_command(self, argv, *args, **kwargs):
365 kwargs.setdefault('check', True)
366 kwargs.setdefault('stdout', subprocess.PIPE)
367 kwargs.setdefault('encoding', 'utf-8')
368 quoted_argv = [shlex.quote(arg) for arg in argv]
369 return subprocess.run(['ssh',
370 '-p', '{}'.format(self.ssh_port),
371 '-i', self.ssh_identity,
372 '-o', 'UserKnownHostsFile={}'.format(
373 self.ssh_known_hosts_file),
374 '-o', 'StrictHostKeyChecking=no',
375 '-o', 'CheckHostIP=no',
376 '-o', 'ConnectTimeout=10',
377 'root@{}'.format(self.ssh_hostname)] + quoted_argv,
378 *args, **kwargs)
379 # pylint: enable=subprocess-run-check
380
381
382class DeviceConfig:
383 ATTRS = {
384 'brand_code': ['mosys', 'platform', 'brand'],
385 'model': ['mosys', 'platform', 'model'],
386 'lsb_release': ['cat', '/etc/lsb-release'],
387 'smbios_name': ['cat', '/sys/class/dmi/id/product_name'],
388 'fdt_compatible_raw': ['cat', '/proc/device-tree/compatible'],
389 'arc_build_props': ['cat', '/usr/share/arc/properties/build.prop'],
390 'mosys_psu_type': ['mosys', 'psu', 'type'],
391 'whitelabel_tag': ['vpd_get_value', 'whitelabel_tag'],
392 'customization_id': ['vpd_get_value', 'customization_id'],
393 'cras_config_dir': ['sh', '/etc/cras/get_device_config_dir'],
394 'internal_ucm_suffix': ['sh', '/etc/cras/get_internal_ucm_suffix'],
395 # disgusting, but whatever...
396 'powerd_raw':
397 ['python3', '-c',
398 'import os;'
399 'import json;'
400 'print(json.dumps('
401 '{f.replace("_", "-"): open("/usr/share/power_manager/board_specific/"+f).read().rstrip()'
402 ' for f in os.listdir("/usr/share/power_manager/board_specific")}))'],
403 }
404
405 @classmethod
406 def from_dut(cls, dut):
407 slf = cls()
408 for attr, cmd in cls.ATTRS.items():
409 try:
410 log('Running {!r} on DUT...'.format(cmd))
411 res = dut.run_command(cmd)
412 except subprocess.CalledProcessError:
413 setattr(slf, attr, None)
414 else:
415 setattr(slf, attr, res.stdout.strip())
416 return slf
417
418 def __str__(self):
419 return 'DeviceConfig({})'.format(
420 ', '.join('{}={!r}'.format(attr, getattr(self, attr))
421 for attr in self.ATTRS))
422
423 def lsb_val(self, name, default=None):
424 for item in self.lsb_release.splitlines():
425 k, _, v = item.partition('=')
426 if k == name:
427 return v
428 return default
429
430 def arc_build_prop(self, name, default=None):
431 for line in self.arc_build_props.splitlines():
432 if '#' in line:
433 line, _, _ = line.partition('#')
434 line = line.strip()
435 if line.startswith('{}='.format(name)):
436 _, _, val = line.partition('=')
437 return val
438 return default
439
440
441def genconf_first_api_level(_, overlay):
442 if overlay.board_name in ('atlas', 'nocturne'):
443 return '28'
444 return '25'
445
446
447def genconf_dt_compatible_match(device, overlay):
448 if not device.fdt_compatible_raw:
449 return None
450 compatible_strings = device.fdt_compatible_raw.strip('\x00').split('\x00')
451 compatible_strings.sort(key=lambda s: (s.startswith('google'),
452 'rev' not in s,
453 'sku' not in s,
454 overlay.board_name in s,
455 -len(s)))
456 return compatible_strings[-1]
457
458
459def genconf_psu_type(device, _):
460 if device.mosys_psu_type:
461 return device.mosys_psu_type
462 devicetype = device.lsb_val('DEVICETYPE')
463 if devicetype == 'CHROMEBOOK':
464 return 'battery'
465 if devicetype in ('CHROMEBIT', 'CHROMEBASE', 'CHROMEBOX'):
466 return 'AC_only'
467 return None
468
469
Nikolai Artemievadc67a92021-05-04 16:43:15 +1000470def genconf_form_factor(device, _):
471 devicetype = device.lsb_val('DEVICETYPE')
472 if devicetype in ('REFERENCE', 'CHROMEBOOK'):
473 return 'CHROMEBOOK'
474 if devicetype in ('CHROMEBIT', 'CHROMEBASE', 'CHROMEBOX'):
475 return devicetype
476 return None
477
478
Jack Rosenthal2ed088c2020-08-11 12:41:17 -0600479def genconf_has_backlight(device, _):
480 devicetype = device.lsb_val('DEVICETYPE')
481 return devicetype not in ('CHROMEBIT', 'CHROMEBOX')
482
483
Jack Rosenthal543bb062020-06-05 15:30:09 -0600484def genconf_fp_board(_, overlay):
485 if overlay.fpmcu_firmware:
486 return ' '.join(overlay.fpmcu_firmware)
487 return None
488
489
490def genconf_fp_type(_, overlay):
491 if 'fp_on_power_button' in overlay.use_flags:
492 return 'on-power-button'
493 if overlay.fpmcu_firmware:
494 return 'stand-alone'
495 return None
496
497
498def genconf_fp_location(_, overlay):
499 if overlay.board_name == 'nocturne':
500 return 'power-button-top-left'
501 return None
502
503
504def genconf_signature_id(device, _):
505 if device.whitelabel_tag:
506 return device.whitelabel_tag.upper()
507 if device.customization_id:
508 return device.customization_id.upper().partition('-')[0]
509 return device.model
510
511
512def genconf_cras_config_dir(device, _):
513 prefix = '/etc/cras/'
514 if device.cras_config_dir and device.cras_config_dir.startswith(prefix):
515 return device.cras_config_dir[len(prefix):]
516 if device.cras_config_dir:
517 return '../../{}'.format(device.cras_config_dir)
518 return None
519
520
521def genconf_powerd_settings(device, overlay):
522 if not device.powerd_raw:
523 d = {}
524 else:
525 d = json.loads(device.powerd_raw)
Jack Rosenthal263ba0e2020-07-06 10:49:15 -0600526
527 # 2-tuples of (use_flag, powerd_option)
528 # Source of truth is power_manager ebuild.
529 use_flag_settings = [
530 ('als', 'has-ambient-light-sensor'),
531 ('cras', 'use-cras'),
532 ('has_keyboard_backlight', 'has-keyboard-backlight'),
533 ('legacy_power_button', 'legacy-power-button'),
534 ('mosys_eventlog', 'mosys-eventlog'),
535 ]
536
537 for flag, powerd_setting in use_flag_settings:
538 if flag in overlay.use_flags:
539 d[powerd_setting] = '1'
540
Jack Rosenthal543bb062020-06-05 15:30:09 -0600541 return d
542
543
Jack Rosenthal36a4c242020-06-10 20:55:06 -0600544def genconf_whitelabel_tag(device, _):
545 # Devices with a Customization ID are not compatible with whitelabel
546 # tags.
547 if device.customization_id:
548 return None
549 return device.whitelabel_tag or None
550
551
Jack Rosenthal19b40eb2020-06-24 15:38:45 -0600552def genconf_wallpaper_id(device, overlay):
553 wallpapers_dir = (overlay.checkout / 'src' / 'platform' / 'chromeos-assets'
554 / 'wallpaper' / 'large')
555 assert wallpapers_dir.is_dir()
556 for wallpaper_id in (overlay.board_name, device.model):
557 if (wallpapers_dir / f'{wallpaper_id}.jpg').is_file():
558 return wallpaper_id
559 return None
560
561
Jack Rosenthal543bb062020-06-05 15:30:09 -0600562M_PUBLIC = (1 << 0)
563M_PRIVATE = (1 << 1)
564
565
566genconf_schema = {
567 'name': (M_PUBLIC | M_PRIVATE, lambda d, _: d.model),
568 'brand-code': (M_PUBLIC, lambda d, _: d.brand_code),
569 'arc': {
570 'build-properties': {
571 'device': (M_PRIVATE, lambda d, _:
572 d.arc_build_prop('ro.product.device')),
573 'marketing-name': (M_PRIVATE, lambda d, _:
574 d.arc_build_prop('ro.product.model')),
575 'oem': (M_PRIVATE,
576 lambda d, _: d.arc_build_prop('ro.product.brand')),
577 'first-api-level': (M_PRIVATE, genconf_first_api_level),
578 'metrics-tag': (M_PRIVATE,
579 lambda d, _: d.arc_build_prop('ro.product.board')),
580 'product': (M_PRIVATE, lambda d, _:
581 d.arc_build_prop('ro.product.name')),
582 },
583 },
584 'audio': {
585 'main': {
586 'cras-config-dir': (M_PUBLIC, genconf_cras_config_dir),
587 'ucm-suffix': (M_PUBLIC, lambda d, _: d.internal_ucm_suffix),
588 },
589 },
590 'fingerprint': {
591 'board': (M_PUBLIC, genconf_fp_board),
592 'fingerprint-sensor-type': (M_PUBLIC, genconf_fp_type),
593 'sensor-location': (M_PUBLIC, genconf_fp_location),
594 },
595 'firmware': {
596 'image-name': (M_PUBLIC, lambda d, _: d.model),
597 'name': (M_PRIVATE, lambda d, _: d.model),
598 'bcs-overlay': (M_PRIVATE, lambda _, b:
599 f'overlay-{b.board_name}-private'),
600 'ec-ro-image': (M_PRIVATE, lambda _, b: b.bcs_ec),
601 'pd-ro-image': (M_PRIVATE, lambda _, b: b.bcs_pd),
602 'main-ro-image': (M_PRIVATE, lambda _, b: b.bcs_main_ro),
603 'main-rw-image': (M_PRIVATE, lambda _, b: b.bcs_main_rw),
604 'build-targets': {
605 'base': (M_PUBLIC, lambda _, b: b.detachable_base_build_target),
606 'coreboot': (M_PUBLIC, lambda _, b: b.board_name),
607 'depthcharge': (M_PUBLIC, lambda _, b: b.board_name),
608 'ec': (M_PUBLIC, lambda _, b: b.ec_build_target),
609 'ec_extras': (M_PUBLIC, lambda _, b: b.ec_extras_build_target),
610 },
611 },
612 'firmware-signing': {
613 'key-id': (M_PRIVATE, lambda d, _: d.model.upper()),
614 'signature-id': (M_PRIVATE, genconf_signature_id),
615 },
616 'hardware-properties': {
Nikolai Artemievadc67a92021-05-04 16:43:15 +1000617 'form-factor': (M_PUBLIC, genconf_form_factor),
Jack Rosenthal25e72da2020-08-20 20:52:04 -0600618 'has-backlight': (M_PUBLIC, genconf_has_backlight),
Jack Rosenthal543bb062020-06-05 15:30:09 -0600619 'psu-type': (M_PUBLIC, genconf_psu_type),
620 },
621 'identity': {
622 'platform-name': (M_PUBLIC, lambda _, b: b.mosys_platform),
Jack Rosenthal9ec0ecd2020-06-17 18:13:56 -0600623 'smbios-name-match': (M_PUBLIC, lambda d, _: d.smbios_name),
624 'device-tree-compatible-match': (M_PUBLIC, genconf_dt_compatible_match),
625 'customization-id': (M_PUBLIC, lambda d, _: d.customization_id or None),
626 'whitelabel-tag': (M_PUBLIC, genconf_whitelabel_tag),
Jack Rosenthal543bb062020-06-05 15:30:09 -0600627 },
628 'power': (M_PUBLIC, genconf_powerd_settings),
Jack Rosenthal19b40eb2020-06-24 15:38:45 -0600629 'wallpaper': (M_PRIVATE, genconf_wallpaper_id),
Jack Rosenthal543bb062020-06-05 15:30:09 -0600630}
631
632
633def genconf(schema, device_conf, overlay_conf):
634
635 def qualifies_as_value(v):
636 return v is not None and v != {}
637
638 if isinstance(schema, dict):
639 pub, priv = {}, {}
640 for k, v in schema.items():
641 pub_r, priv_r = genconf(v, device_conf, overlay_conf)
642 if qualifies_as_value(pub_r):
643 pub[k] = pub_r
644 if qualifies_as_value(priv_r):
645 priv[k] = priv_r
646 return pub, priv
647
648 if isinstance(schema, tuple):
649 pub, priv = None, None
650 flags, func = schema
651 value = func(device_conf, overlay_conf)
652 if flags & M_PUBLIC:
653 pub = value
654 if flags & M_PRIVATE:
655 priv = value
656 return pub, priv
657
658
659def validate_gs_uri(uri):
660 log('Validating {}...'.format(uri))
661 subprocess.run(['gsutil', 'stat', uri], check=True, stdout=subprocess.DEVNULL)
662
663
664def parse_opts(argv):
665 parser = argparse.ArgumentParser()
666 parser.add_argument('--cros-checkout',
667 type=pathlib.Path,
668 default=pathlib.Path(os.getenv('HOME')) / 'trunk',
669 help='Location of the ChromeOS checkout')
670 parser.add_argument('--dut', '-d',
671 type=str,
672 required=True,
673 help='Hostname of DUT to use for querying and testing.')
674 parser.add_argument('--dut-ssh-port', type=int, default=22,
675 help='SSH port to use on the dut.')
676 parser.add_argument('--board', '-b',
677 type=str,
678 required=True,
679 help='Board name to convert.')
680 parser.add_argument('--mosys-platform', type=str, required=True)
681 parser.add_argument('--dry-run',
682 action='store_true',
683 default=False,
684 help='Dry run')
685 return parser.parse_args(argv)
686
687
688def main(argv):
689 opts = parse_opts(argv)
690
691 overlays = BoardOverlays(opts.board, opts.cros_checkout, opts.mosys_platform)
692 dut = Dut(opts.dut, opts.cros_checkout, port=opts.dut_ssh_port)
693
694 log('Loading configuration from DUT...')
695 dut_config = DeviceConfig.from_dut(dut)
696 log('Got configuration: {}'.format(dut_config))
697
698 assert dut_config.lsb_val('CHROMEOS_RELEASE_BOARD') == opts.board
699 assert dut_config.lsb_val('CHROMEOS_RELEASE_UNIBUILD', '0') != '1'
700
701 log('Generating chromeos-config values...')
702 public_config, private_config = genconf(genconf_schema, dut_config, overlays)
703
704 public_config_yaml = format_yaml(public_config)
705 private_config_yaml = format_yaml(private_config)
706 log('Got public config: \n{}'.format(public_config_yaml))
707 log('Got private config: \n{}'.format(private_config_yaml))
708
709 log('Generating ebuilds...')
710
711 public_vpackage = generate_vpackage(('chromeos-base/chromeos-config-bsp', ))
712 private_vpackage = generate_vpackage(
713 ('chromeos-base/chromeos-config-bsp',
714 'chromeos-base/chromeos-config-bsp-private'))
715 log('Got public vpackage: \n{}'.format(public_vpackage))
716 log('Got private vpackage: \n{}'.format(private_vpackage))
717
718 public_bsp_ebuild = generate_bsp_ebuild()
719 private_bsp_ebuild = generate_bsp_ebuild(private=True)
720 log('Got public bsp_ebuild: \n{}'.format(public_bsp_ebuild))
721 log('Got private bsp_ebuild: \n{}'.format(private_bsp_ebuild))
722
723 firmware_ebuild = generate_firmware_ebuild(opts.board)
724 log('Got firmware ebuild: \n{}'.format(firmware_ebuild))
725
726 public_make_defaults = generate_make_defaults(overlays.public_make_defaults)
727 log('Got public make defaults: \n{}'.format(public_make_defaults))
728 private_make_defaults = generate_make_defaults(overlays.private_make_defaults)
729 log('Got private make defaults: \n{}'.format(private_make_defaults))
730
731 cros_config = CrosConfig(public_config_yaml, private_config_yaml)
732 firmware_srcuris = cros_config.run_host_command('get-firmware-uris')
733 log('Got firmware URIs: {}'.format(firmware_srcuris))
734
735 log('Validating firmware srcuris...')
736 for uri in firmware_srcuris.split():
737 validate_gs_uri(uri)
738
739 firmware_srcuris_path = (overlays.firmware_ebuild_path.parent
740 / 'files' / 'srcuris')
741
742 if opts.dry_run:
743 return
744
745 overlays.write_file(
746 M_PUBLIC, 'chromeos-base/chromeos-config-bsp/files/model.yaml',
747 public_config_yaml)
748 overlays.write_file(
749 M_PRIVATE, 'chromeos-base/chromeos-config-bsp-private/files/model.yaml',
750 private_config_yaml)
751 overlays.write_file(
Jack Rosenthalbe2f1272020-06-10 09:31:09 -0600752 M_PUBLIC, 'virtual/chromeos-config-bsp/chromeos-config-bsp-2.ebuild',
753 public_vpackage, make_ebuild_symlink=True)
Jack Rosenthal543bb062020-06-05 15:30:09 -0600754 overlays.write_file(
Jack Rosenthalbe2f1272020-06-10 09:31:09 -0600755 M_PRIVATE, 'virtual/chromeos-config-bsp/chromeos-config-bsp-3.ebuild',
756 private_vpackage, make_ebuild_symlink=True)
Jack Rosenthal543bb062020-06-05 15:30:09 -0600757 overlays.write_file(
758 M_PUBLIC,
759 'chromeos-base/chromeos-config-bsp/chromeos-config-bsp-9999.ebuild',
760 public_bsp_ebuild)
761 overlays.write_file(
762 M_PRIVATE,
763 'chromeos-base/chromeos-config-bsp-private/chromeos-config-bsp-private-9999.ebuild',
764 private_bsp_ebuild)
765 write_file(overlays.firmware_ebuild_path, firmware_ebuild)
Andrew Lambbc1c80a2020-10-23 12:21:49 -0600766 write_file(firmware_srcuris_path, ''.join('{}\n'.format(uri) for uri in firmware_srcuris.split()))
Jack Rosenthal543bb062020-06-05 15:30:09 -0600767 write_file(overlays.public_make_defaults_file, public_make_defaults)
768 write_file(overlays.private_make_defaults_file, private_make_defaults)
769
770
771if __name__ == '__main__':
772 main(sys.argv[1:])