1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/dist/tools/buildsystem_sanity_check/check.sh
Marian Buschsieweke 17dcb97571
dist/tools/buildsystem_sanity_check: check pinned docker version
This tests if the latest manifest on dockerhub matches the pinned
version. The idea is that PRs are not merged until the pinning is
fixed, so that we can ensure that `make BUILD_IN_DOCKER=1` will
always succeed with the pinned version.
2024-06-08 02:12:15 +02:00

438 lines
16 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Copyright (C) 2018 Gaëtan Harter <gaetan.harter@fu-berlin.de>
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
#
#
# Central test script to have sanity checks for the build system
# It is run unconditionally on all files.
#
#
: "${RIOTBASE:="$(cd "$(dirname "$0")/../../../" || exit; pwd)"}"
: "${RIOTTOOLS:=${RIOTBASE}/dist/tools}"
: "${RIOTMAKE:=${RIOTBASE}/makefiles}"
# not running shellcheck with -x in the CI --> disable SC1091
# shellcheck disable=SC1091
. "${RIOTTOOLS}"/ci/github_annotate.sh
SCRIPT_PATH=dist/tools/buildsystem_sanity_check/check.sh
error_with_message() {
while read -r INPUT; do
MESSAGE="${1}"
if github_annotate_is_on; then
FILE=$(echo "${INPUT}" | cut -d: -f1)
LINE=$(echo "${INPUT}" | cut -d: -f2)
MATCH=$(echo "${INPUT}" | cut -d: -f3)
if [[ ! $LINE =~ ^[0-9]+$ ]] || [ -z "$MATCH" ]; then
# line is not provided in grep pattern
LINE=0
fi
github_annotate_error "$FILE" "$LINE" "$MESSAGE"
fi
printf "%s:\n\t%s\n\n" "${MESSAGE}" "${INPUT}"
done
}
# Modules should not check the content of FEATURES_PROVIDED/_REQUIRED/OPTIONAL
# Handling specific behaviors/dependencies should by checking the content of:
# * `USEMODULE`
# * maybe `FEATURES_USED` if it is not a module (== not a periph_)
check_not_parsing_features() {
local patterns=()
local pathspec=()
patterns+=(-e 'if.*filter.*FEATURES_PROVIDED')
patterns+=(-e 'if.*filter.*FEATURES_REQUIRED')
patterns+=(-e 'if.*filter.*FEATURES_OPTIONAL')
# Pathspec with exclude should start by an inclusive pathspec in git 2.7.4
pathspec+=('*')
# Ignore this file when matching as it self matches
pathspec+=(":!${SCRIPT_PATH}")
# These two files contain sanity checks using FEATURES_ so are allowed
pathspec+=(':!Makefile.include' ':!makefiles/info-global.inc.mk')
# We extend FEATURES_PROVIDED in Makefile.features based on what is
# already provided to avoid clutter in each boards Makefile.features.
# E.g. `periph_eth` will pull in `netif_ethernet`, which
# will pull in `netif`.
pathspec+=(':!Makefile.features')
git -C "${RIOTBASE}" grep -n "${patterns[@]}" -- "${pathspec[@]}" \
| error_with_message 'Modules should not check the content of FEATURES_PROVIDED/REQUIRED/OPTIONAL'
}
# Providing features for boards and CPUs should only be done in
# Makefile.features
check_providing_features_only_makefile_features() {
local patterns=()
local pathspec=()
patterns+=(-e '^[ ]*FEATURES_PROVIDED *+= *')
pathspec+=("*Makefile\.*")
pathspec+=(":!*Makefile.features")
git -C "${RIOTBASE}" grep -n "${patterns[@]}" -- "${pathspec[@]}" \
| error_with_message 'Features should only be provided in Makefile.features files'
}
# Some variables do not need to be exported and even cause issues when being
# exported because they are evaluated even when not needed.
#
# Currently this blacklists exported variables instead of whitelisting or
# providing a mechanism for handling it.
# It just keep things not exported anymore in the future.
UNEXPORTED_VARIABLES=()
UNEXPORTED_VARIABLES+=('FLASHFILE')
UNEXPORTED_VARIABLES+=('TERMPROG' 'TERMFLAGS')
UNEXPORTED_VARIABLES+=('FLASHER' 'FFLAGS')
UNEXPORTED_VARIABLES+=('RESET' 'RESETFLAGS')
UNEXPORTED_VARIABLES+=('DEBUGGER' 'DEBUGGER_FLAGS')
UNEXPORTED_VARIABLES+=('DEBUGSERVER' 'DEBUGSERVER_FLAGS')
UNEXPORTED_VARIABLES+=('DEBUGCLIENT' 'DEBUGCLIENT_FLAGS')
UNEXPORTED_VARIABLES+=('PREFLASHER' 'PREFFLAGS' 'FLASHDEPS')
UNEXPORTED_VARIABLES+=('OPENOCD_DEBUG_ADAPTER' 'DEBUG_ADAPTER_ID')
UNEXPORTED_VARIABLES+=('PROGRAMMER_SERIAL')
UNEXPORTED_VARIABLES+=('STLINK_VERSION')
UNEXPORTED_VARIABLES+=('PORT_LINUX' 'PORT_DARWIN')
UNEXPORTED_VARIABLES+=('PORT[ ?=:]' 'PORT$')
UNEXPORTED_VARIABLES+=('LINKFLAGS' 'LINKER_SCRIPT')
UNEXPORTED_VARIABLES+=('USEMODULE_INCLUDES')
UNEXPORTED_VARIABLES+=('OPENOCD_ADAPTER_INIT')
UNEXPORTED_VARIABLES+=('OPENOCD_CONFIG')
UNEXPORTED_VARIABLES+=('OPENOCD_RESET_USE_CONNECT_ASSERT_SRST')
UNEXPORTED_VARIABLES+=('OPENOCD_CMD_RESET_RUN')
UNEXPORTED_VARIABLES+=('OPENOCD_PRE_FLASH_CMDS' 'OPENOCD_PRE_VERIFY_CMDS')
UNEXPORTED_VARIABLES+=('OPENOCD_PRE_FLASH_CHECK_SCRIPT')
UNEXPORTED_VARIABLES+=('PYOCD_FLASH_TARGET_TYPE')
UNEXPORTED_VARIABLES+=('PYOCD_ADAPTER_INIT')
UNEXPORTED_VARIABLES+=('JLINK_DEVICE' 'JLINK_IF')
UNEXPORTED_VARIABLES+=('JLINK_PRE_FLASH' 'JLINK_RESET_FILE')
UNEXPORTED_VARIABLES+=('GIT_CACHE' 'GIT_CACHE_DIR')
UNEXPORTED_VARIABLES+=('LINKXX')
UNEXPORTED_VARIABLES+=('APPDEPS' 'BUILDDEPS' 'DEBUGDEPS')
UNEXPORTED_VARIABLES+=('EMULATOR' 'EMULATOR_FLAGS')
EXPORTED_VARIABLES_ONLY_IN_VARS=()
EXPORTED_VARIABLES_ONLY_IN_VARS+=('CPU_ARCH')
EXPORTED_VARIABLES_ONLY_IN_VARS+=('CPU_CORE')
EXPORTED_VARIABLES_ONLY_IN_VARS+=('CPU_FAM')
EXPORTED_VARIABLES_ONLY_IN_VARS+=('NATIVEINCLUDES')
EXPORTED_VARIABLES_ONLY_IN_VARS+=('UNDEF')
EXPORTED_VARIABLES_ONLY_IN_VARS+=('USEMODULE')
EXPORTED_VARIABLES_ONLY_IN_VARS+=('TARGET_ARCH')
EXPORTED_VARIABLES_ONLY_IN_VARS+=('TOOLCHAIN')
EXPORTED_VARIABLES_ONLY_IN_VARS+=('WERROR')
EXPORTED_VARIABLES_ONLY_IN_VARS+=('WPEDANTIC')
EXPORTED_VARIABLES_ONLY_IN_VARS+=('CC[ =]' 'CXX' 'CCAS')
EXPORTED_VARIABLES_ONLY_IN_VARS+=('AR[ =]' 'RANLIB')
EXPORTED_VARIABLES_ONLY_IN_VARS+=('AS' 'NM' 'SIZE' 'LINK')
EXPORTED_VARIABLES_ONLY_IN_VARS+=('OBJDUMP' 'OBJCOPY')
check_not_exporting_variables() {
local patterns=()
local pathspec=()
for variable in "${UNEXPORTED_VARIABLES[@]}"; do
patterns+=(-e "export[[:blank:]]\+${variable}")
done
git -C "${RIOTBASE}" grep -n "${patterns[@]}" \
| error_with_message 'Variables must not be exported'
# Some variables may still be exported in 'makefiles/vars.inc.mk' as the
# only place that should export common variables
pathspec+=('*')
pathspec+=(':!makefiles/vars.inc.mk')
pathspec+=(':!**/Vagrantfile')
patterns=()
for variable in "${EXPORTED_VARIABLES_ONLY_IN_VARS[@]}"; do
patterns+=(-e "export[[:blank:]]\+${variable}")
done
# Only run if there are patterns, otherwise it matches everything
if [ ${#patterns[@]} -ne 0 ]; then
git -C "${RIOTBASE}" grep -n "${patterns[@]}" -- "${pathspec[@]}" \
| error_with_message "Variables must only be exported in \`makefiles/vars.inc.mk\`"
fi
}
# Deprecated variables or patterns
# Prevent deprecated variables or patterns to re-appear after cleanup
check_deprecated_vars_patterns() {
local patterns=()
local pathspec=()
patterns+=(-e 'FEATURES_MCU_GROUP')
patterns+=(-e 'TEST_ON_CI_WHITELIST += all')
# Pathspec with exclude should start by an inclusive pathspec in git 2.7.4
pathspec+=('*')
# Ignore this file when matching as it self matches
pathspec+=(":!${SCRIPT_PATH}")
git -C "${RIOTBASE}" grep -n "${patterns[@]}" -- "${pathspec[@]}" \
| error_with_message 'Deprecated variables or patterns'
}
# Makefile files cpu must not be included by the board anymore
# They are included by the main Makefile.include/Makefile.features/Makefile.dep
check_board_do_not_include_cpu_features_dep() {
local patterns=()
local pathspec=()
patterns+=(-e "include \$(RIOTCPU)/.*/Makefile\..*")
pathspec+=('boards/')
git -C "${RIOTBASE}" grep -n "${patterns[@]}" -- "${pathspec[@]}" \
| error_with_message 'Makefiles files from cpu must not be included by the board anymore'
}
# CPU and CPU_MODEL definition have been moved to 'BOARD|CPU/Makefile.features'
check_cpu_cpu_model_defined_in_makefile_features() {
local patterns=()
local pathspec=()
# With our without space and with or without ?=
patterns+=(-e '^ *\(export\)\? *CPU \??\?=')
patterns+=(-e '^ *\(export\)\? *CPU_MODEL \??\?=')
pathspec+=(':!**.md')
pathspec+=(':!boards/**/Makefile.features')
pathspec+=(':!cpu/**/Makefile.features')
git -C "${RIOTBASE}" grep -n "${patterns[@]}" -- "${pathspec[@]}" \
| error_with_message 'CPU and CPU_MODEL definition must be done by board/BOARD/Makefile.features, board/common/**/Makefile.features or cpu/CPU/Makefile.features'
}
# Applications Makefile must not set 'BOARD =' unconditionally
check_not_setting_board_equal() {
local patterns=()
local pathspec=()
patterns+=(-e '^[[:space:]]*BOARD[[:space:]]*=')
pathspec+=('**/Makefile')
git -C "${RIOTBASE}" grep -n "${patterns[@]}" -- "${pathspec[@]}" \
| error_with_message 'Applications Makefile should use "BOARD ?="'
}
# Examples must not provide BOARD_INSUFFICIENT_MEMORY in Makefile, but in
# Makefile.ci
check_board_insufficient_memory_not_in_makefile() {
local patterns=()
local pathspec=()
patterns+=(-e '^[[:space:]]*BOARD_INSUFFICIENT_MEMORY[[:space:]:+]*=')
pathspec+=('**/Makefile')
git -C "${RIOTBASE}" grep -n "${patterns[@]}" -- "${pathspec[@]}" \
| error_with_message 'Move BOARD_INSUFFICIENT_MEMORY to Makefile.ci'
}
# Test applications must not define the APPLICATION variable
checks_tests_application_not_defined_in_makefile() {
local patterns=()
local pathspec=()
patterns+=(-e '^[[:space:]]*APPLICATION[[:space:]:+]=')
pathspec+=('tests/**/Makefile')
pathspec+=(':!tests/build_system/external_board_native/Makefile')
git -C "${RIOTBASE}" grep -n "${patterns[@]}" -- "${pathspec[@]}" \
| error_with_message "Don't define APPLICATION in test Makefile"
}
# Develhelp should not be set via CFLAGS
checks_develhelp_not_defined_via_cflags() {
local patterns=()
local pathspec=()
patterns+=(-e '^[[:space:]]*CFLAGS[[:space:]:+]+=[[:space:]:+]-DDEVELHELP')
pathspec+=('**/Makefile')
git -C "${RIOTBASE}" grep -n "${patterns[@]}" -- "${pathspec[@]}" \
| error_with_message "Use DEVELHELP ?= 1 instead of using CFLAGS directly"
}
# Common code in boards should not use $(BOARD) to reference files
check_files_in_boards_not_reference_board_var() {
local patterns=()
local pathspec=()
patterns+=(-e "/\$(BOARD)/")
pathspec+=('boards/')
# boards/common/nrf52 uses a hack to resolve dependencies early
pathspec+=(':!boards/common/nrf52/Makefile.include')
git -C "${RIOTBASE}" grep -n "${patterns[@]}" -- "${pathspec[@]}" \
| error_with_message "Code in boards/ should not use \$(BOARDS) to reference files since this breaks external BOARDS changing BOARDSDIR"
}
check_no_pseudomodules_in_makefile_dep() {
local patterns=()
local pathspec=()
patterns+=(-e 'PSEUDOMODULES[\t ]*[+:]*=')
pathspec+=('**/Makefile.dep')
git -C "${RIOTBASE}" grep -n "${patterns[@]}" -- "${pathspec[@]}" \
| error_with_message "Don't define PSEUDOMODULES in Makefile.dep"
}
check_no_usemodules_in_makefile_include() {
local patterns=()
local pathspec=()
patterns+=(-e 'USEMODULE[\t ]*[+:]*=')
pathspec+=('**/Makefile.include')
pathspec+=(':!Makefile.include')
pathspec+=(':!tests/**/Makefile.include')
pathspec+=(':!examples/**/Makefile.include')
git -C "${RIOTBASE}" grep -n "${patterns[@]}" -- "${pathspec[@]}" \
| error_with_message "Don't include USEMODULE in Makefile.include"
}
check_no_pkg_source_local() {
local patterns=()
local pathspec=()
patterns+=(-e 'PKG_SOURCE_LOCAL')
pathspec+=('pkg/*/Makefile')
git -C "${RIOTBASE}" grep -n "${patterns[@]}" -- "${pathspec[@]}" \
| error_with_message "Don't push PKG_SOURCE_LOCAL definitions upstream"
}
check_no_riot_config() {
local patterns=()
local pathspec=()
patterns+=(-e 'RIOT_CONFIG_.*')
pathspec+=('Makefile*')
pathspec+=('**/Makefile*')
pathspec+=('**/*.mk')
pathspec+=(':!makefiles/kconfig.mk')
pathspec+=(':!makefiles/docker.inc.mk')
git -C "${RIOTBASE}" grep -n "${patterns[@]}" -- "${pathspec[@]}" \
| error_with_message "Don't push RIOT_CONFIG_* definitions upstream. Rather define configuration via Kconfig"
}
check_stderr_null() {
local patterns=()
local pathspec=()
patterns+=(-e '2>[[:blank:]]*&1[[:blank:]]*>[[:blank:]]*/dev/null')
pathspec+=('Makefile*')
pathspec+=('**/Makefile*')
pathspec+=('**/*.mk')
git -C "${RIOTBASE}" grep -n "${patterns[@]}" -- "${pathspec[@]}" \
| error_with_message "Redirecting stderr and stdout to /dev/null is \`>/dev/null 2>&1\`; the other way round puts the old stderr to the new stdout."
}
# Test application directories that starts by the following strings are invalid
# and should moved to the corresponding tests subdirectory.
FORBIDDEN_TEST_DIRS=()
FORBIDDEN_TEST_DIRS+=('build_system')
FORBIDDEN_TEST_DIRS+=('core') # => move under tests/core
FORBIDDEN_TEST_DIRS+=('cpu') # => move under tests/cpu
FORBIDDEN_TEST_DIRS+=('driver') # => move under tests/drivers
FORBIDDEN_TEST_DIRS+=('gnrc') # => move under tests/net
FORBIDDEN_TEST_DIRS+=('periph') # => move under tests/periph
FORBIDDEN_TEST_DIRS+=('net') # => move under tests/net
FORBIDDEN_TEST_DIRS+=('pkg') # => move under tests/pkg
FORBIDDEN_TEST_DIRS+=('sys') # => move under tests/sys
FORBIDDEN_TEST_DIRS+=('vfs') # => move under tests/sys
FORBIDDEN_TEST_DIRS+=('xtimer') # => move under tests/sys
FORBIDDEN_TEST_DIRS+=('ztimer') # => move under tests/sys
check_tests_application_path() {
local patterns=()
patterns+=(-regex "^tests/bench_.*/Makefile")
for forbidden in "${FORBIDDEN_TEST_DIRS[@]}"; do
patterns+=(-o -regex "^tests/${forbidden}_.*/Makefile")
done
find tests/ -type f "${patterns[@]}" | error_with_message "Invalid application path in tests/"
}
check_pinned_docker_version_is_up_to_date() {
local pinned_digest
local pinned_repo_digest
local upstream_digest
local upstream_repo_digest
pinned_digest="$(awk '/^DOCKER_TESTED_IMAGE_ID := (.*)$/ { print substr($0, index($0, $3)); exit }' "$RIOTMAKE/docker.inc.mk")"
pinned_repo_digest="$(awk '/^DOCKER_TESTED_IMAGE_REPO_DIGEST := (.*)$/ { print substr($0, index($0, $3)); exit }' "$RIOTMAKE/docker.inc.mk")"
# not using docker and jq here but a python script to not have to install
# more stuff for the static test docker image
IFS=' ' read -r upstream_digest upstream_repo_digest <<< "$("$RIOTTOOLS/buildsystem_sanity_check/get_dockerhub_digests.py" "riot/riotbuild")"
if [ "$pinned_digest" != "$upstream_digest" ]; then
git -C "${RIOTBASE}" grep -n '^DOCKER_TESTED_IMAGE_ID :=' "$RIOTMAKE/docker.inc.mk" \
| error_with_message "Update docker image SHA256 to ${upstream_digest}"
fi
if [ "$pinned_repo_digest" != "$upstream_repo_digest" ]; then
git -C "${RIOTBASE}" grep -n '^DOCKER_TESTED_IMAGE_REPO_DIGEST :=' "$RIOTMAKE/docker.inc.mk" \
| error_with_message "Update manifest digest to ${upstream_repo_digest}"
fi
}
error_on_input() {
! grep ''
}
all_checks() {
check_not_parsing_features
check_providing_features_only_makefile_features
check_not_exporting_variables
check_deprecated_vars_patterns
check_board_do_not_include_cpu_features_dep
check_cpu_cpu_model_defined_in_makefile_features
check_not_setting_board_equal
check_board_insufficient_memory_not_in_makefile
checks_tests_application_not_defined_in_makefile
checks_develhelp_not_defined_via_cflags
check_files_in_boards_not_reference_board_var
check_no_pseudomodules_in_makefile_dep
check_no_usemodules_in_makefile_include
check_no_pkg_source_local
check_no_riot_config
check_stderr_null
check_tests_application_path
check_pinned_docker_version_is_up_to_date
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
github_annotate_setup
all_checks | error_on_input
result="$?"
github_annotate_teardown
exit "${result}"
fi