blob: 1df69063685ba2e0ccdf25152c6e3c598a17fe94 [file] [log] [blame]
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright 2020 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Join information from config_bundle, model.yaml and HWID.
Takes a generated config_bundle payload, optional public and private model.yaml
files, and an optional HWID database and merges the data together into a new
set of generated joined data.
Can optionally generate a new ConfigBundle from just the model.yaml and HWID
files. Simple specify a project name with --project-name/-p and omit
--config-bundle/-c. At least one of these two options must be specified.
"""
import argparse
import logging
import os
import re
import sys
import tempfile
import yaml
from common import config_bundle_utils
from checker import io_utils
from chromiumos.config.api import topology_pb2
from chromiumos.config.api.software import firmware_config_pb2
from chromiumos.config.payload import config_bundle_pb2
# HWID databases use some custom tags, which are mostly legacy as far as I can
# tell, so we'll ignore them explicitly to allow the parser to succeed.
yaml.add_constructor('!re', lambda loader, node: loader.construct_scalar(node))
yaml.add_constructor('!region_field', lambda loader, node: None)
yaml.add_constructor('!region_component', lambda loader, node: None)
# git repo locations
CROS_PLATFORM_REPO = 'https://chromium.googlesource.com/chromiumos/platform2'
def load_models(public_path, private_path):
"""Load model.yaml from a public and/or private path."""
# Have to import this here since we need repos cloned and sys.path set up
# pylint: disable=import-outside-toplevel, import-error
from cros_config_host import cros_config_schema
from libcros_config_host import CrosConfig
# pylint: enable=import-outside-toplevel, import-error
if not (public_path or private_path):
return None
configs = [config for config in [public_path, private_path] if config]
with tempfile.TemporaryDirectory() as temp_dir:
# Convert the model.yaml files into a payload JSON
config_file = os.path.join(temp_dir, 'config.json')
cros_config_schema.Main(
schema=None, config=None, output=config_file, configs=configs)
# And load the payload json into a CrosConfigJson object
return CrosConfig(config_file)
def load_hwid(hwid_path):
"""Load a HWID database from the given path."""
with open(hwid_path) as infile:
return yaml.load(infile, Loader=yaml.FullLoader)
def non_null_values(items):
"""Unwrap a HWID item block into a dictionary of key => values for non-null values.
a HWID item block looks like:
items:
storage_device:
status: unsupported
values:
class: '0x010101'
device: '0xff00'
sectors: '5000000'
vendor: '0xbeef'
some_hardware:
values:
FAKE_RAM_CHIP:
values:
class: '0x010101'
device: '0xff00'
sectors: '250000000'
vendor: '0xabcd'
We'll iterate over this and break out the 'values' block, make sure it's not None,
and check whether we should exclude it based on the 'status' field if present."""
def _include(val):
if not val['values']:
return False
if 'status' in val and val['status'].lower() == 'unsupported':
return False
return True
return [(key, val['values']) for key, val in items.items() if _include(val)]
def add_hwid_components(config_bundle, hwid_db):
"""Add components from the HWID database to the config_bundle.
HWID doesn't map hardware to SKU, it's more a listing of all possible
hardware components, which is resolved at runtime to generate an actual
accounting of what hardware is on a specific device. So we'll add the
components under the ConfigBundle, but not actually tie them together
into design configs (yet).
Args:
config_bundle (ConfigBundle): config to add HWID components to
hwid_db (dict): parsed HWID database
Returns:
A reference to the input config_bundle updated with components from HWID
"""
# pylint: disable=too-many-statements
# pylint: disable=too-many-locals
def create_audio_components(items):
for key, values in non_null_values(items):
comp = config_bundle.components.add()
comp.id.value = key
comp.name = values.get('name', '')
comp.audio_codec.name = comp.name
def create_battery_components(items):
for key, values in non_null_values(items):
comp = config_bundle.components.add()
comp.id.value = key
comp.name = comp.id.value
if 'manufacturer' in values:
comp.manufacturer_id.MergeFrom(
config_bundle_utils.find_partner(
config_bundle, values['manufacturer'], create=True).id)
comp.battery.model = values.get('model_name', '')
if 'technology' in values and values['technology'].lower() == 'li-ion':
comp.battery.technology = comp.battery.LI_ION
def create_bluetooth_components(items):
for key, values in non_null_values(items):
comp = config_bundle.components.add()
comp.id.value = key
comp.name = comp.id.value
comp.bluetooth.usb.vendor_id = values.get('idVendor', '')
comp.bluetooth.usb.product_id = values.get('idProduct', '')
comp.bluetooth.usb.bcd_device = values.get('bcdDevice', '')
def create_cpu_components(items):
model_re = re.compile( # reversed from HWID cpu model values
'(a[0-9]?-[0-9]+[a-z]?|(m3-|i3-|i5-|i7-)*[0-9y]{4,5}[uy]?|n[0-9]{4})')
for key, values in non_null_values(items):
comp = config_bundle.components.add()
comp.id.value = key
comp.soc.model = values.get('model', '')
comp.soc.cores = int(values.get('cores', 0))
if 'model' in values:
model_string = values['model'].lower()
if 'intel' in model_string or 'amd' in model_string:
comp.soc.family.arch = comp.soc.X86_64
elif 'aarch64' in model_string or 'armv8' in model_string:
comp.soc.family.arch = comp.soc.ARM64
elif 'armv7' in model_string:
comp.soc.family.arch = comp.soc.ARM
else:
logging.warning('unknown family for cpu model \'%s\'', model_string)
match = model_re.search(model_string)
if match:
comp.soc.family.name = match.group(0).upper()
def create_display_components(items):
for key, values in non_null_values(items):
comp = config_bundle.components.add()
comp.id.value = key
if 'vendor' in values:
comp.manufacturer_id.MergeFrom(
config_bundle_utils.find_partner(
config_bundle, values['vendor'], create=True).id)
comp.display_panel.product_id = values.get('product_id', '')
comp.display_panel.properties.width_px = int(values.get('width', 0))
comp.display_panel.properties.height_px = int(values.get('height', 0))
def create_dram_components(items):
# There's a lot of duplicated part numbers in the HWID db, mostly
# due to specifying slot number. That information is largely incorrect
# anyways, so we'll deduplicate parts here
part_values = {}
for _, values in non_null_values(items):
part_values[values['part']] = (int(values['size']), values['timing'])
for part_number, (size, timing) in part_values.items():
comp = config_bundle.components.add()
comp.id.value = part_number
comp.memory.part_number = part_number
comp.memory.profile.size_megabytes = int(size)
memory_type = timing.split('-')[0]
if memory_type == 'LPDDR4':
comp.memory.profile.type = comp.memory.LP_DDR4
elif memory_type == 'LPDDR3':
comp.memory.profile.type = comp.memory.LP_DDR3
elif memory_type == 'DDR4':
comp.memory.profile.type = comp.memory.DDR4
elif memory_type == 'DDR3':
comp.memory.profile.type = comp.memory.DDR3
elif memory_type == 'DDR2':
comp.memory.profile.type = comp.memory.DDR2
elif memory_type == 'DDR':
comp.memory.profile.type = comp.memory.DDR
def create_ec_flash_components(items):
for _, values in non_null_values(items):
part_number = values.get('name', '')
comp = config_bundle.components.add()
comp.id.value = part_number
if 'vendor' in values:
comp.manufacturer_id.MergeFrom(
config_bundle_utils.find_partner(
config_bundle, values['vendor'], create=True).id)
comp.ec_flash_chip.part_number = part_number
def create_flash_components(items):
for _, values in non_null_values(items):
part_number = values.get('name', '')
comp = config_bundle.components.add()
comp.id.value = part_number
if 'vendor' in values:
comp.manufacturer_id.MergeFrom(
config_bundle_utils.find_partner(
config_bundle, values['vendor'], create=True).id)
comp.system_flash_chip.part_number = part_number
def create_ec_components(items):
for _, values in non_null_values(items):
part_number = values.get('name', '')
comp = config_bundle.components.add()
comp.id.value = part_number
comp.name = part_number
if 'vendor' in values:
comp.manufacturer_id.MergeFrom(
config_bundle_utils.find_partner(
config_bundle, values['vendor'], create=True).id)
comp.ec.part_number = part_number
def create_storage_components(items):
for key, values in non_null_values(items):
comp = config_bundle.components.add()
comp.id.value = key
comp.name = key
comp.storage.emmc5_fw_ver = values.get('emmc5_fw_ver', '')
comp.storage.manfid = values.get('manfid', '')
comp.storage.name = values.get('name', '')
comp.storage.oemid = values.get('oemid', '')
comp.storage.prv = values.get('prv', '')
comp.storage.sectors = values.get('sectors', '')
if 'type' in values:
storage_type = values['type'].lower()
if storage_type == 'mmc':
comp.storage.type = comp.storage.EMMC
def create_touchpad_components(items):
for _, values in non_null_values(items):
comp = config_bundle.components.add()
comp.name = values.get('name', '')
# Check for USB based touchpad
# We don't receive an explicit type for the touchpad bus type, so
# we assume that if we have a product and vendor id, that it's USB,
# otherwise it's I2C (rare)
if 'product' in values and 'vendor' in values:
comp.touchpad.type = comp.touchpad.USB
comp.touchpad.product_id = comp.name
comp.touchpad.usb.vendor_id = values['vendor']
comp.touchpad.usb.product_id = values['product']
elif 'fw_version' in values and 'fw_csum' in values:
# i2c based touchpad
comp.touchpad.type = comp.touchpad.I2C
comp.touchpad.product_id = values.get('product_id', '')
comp.touchpad.fw_version = values['fw_version']
comp.touchpad.fw_checksum = values['fw_csum']
def create_tpm_components(items):
for key, values in non_null_values(items):
comp = config_bundle.components.add()
comp.name = key
comp.tpm.manufacturer_info = values.get('manufacturer_info', '')
comp.tpm.version = values.get('version', '')
def create_usb_host_components(items):
def get_oneof(obj, keys, default=None):
"""Get one of a set of keys from a dict, or return a default value."""
for key in keys:
if key in obj:
return obj[key]
return default
for key, values in non_null_values(items):
comp = config_bundle.components.add()
comp.name = values.get('product', key)
if 'manufacturer' in values:
comp.manufacturer_id.MergeFrom(
config_bundle_utils.find_partner(
config_bundle, values['manufacturer'], create=True).id)
host = comp.usb_host
host.product_id = get_oneof(values, ['idProduct', 'device'], '')
host.vendor_id = get_oneof(values, ['idVendor', 'vendor'], '')
host.bcd_device = get_oneof(values, ['bcdDevice', 'revision_id'], '')
def create_video_components(items):
for key, values in non_null_values(items):
comp = config_bundle.components.add()
if values.get('bus_type') == 'usb':
comp.id.value = key
comp.name = values['product']
if 'manufacturer' in values:
comp.manufacturer_id.MergeFrom(
config_bundle_utils.find_partner(
config_bundle, values['manufacturer'], create=True).id)
comp.camera.usb.vendor_id = values.get('idVendor', '')
comp.camera.usb.product_id = values.get('idProduct', '')
comp.camera.usb.bcd_device = values.get('bcdDevice', '')
if values.get('bus_type') == 'pci':
comp.id.value = key
comp.name = key
comp.camera.pci.vendor_id = values.get('vendor', '')
comp.camera.pci.device_id = values.get('device', '')
comp.camera.pci.revision_id = values.get('revision_id', '')
def create_wireless_components(items):
for key, values in non_null_values(items):
comp = config_bundle.components.add()
comp.id.value = key
comp.name = key
comp.wifi.pci.vendor_id = values.get('vendor', '')
comp.wifi.pci.device_id = values.get('device', '')
comp.wifi.pci.revision_id = values.get('revision_id', '')
components = hwid_db['components']
for component_type, value in components.items():
if value:
{
'audio_codec': create_audio_components,
'battery': create_battery_components,
'bluetooth': create_bluetooth_components,
'cpu': create_cpu_components,
'display_panel': create_display_components,
'dram': create_dram_components,
'ec_flash_chip': create_ec_flash_components,
'embedded_controller': create_ec_components,
# 'firmware_keys': create_fw_key_components,
'flash_chip': create_flash_components,
# 'mainboard': create_mainboard_components,
# 'region': create_region_components,
# 'ro_ec_firmware': create_ro_ec_fw_components,
# 'ro_main_firmware': create_ro_main_components,
'storage': create_storage_components,
'touchpad': create_touchpad_components,
'tpm': create_tpm_components,
'usb_hosts': create_usb_host_components,
'video': create_video_components,
'wireless': create_wireless_components
}.get(component_type, (lambda x: None))(
value['items'])
# TODO(smcallis): implement
return config_bundle
def merge_build_target(build_target, model):
"""Merge build configuration from model.yaml into the given build target.
Args:
build_target (BuildTarget): build target to modify
model (CrosConfig): parsed model.yaml information
Returns:
None
"""
build_props = model.GetProperties('/arc/build-properties')
build_target.arc.device = build_props['device']
build_target.arc.first_api_level = build_props['first-api-level']
def merge_audio_config(sw_config, model):
"""Merge audio configuration from model.yaml into the given sw_config.
Args:
sw_config (SoftwareConfig): software config to update
model (CrosConfig): parsed model.yaml information
Returns:
None
"""
audio_props = model.GetProperties('/audio/main')
audio_config = sw_config.audio_configs.add()
audio_config.ucm_suffix = audio_props.get('ucm-suffix', '')
def merge_power_config(sw_config, model):
"""Merge power configuration from model.yaml into the given sw_config.
Args:
sw_config (SoftwareConfig): software config to update
model (CrosConfig): parsed model.yaml information
Returns:
None
"""
power_props = model.GetProperties('/power')
power_config = sw_config.power_config
for key, val in power_props.items():
# We don't support autobrightness yet
if key == 'autobrightness':
continue
power_config.preferences[key] = val
def merge_bluetooth_config(sw_config, model):
"""Merge bluetooth configuration from model.yaml into the given sw_config.
Args:
sw_config (SoftwareConfig): software config to update
model (CrosConfig): parsed model.yaml information
Returns:
None
"""
bt_props = model.GetProperties('/bluetooth')
bt_config = sw_config.bluetooth_config
for key, val in bt_props.get('flags', {}).items():
bt_config.flags[key] = val
def merge_firmware_config(sw_config, model):
"""Merge firmware configuration from model.yaml into the given sw_config.
Args:
sw_config (SoftwareConfig): software config to update
model (CrosConfig): parsed model.yaml information
Returns:
None
"""
fw_props = model.GetProperties('/firmware')
# Populate firmware config
fw_config = sw_config.firmware
fw_config.main_ro_payload.type = firmware_config_pb2.FirmwareType.MAIN
fw_config.main_ro_payload.firmware_image_name = \
fw_props.get('main-ro-image', '')
fw_config.main_rw_payload.type = firmware_config_pb2.FirmwareType.MAIN
fw_config.main_rw_payload.firmware_image_name = \
fw_props.get('main-rw-image', '')
fw_config.ec_ro_payload.type = firmware_config_pb2.FirmwareType.EC
fw_config.ec_ro_payload.firmware_image_name = \
fw_props.get('ec-ro-image', '')
fw_config.pd_ro_payload.type = firmware_config_pb2.FirmwareType.PD
fw_config.pd_ro_payload.firmware_image_name = \
fw_props.get('pd-ro-image', '')
# Populate build config
build_props = model.GetProperties('/firmware/build-targets')
build_config = sw_config.firmware_build_config
build_config.build_targets.coreboot = build_props.get('coreboot', '')
build_config.build_targets.depthcharge = build_props.get('depthcharge', '')
build_config.build_targets.ec = build_props.get('ec', '')
build_config.build_targets.libpayload = build_props.get('libpayload', '')
for extra in build_props.get('ec-extras', []):
build_config.build_targets.ec_extras.add(extra)
def merge_camera_config(hw_feat, model):
"""Merge camera config from model.yaml into the given hardware features.
Args:
hw_feat (HardwareFeatures): hardware features to update
model (CrosConfig): parsed model.yaml information
Returns:
None
"""
camera_props = model.GetProperties('/camera')
hw_feat.camera.count.value = camera_props.get('count', 0)
def merge_buttons(hw_feat, model):
"""Merge power/volume button information from model.yaml into hardware features.
Args:
hw_feat (HardwareFeatures): hardware features to update
model (CrosConfig): parsed model.yaml information
Returns:
None
"""
ui_props = model.GetProperties('/ui')
button = topology_pb2.HardwareFeatures.Button
if 'power-button' in ui_props:
edge = ui_props['power-button']['edge']
hw_feat.power_button.edge = button.Edge.Value(edge.upper())
hw_feat.power_button.position = float(ui_props['power-button']['position'])
if 'side-volume-button' in ui_props:
region = ui_props['side-volume-button']['region']
hw_feat.volume_button.region = button.Region.Value(region.upper())
side = ui_props['side-volume-button']['side']
hw_feat.volume_button.edge = button.Edge.Value(side.upper())
def merge_hardware_props(hw_feat, model):
"""Merge hardware properties from model.yaml into the given hardware features.
Args:
hw_feat (HardwareFeatures): hardware features to update
model (CrosConfig): parsed model.yaml information
Returns:
None
"""
form_factor = topology_pb2.HardwareFeatures.FormFactor
stylus = topology_pb2.HardwareFeatures.Stylus
def kw_to_present(config, key):
if not key in config:
return topology_pb2.HardwareFeatures.PRESENT_UNKNOWN
if config[key]:
return topology_pb2.HardwareFeatures.PRESENT
return topology_pb2.HardwareFeatures.NOT_PRESENT
hw_props = model.GetProperties('/hardware-properties')
hw_feat.accelerometer.base_accelerometer = \
kw_to_present(hw_props, 'has-base-accelerometer')
hw_feat.accelerometer.lid_accelerometer = \
kw_to_present(hw_props, 'has-lid-accelerometer')
hw_feat.gyroscope.base_gyroscope = \
kw_to_present(hw_props, 'has-base-gyroscope')
hw_feat.gyroscope.lid_gyroscope = \
kw_to_present(hw_props, 'has-lid-gyroscope')
hw_feat.light_sensor.base_lightsensor = \
kw_to_present(hw_props, 'has-base-light-sensor')
hw_feat.light_sensor.lid_lightsensor = \
kw_to_present(hw_props, 'has-lid-light-sensor')
hw_feat.magnetometer.base_magnetometer = \
kw_to_present(hw_props, 'has-base-magnetometer')
hw_feat.magnetometer.lid_magnetometer = \
kw_to_present(hw_props, 'has-lid-magnetometer')
hw_feat.screen.touch_support = \
kw_to_present(hw_props, 'has-touchscreen')
hw_feat.form_factor.form_factor = form_factor.FORM_FACTOR_UNKNOWN
if hw_props.get('is-lid-convertible', False):
hw_feat.form_factor.form_factor = form_factor.CONVERTIBLE
stylus_val = hw_props.get('stylus-category', '')
if not stylus_val:
hw_feat.stylus.stylus = stylus.STYLUS_UNKNOWN
if stylus_val == 'none':
hw_feat.stylus.stylus = stylus.NONE
if stylus_val == 'internal':
hw_feat.stylus.stylus = stylus.INTERNAL
if stylus_val == 'external':
hw_feat.stylus.stylus = stylus.EXTERNAL
def merge_fingerprint_config(hw_feat, model):
"""Merge fingerprint config from model.yaml into the given hardware features.
Args:
hw_feat (HardwareFeatures): hardware features to update
model (CrosConfig): parsed model.yaml information
Returns:
None
"""
location = topology_pb2.HardwareFeatures.Fingerprint.Location
fing_prop = model.GetProperties('/fingerprint')
hw_feat.fingerprint.board = fing_prop.get('board', '')
sensor_location = fing_prop.get('sensor-location', 'none')
if sensor_location == 'none':
sensor_location = 'not-present'
hw_feat.fingerprint.location = location.Value(sensor_location.upper().replace(
'-', '_'))
def merge_model(config_bundle, design_config, model, project_name):
"""Merge model from model.yaml into a specific Design.Config instance.
The ConfigBundle, and Design.Config are updated in place with
model.yaml information.
Args:
config_bundle (ConfigBundle): top level ConfigBundle to update
design_config (Design.Config): design config in the config bundle to update
model (CrosConfig): parsed model.yaml information
project_name (str): name of the device (eg: phaser)
Returns:
A reference to the input config_bundle updated with data from model
"""
identity = model.GetProperties('/identity')
# Merge build target configuration
build_target = config_bundle.build_targets.add()
build_target.id.value = project_name
merge_build_target(build_target, model)
# Merge hardware configuration
hw_feat = design_config.hardware_features
merge_fingerprint_config(hw_feat, model)
merge_hardware_props(hw_feat, model)
merge_camera_config(hw_feat, model)
merge_buttons(hw_feat, model)
# Merge software configuration
sw_config = config_bundle.software_configs.add()
sw_config.design_config_id.MergeFrom(design_config.id)
sw_config.id_scan_config.firmware_sku = identity['sku-id']
if 'smbios-name-match' in identity:
sw_config.id_scan_config.smbios_name_match = identity['smbios-name-match']
if 'device-tree-compatible-match' in identity:
sw_config.id_scan_config.device_tree_compatible_match = \
identity['device-tree-compatible-match']
merge_firmware_config(sw_config, model)
merge_bluetooth_config(sw_config, model)
merge_power_config(sw_config, model)
merge_audio_config(sw_config, model)
return config_bundle
def merge_configs(config_path, project_name, public_path, private_path,
hwid_path):
# pylint: disable=too-many-locals
# pylint: disable=too-many-branches
# pylint: disable=too-many-statements
"""Read and merge configs together, generating new config_bundle output."""
config_bundle = config_bundle_pb2.ConfigBundle()
if config_path:
config_bundle = io_utils.read_config(config_path)
models = load_models(public_path, private_path)
def find_design_config(prog_name, proj_name, sku):
"""Searches config_bundle a matching design_config.
Args:
prog_name (str): program name
proj_name (str): project name
sku (str): specific sku
Returns:
Either found Design and Design.Config for input parameters or new ones
create and placed in the config_bundle.
"""
# Ensure program exists
program = config_bundle_utils.find_program(
config_bundle, prog_name, create=True)
# Find design matching program and project names
program_design = None
for design in config_bundle.design_list:
if program.id != design.program_id:
continue
program_design = design
if proj_name.lower() != design.name.lower():
continue
# Found matching design, iterate design configs looking for SKU
for design_config in design.configs:
design_sku = design_config.id.value.lower().split(':')[-1]
if design_sku == sku:
return design, design_config
# No Design found, create one
if not program_design:
program_design = config_bundle.design_list.add()
program_design.id.value = proj_name
program_design.name = proj_name
program_design.program_id.MergeFrom(program.id)
# Create new Design.Config, the board id is encoded according to CBI:
# http://go/chromiumsrc/chromiumos/docs/+/master/design_docs/cros_board_info.md
design_config = program_design.configs.add()
design_config.id.value = '{}:{}'.format(proj_name.capitalize(), sku)
return program_design, design_config
# The primary source of SKU truth is the model.yaml files. We'll take each
# sku we find there and attempt to match with a DesignConfig in the
# ConfigBundles, and update it if we find it, otherwise creating a new one.
for model in models.GetDeviceConfigs() if models else []:
identity = model.GetProperties('/identity')
program = identity['platform-name']
project = model.GetName()
whitelabel = identity.get('whitelabel-tag', '').lower()
sku = str(identity.get('sku-id', 'any')).lower()
if sku == 'any':
logging.info('skipping wildcard sku in %s', project)
continue
if sku == '255':
logging.info('skipping unprovisioned sku %s', sku)
continue
assert program, 'program name is undefined'
assert project, 'project name is undefined'
# Ignore projects other than the one specified
if project_name and (project != project_name.lower()):
continue
# Lookup design config for this specific device
design, design_config = find_design_config(program, project, sku)
# If we have a whitelabel tag, then just create a new DeviceBrand instead of
# actually updating the config, since that will be handled by the non white
# label variant
if whitelabel:
# Find brand for whitelabel if it exists
brand = None
for val in config_bundle.device_brand_list:
if val.brand_name == whitelabel:
brand = val
break
if not brand:
brand = config_bundle.device_brand_list.add()
brand.design_id.MergeFrom(design.id)
brand.id.value = whitelabel
brand.brand_name = whitelabel
brand.brand_code = model.GetProperties('/brand-code')
# And a new BrandConfig to hold the whitelabel tag and wallpaper
brand_config = None
for val in config_bundle.brand_configs:
if val.scan_config.whitelabel_tag == whitelabel:
brand_config = val
break
if not brand_config:
brand_config = config_bundle.brand_configs.add()
brand_config.brand_id.MergeFrom(brand.id)
brand_config.scan_config.whitelabel_tag = whitelabel
wallpaper = model.GetWallpaperFiles()
if wallpaper:
brand_config.wallpaper = wallpaper.pop()
else:
merge_model(config_bundle, design_config, model, project_name)
# Merge information from HWID into config bundle
if hwid_path:
return add_hwid_components(config_bundle, load_hwid(hwid_path))
return config_bundle
def main(options):
"""Runs the script."""
def clone_repo(repo, path):
"""Clone a given repo to the given path in the file system."""
cmd = 'git clone -q --depth=1 --shallow-submodules {repo} {path}'.format(
repo=repo, path=path)
print('Creating shallow clone of {repo} ({cmd})'.format(repo=repo, cmd=cmd))
os.system(cmd)
if not (options.config_bundle or options.project_name):
raise RuntimeError(
'At least one of {config_opt} or {project_opt} must be specified.'
.format(
config_opt='--config-bundle/-c', project_opt='--project-name/-p'))
with tempfile.TemporaryDirectory(prefix='join_proto_') as temppath:
clone_repo(CROS_PLATFORM_REPO, os.path.join(temppath, 'platform2'))
# setup sys.path so we can import from the cloned repos
sys.path.append(os.path.join(temppath, 'platform2', 'chromeos-config'))
io_utils.write_message_json(
merge_configs(options.config_bundle, options.project_name,
options.public_model, options.private_model,
options.hwid),
options.output,
default_fields=True)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
'-o',
'--output',
type=str,
required=True,
help='output file to write joined ConfigBundle jsonproto to')
parser.add_argument(
'-c',
'--config-bundle',
type=str,
help="""generated config_bundle payload in jsonpb format
(eg: generated/config.jsonproto). If not specified, an empty ConfigBundle
instance is used instad.""")
parser.add_argument(
'-p',
'--project-name',
type=str,
help="""When specified without --config-bundle/-c, this species the project name to
generate ConfigBundle information for from the model.yaml/HWID files. When
specified with --config-bundle/-c, then only projects with this name will be
updated.""")
parser.add_argument(
'--public-model', type=str, help='public model.yaml file to merge')
parser.add_argument(
'--private-model', type=str, help='private model.yaml file to merge')
parser.add_argument('--hwid', type=str, help='HWID database to merge')
main(parser.parse_args(sys.argv[1:]))