ec: common: Make IS_ENABLED fail on unknown values
If IS_ENABLED is called with any unknown values, a compiler error will be thrown. This change requires that the optimizer always be enabled, otherwise errors will be thrown when a value is not defined. BUG=none BRANCH=none TEST=make runtests TEST_LIST_HOST="is_enabled_error is_enabled" Change-Id: I1b166311f81d07e48b3665f4bc0e9502d2ccc4c6 Signed-off-by: Raul E Rangel <rrangel@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/1592728 Reviewed-by: Jett Rink <jettrink@chromium.org> Reviewed-by: Jack Rosenthal <jrosenth@chromium.org> Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org>
This commit is contained in:
parent
1598a615eb
commit
aaba1d5efd
5
Makefile
5
Makefile
|
@ -308,8 +308,9 @@ rw-objs := $(sort $(rw-common-objs) $(rw-only-objs))
|
|||
ifeq ($(CONFIG_SHAREDLIB),y)
|
||||
ro-objs := $(filter-out %_sharedlib.o, $(ro-objs))
|
||||
endif
|
||||
ro-deps := $(ro-objs:%.o=%.o.d)
|
||||
rw-deps := $(rw-objs:%.o=%.o.d)
|
||||
ro-deps := $(addsuffix .d, $(ro-objs))
|
||||
rw-deps := $(addsuffix .d, $(rw-objs))
|
||||
|
||||
deps := $(ro-deps) $(rw-deps) $(deps-y)
|
||||
|
||||
.PHONY: ro rw
|
||||
|
|
|
@ -94,7 +94,8 @@ cmd_cxx_to_host = $(HOSTCXX) -std=c++0x $(COMMON_WARN) $(HOST_CXXFLAGS)\
|
|||
cmd_o_to_a = $(AR) rcs $@ $^
|
||||
cmd_host_test = $(MAKE) --no-print-directory BOARD=host PROJECT=$* \
|
||||
V=$(V) out=build/host/$* TEST_BUILD=y EMU_BUILD=y CROSS_COMPILE= \
|
||||
$(TEST_FLAG) build/host/$*/$*.exe
|
||||
$(if $(TEST_SCRIPT),TEST_SCRIPT=$(TEST_SCRIPT)) $(TEST_FLAG) \
|
||||
build/host/$*/$*.exe
|
||||
cmd_run_host_test = ./util/run_host_test $* $(silent)
|
||||
# generate new version.h, compare if it changed and replace if so
|
||||
cmd_version = ./util/getversion.sh > $@.tmp && \
|
||||
|
@ -122,6 +123,7 @@ cmd_emmc_bootblock = $(out)/util/gen_emmc_transfer_data \
|
|||
$(if $(BOOTBLOCK),-i $(BOOTBLOCK)) -o $@
|
||||
|
||||
cmd_ipi_table = $(out)/util/gen_ipi_table $@
|
||||
cmd_cp_script = cp "$<" "$@" && chmod +x "$@"
|
||||
|
||||
# commands for RSA signature: rwsig does not need to sign the whole image
|
||||
# (it signs the RW part separately). usbpd1 type needs to sign the final image.
|
||||
|
@ -471,11 +473,16 @@ $(out)/%.smap: $(out)/%.elf
|
|||
ifeq ($(TEST_FUZZ),y)
|
||||
$(out)/$(PROJECT).exe: $(rw-only-objs) $(out)/libec.a
|
||||
$(call quiet,fuzz_exe,EXE )
|
||||
else ifneq ($(TEST_SCRIPT),)
|
||||
$(out)/$(PROJECT).exe: test/$(TEST_SCRIPT) | $(ro-objs)
|
||||
$(call quiet,cp_script,CP )
|
||||
else
|
||||
$(out)/$(PROJECT).exe: $(ro-objs)
|
||||
$(call quiet,exe,EXE )
|
||||
endif
|
||||
|
||||
$(out)/RO/%.o.cmd:%.c
|
||||
$(file > $@,$(subst .o.cmd,.o,$(cmd_c_to_o)))
|
||||
$(out)/RO/%.o:%.c
|
||||
$(call quiet,c_to_o,CC )
|
||||
$(out)/RW/%.o:%.c
|
||||
|
|
|
@ -92,6 +92,8 @@ HOST_CPPFLAGS=$(CFLAGS_DEFINE) $(CFLAGS_INCLUDE) $(CFLAGS_TEST) \
|
|||
ifneq ($(BOARD),host)
|
||||
CPPFLAGS+=-ffreestanding -fno-builtin -nostdinc -nostdlib
|
||||
CPPFLAGS+=-Ibuiltin/
|
||||
else
|
||||
CPPFLAGS+=-Og
|
||||
endif
|
||||
CFLAGS=$(CPPFLAGS) $(CFLAGS_CPU) $(CFLAGS_DEBUG) $(COMMON_WARN) $(CFLAGS_y)
|
||||
CFLAGS+= -ffunction-sections -fshort-wchar
|
||||
|
|
|
@ -236,29 +236,49 @@ enum ec_error_list {
|
|||
|
||||
/*
|
||||
* Getting something that works in C and CPP for an arg that may or may
|
||||
* not be defined is tricky. Here, if we have "#define CONFIG_BOOGER"
|
||||
* we match on the placeholder define, insert the "0," for arg1 and generate
|
||||
* the triplet (0, 1, 0). Then the last step cherry picks the 2nd arg (a one).
|
||||
* When CONFIG_BOOGER is not defined, we generate a (... 1, 0) pair, and when
|
||||
* the last step cherry picks the 2nd arg, we get a zero.
|
||||
* not be defined is tricky. Here, if we have "#define CONFIG_FOO"
|
||||
* we match on the placeholder define, insert the "_, 1," for arg1 and generate
|
||||
* the triplet (_, 1, _, (...)). Then the last step cherry picks the 2nd arg
|
||||
* (a one).
|
||||
* When CONFIG_FOO is not defined, we generate a (_, (...)) pair, and when
|
||||
* the last step cherry picks the 2nd arg, we get a code block that verifies
|
||||
* the value of the option. Since the preprocessor won't replace an unknown
|
||||
* token, we compare the option name with the value string. If they are
|
||||
* identical we assume that the value was undefined and return 0. If the value
|
||||
* happens to be anything else we call an undefined method that will raise
|
||||
* a compiler error. This technique requires that the optimizer be enabled so it
|
||||
* can remove the undefined function call.
|
||||
*
|
||||
*/
|
||||
#define __ARG_PLACEHOLDER_ 0,
|
||||
#define config_enabled(cfg) _config_enabled(cfg)
|
||||
#define _config_enabled(value) __config_enabled(__ARG_PLACEHOLDER_##value)
|
||||
#define __config_enabled(arg1_or_junk) ___config_enabled(arg1_or_junk 1, 0, 0)
|
||||
#define __ARG_PLACEHOLDER_ _, 1,
|
||||
#define _config_enabled(cfg, value) \
|
||||
__config_enabled(__ARG_PLACEHOLDER_##value, cfg, value)
|
||||
#define __config_enabled(arg1_or_junk, cfg, value) ___config_enabled( \
|
||||
arg1_or_junk _,\
|
||||
({ \
|
||||
int __undefined = __builtin_strcmp(cfg, #value) == 0; \
|
||||
extern int IS_ENABLED_BAD_ARGS(void) __attribute__(( \
|
||||
error(cfg " must be <blank>, or not defined.")));\
|
||||
if (!__undefined) \
|
||||
IS_ENABLED_BAD_ARGS(); \
|
||||
0; \
|
||||
}))
|
||||
#define ___config_enabled(__ignored, val, ...) val
|
||||
|
||||
/**
|
||||
* Checks if a config option is defined to an empty value.
|
||||
* Checks if a config option is enabled or disabled
|
||||
*
|
||||
* IS_ENABLED(CONFIG_MY_OPTION) will return 1 in the following case:
|
||||
* #define CONFIG_MY_OPTION
|
||||
* Enabled examples:
|
||||
* #define CONFIG_FOO
|
||||
*
|
||||
* Otherwise if the option has not been defined or defined with a value, it will
|
||||
* return 0.
|
||||
* Disabled examples:
|
||||
* #undef CONFIG_FOO
|
||||
*
|
||||
* @param CONFIG_OPTION
|
||||
* If the option is defined to any value a compiler error will be thrown.
|
||||
*
|
||||
* Note: This macro will only function inside a code block due to the way
|
||||
* it checks for unknown values.
|
||||
*/
|
||||
#define IS_ENABLED(option) config_enabled(option)
|
||||
#define IS_ENABLED(option) _config_enabled(#option, option)
|
||||
|
||||
#endif /* __CROS_EC_COMMON_H */
|
||||
|
|
|
@ -36,6 +36,8 @@ test-list-host += hooks
|
|||
test-list-host += host_command
|
||||
test-list-host += inductive_charging
|
||||
test-list-host += interrupt
|
||||
test-list-host += is_enabled
|
||||
test-list-host += is_enabled_error
|
||||
test-list-host += kb_8042
|
||||
test-list-host += kb_mkbp
|
||||
test-list-host += kb_scan
|
||||
|
@ -101,6 +103,7 @@ host_command-y=host_command.o
|
|||
inductive_charging-y=inductive_charging.o
|
||||
interrupt-scale=10
|
||||
interrupt-y=interrupt.o
|
||||
is_enabled-y=is_enabled.o
|
||||
kb_8042-y=kb_8042.o
|
||||
kb_mkbp-y=kb_mkbp.o
|
||||
kb_scan-y=kb_scan.o
|
||||
|
@ -152,3 +155,6 @@ TPM2_ROOT := $(CROS_WORKON_SRCROOT)/src/third_party/tpm2
|
|||
$(out)/RO/common/new_nvmem.o: CFLAGS += -I$(TPM2_ROOT)
|
||||
$(out)/RO/test/nvmem.o: CFLAGS += -I$(TPM2_ROOT)
|
||||
$(out)/RO/test/nvmem_tpm2_mock.o: CFLAGS += -I$(TPM2_ROOT)
|
||||
|
||||
host-is_enabled_error: TEST_SCRIPT=is_enabled_error.sh
|
||||
is_enabled_error-y=is_enabled_error.o.cmd
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/* Copyright 2019 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.
|
||||
*
|
||||
* Test the IS_ENABLED macro.
|
||||
*/
|
||||
#include "common.h"
|
||||
#include "test_util.h"
|
||||
|
||||
#undef CONFIG_UNDEFINED
|
||||
#define CONFIG_BLANK
|
||||
|
||||
static int test_undef(void)
|
||||
{
|
||||
TEST_ASSERT(IS_ENABLED(CONFIG_UNDEFINED) == 0);
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
static int test_blank(void)
|
||||
{
|
||||
TEST_ASSERT(IS_ENABLED(CONFIG_BLANK) == 1);
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
void run_test(void)
|
||||
{
|
||||
test_reset();
|
||||
|
||||
RUN_TEST(test_undef);
|
||||
RUN_TEST(test_blank);
|
||||
|
||||
test_print_result();
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
/* Copyright (c) 2019 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* See CONFIG_TASK_LIST in config.h for details.
|
||||
*/
|
||||
#define CONFIG_TEST_TASK_LIST /* No test task */
|
|
@ -0,0 +1,27 @@
|
|||
/* Copyright 2019 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.
|
||||
*
|
||||
* Test the IS_ENABLED macro fails on unexpected input.
|
||||
*/
|
||||
#include "common.h"
|
||||
#include "test_util.h"
|
||||
|
||||
#define CONFIG_VALUE TEST_VALUE
|
||||
|
||||
static int test_invalid_value(void)
|
||||
{
|
||||
/* This will cause a compilation error */
|
||||
TEST_ASSERT(IS_ENABLED(CONFIG_VALUE) == 0);
|
||||
|
||||
return EC_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
void run_test(void)
|
||||
{
|
||||
test_reset();
|
||||
|
||||
RUN_TEST(test_invalid_value);
|
||||
|
||||
test_print_result();
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
#!/bin/bash -e
|
||||
# Copyright 2019 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.
|
||||
|
||||
TEST_DIR="$(dirname "${BASH_SOURCE[0]}")"
|
||||
|
||||
TEST_CMD="$(cat "${TEST_DIR}/RO/test/is_enabled_error.o.cmd")"
|
||||
|
||||
TEST_ERROR_COUNT=0
|
||||
|
||||
for test_value in 0 1 2 A "5 + 5"; do
|
||||
echo -n "Running TEST_VALUE=${test_value}..."
|
||||
TEST_CMD_COMPLETE="${TEST_CMD} \"-DTEST_VALUE=${test_value}\""
|
||||
if BUILD_OUTPUT="$(sh -c "$TEST_CMD_COMPLETE" 2>&1)"; then
|
||||
echo "Fail"
|
||||
echo "Compilation should not have succeeded for" \
|
||||
"TEST_VALUE=${test_value}"
|
||||
echo "$BUILD_OUTPUT"
|
||||
TEST_ERROR_COUNT=$((TEST_ERROR_COUNT+1))
|
||||
continue
|
||||
fi
|
||||
|
||||
EXPECTED_ERROR="CONFIG_VALUE must be <blank>, or not defined"
|
||||
if grep -q "$EXPECTED_ERROR" <<< "$BUILD_OUTPUT"; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "Fail"
|
||||
echo "Expected to find: $EXPECTED_ERROR"
|
||||
echo "Actual error:"
|
||||
echo "$BUILD_OUTPUT"
|
||||
TEST_ERROR_COUNT=$((TEST_ERROR_COUNT+1))
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $TEST_ERROR_COUNT -eq 0 ]]; then
|
||||
echo "Pass!"
|
||||
else
|
||||
echo "Fail! (${TEST_ERROR_COUNT} tests)"
|
||||
fi
|
|
@ -0,0 +1,9 @@
|
|||
/* Copyright (c) 2019 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* See CONFIG_TASK_LIST in config.h for details.
|
||||
*/
|
||||
#define CONFIG_TEST_TASK_LIST /* No test task */
|
Loading…
Reference in New Issue