| #!/bin/bash |
| # Copyright (c) 2017 Linaro Limited |
| # Copyright (c) 2018 Intel Corporation |
| # |
| # SPDX-License-Identifier: Apache-2.0 |
| # |
| # Author: Anas Nashif |
| # |
| # This script is run in CI and support both pull request and commit modes. So |
| # it can be used also on commits to the tree or on tags. |
| # The following options are supports: |
| # -p start the script for pull requests |
| # -m matrix node number, for example 3 on a 5 node matrix |
| # -M total number of nodes in the matrix |
| # -b base branch |
| # -r the remote to rebase on |
| # |
| # The script can be run locally using for example: |
| # ./scripts/ci/run_ci.sh -b main -r origin -l -R <commit range> |
| |
| set -xe |
| |
| twister_options=" --inline-logs -M -N -v --integration" |
| |
| west_commands_results_file="./pytest_out/west_commands.xml" |
| |
| matrix_builds=1 |
| matrix=1 |
| |
| function handle_coverage() { |
| # Upload to codecov.io only on merged builds or if CODECOV_IO variable |
| # is set. |
| if [ -n "${CODECOV_IO}" -o -z "${pull_request_nr}" ]; then |
| # Capture data |
| echo "Running lcov --capture ..." |
| lcov --capture \ |
| --directory twister-out/native_posix/ \ |
| --directory twister-out/nrf52_bsim/ \ |
| --directory twister-out/unit_testing/ \ |
| --output-file lcov.pre.info -q --rc lcov_branch_coverage=1 |
| |
| # Remove noise |
| echo "Exclude data from coverage report..." |
| lcov -q \ |
| --remove lcov.pre.info mylib.c \ |
| --remove lcov.pre.info tests/\* \ |
| --remove lcov.pre.info samples/\* \ |
| --remove lcov.pre.info ext/\* \ |
| --remove lcov.pre.info *generated* \ |
| -o lcov.info --rc lcov_branch_coverage=1 |
| |
| # Cleanup |
| rm lcov.pre.info |
| rm -rf twister-out out-2nd-pass |
| |
| # Upload to codecov.io |
| echo "Upload coverage reports to codecov.io" |
| bash <(curl -s https://codecov.io/bash) -f "lcov.info" -X coveragepy -X fixes |
| rm -f lcov.info |
| fi |
| |
| rm -rf twister-out out-2nd-pass |
| |
| } |
| |
| function handle_compiler_cache() { |
| # Give more details in case we fail because of compiler cache |
| if [ -f "$HOME/.cache/zephyr/ToolchainCapabilityDatabase.cmake" ]; then |
| echo "Dumping the capability database in case we are affected by #9992" |
| cat $HOME/.cache/zephyr/ToolchainCapabilityDatabase.cmake |
| fi |
| } |
| |
| function on_complete() { |
| source zephyr-env.sh |
| if [ "$1" == "failure" ]; then |
| handle_compiler_cache |
| fi |
| |
| rm -rf ccache $HOME/.cache/zephyr |
| |
| if [ "$matrix" = "1" ]; then |
| echo "Skip handling coverage data..." |
| #handle_coverage |
| else |
| rm -rf twister-out out-2nd-pass |
| fi |
| } |
| |
| function build_test_file() { |
| # cleanup |
| rm -f test_file_boards.txt test_file_tests.txt test_file_archs.txt test_file_full.txt |
| touch test_file_boards.txt test_file_tests.txt test_file_archs.txt test_file_full.txt |
| |
| twister_exclude_tag_opt="" |
| |
| # In a pull-request see if we have changed any tests or board definitions |
| if [ -n "${pull_request_nr}" -o -n "${local_run}" ]; then |
| ./scripts/zephyr_module.py --twister-out module_tests.args |
| ./scripts/ci/get_twister_opt.py --commits ${commit_range} |
| |
| if [ -s modified_tags.args ]; then |
| twister_exclude_tag_opt="+modified_tags.args" |
| fi |
| |
| if [ -s modified_boards.args ]; then |
| ${twister} ${twister_options} ${twister_exclude_tag_opt} \ |
| +modified_boards.args \ |
| --save-tests test_file_boards.txt || exit 1 |
| fi |
| if [ -s modified_tests.args ]; then |
| ${twister} ${twister_options} ${twister_exclude_tag_opt} \ |
| +modified_tests.args \ |
| --save-tests test_file_tests.txt || exit 1 |
| fi |
| if [ -s modified_archs.args ]; then |
| ${twister} ${twister_options} ${twister_exclude_tag_opt} \ |
| +modified_archs.args \ |
| --save-tests test_file_archs.txt || exit 1 |
| fi |
| rm -f modified_tests.args modified_boards.args modified_archs.args |
| fi |
| |
| if [ "$SC" == "full" ]; then |
| # Save list of tests to be run |
| ${twister} ${twister_options} ${twister_exclude_tag_opt} \ |
| --save-tests test_file_full.txt || exit 1 |
| fi |
| |
| rm -f modified_tags.args |
| |
| # Remove headers from all files. We insert it into test_file.txt explicitly |
| # so we treat all test_file*.txt files the same. |
| tail -n +2 test_file_full.txt > test_file_full_in.txt |
| tail -n +2 test_file_archs.txt > test_file_archs_in.txt |
| tail -n +2 test_file_tests.txt > test_file_tests_in.txt |
| tail -n +2 test_file_boards.txt > test_file_boards_in.txt |
| |
| echo "test,arch,platform,status,extra_args,handler,handler_time,ram_size,rom_size" \ |
| > test_file.txt |
| cat test_file_full_in.txt test_file_archs_in.txt test_file_tests_in.txt \ |
| test_file_boards_in.txt >> test_file.txt |
| } |
| |
| function west_setup() { |
| # West handling |
| git_dir=$(basename $PWD) |
| pushd .. |
| if [ ! -d .west ]; then |
| west init -l ${git_dir} |
| west update 1> west.update.log || west update 1> west.update-2.log |
| west forall -c 'git reset --hard HEAD' |
| fi |
| popd |
| } |
| |
| |
| while getopts ":p:m:b:r:M:cfslR:" opt; do |
| case $opt in |
| c) |
| echo "Execute CI" >&2 |
| main_ci=1 |
| ;; |
| l) |
| echo "Executing script locally" >&2 |
| local_run=1 |
| main_ci=1 |
| ;; |
| s) |
| echo "Success" >&2 |
| success=1 |
| ;; |
| f) |
| echo "Failure" >&2 |
| failure=1 |
| ;; |
| p) |
| echo "Testing a Pull Request: $OPTARG." >&2 |
| pull_request_nr=$OPTARG |
| ;; |
| m) |
| echo "Running on Matrix $OPTARG" >&2 |
| matrix=$OPTARG |
| ;; |
| M) |
| echo "Running a matrix of $OPTARG slaves" >&2 |
| matrix_builds=$OPTARG |
| ;; |
| b) |
| echo "Base Branch: $OPTARG" >&2 |
| branch=$OPTARG |
| ;; |
| r) |
| echo "Remote: $OPTARG" >&2 |
| remote=$OPTARG |
| ;; |
| R) |
| echo "Range: $OPTARG" >&2 |
| range=$OPTARG |
| ;; |
| \?) |
| echo "Invalid option: -$OPTARG" >&2 |
| ;; |
| esac |
| done |
| |
| if [ -n "$main_ci" ]; then |
| |
| west_setup |
| |
| if [ -z "$branch" ]; then |
| echo "No base branch given" |
| exit 1 |
| else |
| commit_range=$remote/${branch}..HEAD |
| echo "Commit range:" ${commit_range} |
| fi |
| if [ -n "$range" ]; then |
| commit_range=$range |
| fi |
| source zephyr-env.sh |
| twister="${ZEPHYR_BASE}/scripts/twister" |
| |
| # Possibly the only record of what exact version is being tested: |
| short_git_log='git log -n 5 --oneline --decorate --abbrev=12 ' |
| |
| # check what files have changed for PRs or local runs. If we are |
| # building for a commit than we always do a "Full Run". |
| if [ -n "${pull_request_nr}" -o -n "${local_run}" ]; then |
| SC=`./scripts/ci/what_changed.py --commits ${commit_range}` |
| else |
| echo "Full Run" |
| SC="full" |
| fi |
| |
| if [ -n "$pull_request_nr" ]; then |
| $short_git_log $remote/${branch} |
| # Now let's pray this script is being run from a |
| # different location |
| # https://stackoverflow.com/questions/3398258/edit-shell-script-while-its-running |
| git rebase $remote/${branch} |
| fi |
| $short_git_log |
| |
| build_test_file |
| |
| echo "+++ run twister" |
| |
| # Run a subset of tests based on matrix size |
| ${twister} ${twister_options} --load-tests test_file.txt \ |
| --subset ${matrix}/${matrix_builds} --retry-failed 3 |
| |
| # Run module tests on matrix #1 |
| if [ "$matrix" = "1" -a "$SC" == "full" ]; then |
| if [ -s module_tests.args ]; then |
| ${twister} ${twister_options} \ |
| +module_tests.args --outdir module_tests |
| fi |
| fi |
| |
| # cleanup |
| rm -f test_file* |
| |
| elif [ -n "$failure" ]; then |
| on_complete failure |
| elif [ -n "$success" ]; then |
| on_complete |
| else |
| echo "Nothing to do" |
| fi |
| |