blob: e886558ba820ec3cbc9cb6edb2876baf1e09e40d [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
from collections import namedtuple
from enum import Enum
from xml.etree import ElementTree as ET
from .builder import BuilderOutput
from .gn import GnBuilder
Board = namedtuple('Board', ['target_cpu'])
App = namedtuple('App', ['name', 'source', 'outputs'])
Tool = namedtuple('Tool', ['name', 'source', 'outputs'])
TestDriver = namedtuple('TestDriver', ['name', 'source'])
class TizenBoard(Enum):
ARM = Board('arm')
class TizenApp(Enum):
ALL_CLUSTERS = App(
'chip-all-clusters-app',
'examples/all-clusters-app/tizen',
('chip-all-clusters-app',
'chip-all-clusters-app.map'))
LIGHT = App(
'chip-lighting-app',
'examples/lighting-app/tizen',
('chip-lighting-app',
'chip-lighting-app.map'))
CHIP_TOOL = Tool(
'chip-tool',
'examples/chip-tool',
('chip-tool',
'chip-tool.map'))
TESTS = TestDriver(
'tests',
'src/test_driver/tizen')
@property
def is_tpk(self):
"""If True, this app is a TPK."""
return isinstance(self.value, App)
@property
def package(self):
return f'{self.package_name}-{self.package_version}.tpk'
@property
def package_name(self):
return self.manifest.get('package')
@property
def package_version(self):
return self.manifest.get('version')
def parse_manifest(self, manifest: str):
self.manifest = ET.parse(manifest).getroot()
class TizenBuilder(GnBuilder):
def __init__(self,
root,
runner,
app: TizenApp = TizenApp.LIGHT,
board: TizenBoard = TizenBoard.ARM,
enable_ble: bool = True,
enable_thread: bool = True,
enable_wifi: bool = True,
use_asan: bool = False,
use_tsan: bool = False,
use_ubsan: bool = False,
use_coverage: bool = False,
with_ui: bool = False,
):
super(TizenBuilder, self).__init__(
root=os.path.join(root, app.value.source),
runner=runner)
self.app = app
self.board = board
self.extra_gn_options = []
if self.app.is_tpk:
try:
# Try to load Tizen application XML manifest. We have to use
# try/except here, because of TestBuilder test. This test runs
# in a fake build root /TEST/BUILD/ROOT which obviously does
# not have Tizen manifest file.
self.app.parse_manifest(
os.path.join(self.root, "tizen-manifest.xml"))
except FileNotFoundError:
pass
if app == TizenApp.TESTS:
self.extra_gn_options.append('chip_build_tests=true')
# Tizen test driver creates ISO image with all unit test files. So,
# it uses twice as much space as regular build. Due to CI storage
# limitations, we need to strip debug symbols from executables.
self.extra_gn_options.append('strip_symbols=true')
self.build_command = 'check'
if not enable_ble:
self.extra_gn_options.append('chip_config_network_layer_ble=false')
if not enable_thread:
self.extra_gn_options.append('chip_enable_openthread=false')
if not enable_wifi:
self.extra_gn_options.append('chip_enable_wifi=false')
if use_asan:
self.extra_gn_options.append('is_asan=true')
if use_tsan:
raise Exception("TSAN sanitizer not supported by Tizen toolchain")
if use_ubsan:
self.extra_gn_options.append('is_ubsan=true')
self.use_coverage = use_coverage
if use_coverage:
self.extra_gn_options.append('use_coverage=true')
if with_ui:
self.extra_gn_options.append('chip_examples_enable_ui=true')
def generate(self):
super(TizenBuilder, self).generate()
if self.app == TizenApp.TESTS and self.use_coverage:
self.coverage_dir = os.path.join(self.output_dir, 'coverage')
self._Execute(['mkdir', '-p', self.coverage_dir], title="Create coverage output location")
def lcov_args(self):
gcov = os.path.join(os.environ['TIZEN_SDK_TOOLCHAIN'], 'bin/arm-linux-gnueabi-gcov')
return [
'lcov', '--gcov-tool', gcov, '--ignore-errors', 'unused,mismatch', '--capture', '--directory', os.path.join(
self.output_dir, 'obj'),
'--exclude', '**/src/controller/*',
'--exclude', '**/connectedhomeip/zzz_generated/*',
'--exclude', '**/connectedhomeip/third_party/*',
'--exclude', '/opt/*',
]
def PreBuildCommand(self):
if self.app == TizenApp.TESTS and self.use_coverage:
cmd = ['ninja', '-C', self.output_dir]
if self.ninja_jobs is not None:
cmd.append('-j' + str(self.ninja_jobs))
cmd.append('Tizen')
self._Execute(cmd, title="Build-only")
self._Execute(self.lcov_args() + [
'--initial',
'--output-file', os.path.join(self.coverage_dir, 'lcov_base.info')
], title="Initial coverage baseline")
def PostBuildCommand(self):
if self.app == TizenApp.TESTS and self.use_coverage:
self._Execute(self.lcov_args() + ['--output-file', os.path.join(self.coverage_dir,
'lcov_test.info')], title="Update coverage")
gcov = os.path.join(os.environ['TIZEN_SDK_TOOLCHAIN'], 'bin/arm-linux-gnueabi-gcov')
self._Execute(['lcov', '--gcov-tool', gcov, '--add-tracefile', os.path.join(self.coverage_dir, 'lcov_base.info'),
'--add-tracefile', os.path.join(self.coverage_dir, 'lcov_test.info'),
'--output-file', os.path.join(self.coverage_dir, 'lcov_final.info')
], title="Final coverage info")
self._Execute(['genhtml', os.path.join(self.coverage_dir, 'lcov_final.info'), '--output-directory',
os.path.join(self.coverage_dir, 'html')], title="HTML coverage")
def GnBuildArgs(self):
# Make sure that required ENV variables are defined
for env in ('TIZEN_SDK_ROOT', 'TIZEN_SDK_SYSROOT'):
if env not in os.environ:
raise Exception(
"Environment %s missing, cannot build Tizen target" % env)
return self.extra_gn_options + [
'target_os="tizen"',
'target_cpu="%s"' % self.board.value.target_cpu,
'tizen_sdk_root="%s"' % os.environ['TIZEN_SDK_ROOT'],
'tizen_sdk_sysroot="%s"' % os.environ['TIZEN_SDK_SYSROOT'],
]
def _bundle(self):
if self.app.is_tpk:
logging.info('Packaging %s', self.output_dir)
cmd = ['ninja', '-C', self.output_dir, self.app.value.name + ':tpk']
self._Execute(cmd, title='Packaging ' + self.identifier)
def build_outputs(self):
for name in self.app.value.outputs:
if not self.options.enable_link_map_file and name.endswith(".map"):
continue
yield BuilderOutput(
os.path.join(self.output_dir, name),
name)
def bundle_outputs(self):
if not self.app.is_tpk:
return
source = os.path.join(self.output_dir, self.app.package_name,
'out', self.app.package)
yield BuilderOutput(source, self.app.package)