blob: dc420fce5e6b41cd19fb6180ab3568388fcfbad6 [file] [log] [blame]
# Copyright (c) 2021 Project CHIP Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
import os
import shutil
import tarfile
from abc import ABC, abstractmethod
from dataclasses import dataclass
@dataclass
class BuilderOptions:
# Enable flashbundle generation stage
enable_flashbundle: bool = False
# Allow to wrap default build command
pw_command_launcher: str = None
# Locations where files are pre-generated
pregen_dir: str = None
class Builder(ABC):
"""Generic builder base class for CHIP.
Provides ability to bootstrap and copy output artifacts and subclasses can
use a generic shell runner.
"""
def __init__(self, root, runner):
self.root = os.path.abspath(root)
self._runner = runner
# Set post-init once actual build target is known
self.identifier = None
self.output_dir = None
self.options = BuilderOptions()
@abstractmethod
def generate(self):
"""Generate the build files - generally the ninja/makefiles"""
raise NotImplementedError()
@abstractmethod
def _build(self):
"""Perform an actual build"""
raise NotImplementedError()
def _generate_flashbundle(self):
"""Perform an actual generating of flashbundle
May do nothing (and builder can choose not to implement this) if the
app does not need special steps for generating flashbundle. (e.g. the
example apps on Linux platform can run the ELF files directly.)
"""
pass
@abstractmethod
def build_outputs(self):
"""Return a list of relevant output files after a build.
May use build output data (e.g. manifests), so this should be invoked
only after a build has succeeded.
"""
raise NotImplementedError()
def flashbundle(self):
"""Return the files in flashbundle.
Return an empty dict (and builder can choose not to implement this) if the
app does not need special files as flashbundle. (e.g. the example apps on
Linux platform can run the ELF files directly.)
May use data from do_generate_flashbundle, so this should be invoked only
after do_generate_flashbundle has succeeded.
"""
return {}
def outputs(self):
artifacts = self.build_outputs()
if self.options.enable_flashbundle:
artifacts.update(self.flashbundle())
return artifacts
def build(self):
self._build()
if self.options.enable_flashbundle:
self._generate_flashbundle()
def _Execute(self, cmdarray, title=None):
self._runner.Run(cmdarray, title=title)
def CompressArtifacts(self, target_file: str):
with tarfile.open(target_file, "w:gz") as tar:
for target_name, source_name in self.outputs().items():
logging.info(
f'Adding {source_name} into {target_file}/{target_name}')
tar.add(source_name, target_name)
def CopyArtifacts(self, target_dir: str):
for target_name, source_name in self.outputs().items():
target_full_name = os.path.join(target_dir, target_name)
logging.info('Copying %s into %s', source_name, target_name)
target_dir_full_name = os.path.dirname(target_full_name)
if not os.path.exists(target_dir_full_name):
logging.info('Creating subdirectory %s first',
target_dir_full_name)
os.makedirs(target_dir_full_name)
shutil.copyfile(source_name, target_full_name)
shutil.copymode(source_name, target_full_name)