1# PRODUCT_VALIDATION_CHECKS allows you to enforce that your product config variables follow some
2# rules. To use it, add the paths to starlark configuration language (scl) files in
3# PRODUCT_VALIDATION_CHECKS. A validate_product_variables function in those files will be called
4# with a single "context" object.
5#
6# The context object currently 2 attributes:
7#   - product_variables: This has all the product variables. All the variables are either of type
8#                        string or list, more accurate typing (like bool) isn't known.
9#   - board_variables: This only has a small subset of the board variables, because there isn't a
10#                      known list of board variables. Feel free to expand the subset if you need a
11#                      new variable.
12#
13# You can then inspect (but not modify) these variables and fail() if they don't meet your
14# requirements. Example:
15#
16# In a product config file: PRODUCT_VALIDATION_CHECKS += //path/to/my_validations.scl
17# In my_validations.scl:
18# def validate_product_variables(ctx):
19#     for dir in ctx.board_variables.BOARD_SEPOLICY_DIRS:
20#         if not dir.startswith('system/sepolicy/'):
21#             fail('Only sepolicies in system/seplicy are allowed, found: ' + dir)
22
23ifdef PRODUCT_VALIDATION_CHECKS
24
25$(if $(filter-out //%.scl,$(PRODUCT_VALIDATION_CHECKS)), \
26	$(error All PRODUCT_VALIDATION_CHECKS files must start with // and end with .scl, exceptions: $(filter-out //%.scl,$(PRODUCT_VALIDATION_CHECKS))))
27
28known_board_variables := \
29  BOARD_VENDOR_SEPOLICY_DIRS BOARD_SEPOLICY_DIRS \
30  SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS \
31  SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS \
32  PRODUCT_PUBLIC_SEPOLICY_DIRS \
33  PRODUCT_PRIVATE_SEPOLICY_DIRS
34
35known_board_list_variables := \
36  BOARD_VENDOR_SEPOLICY_DIRS BOARD_SEPOLICY_DIRS \
37  SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS \
38  SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS \
39  PRODUCT_PUBLIC_SEPOLICY_DIRS \
40  PRODUCT_PRIVATE_SEPOLICY_DIRS
41
42escape_starlark_string=$(subst ",\",$(subst \,\\,$(1)))
43product_variable_starlark_value=$(if $(filter $(1),$(_product_list_vars) $(known_board_list_variables)),[$(foreach w,$($(1)),"$(call escape_starlark_string,$(w))", )],"$(call escape_starlark_string,$(1))")
44filename_to_starlark=$(subst -,_,$(subst /,_,$(subst .,_,$(1))))
45_c:=$(foreach f,$(PRODUCT_VALIDATION_CHECKS),$(newline)load("$(f)", validate_product_variables_$(call filename_to_starlark,$(f)) = "validate_product_variables"))
46# TODO: we should freeze the context because it contains mutable lists, so that validation checks can't affect each other
47_c+=$(newline)_ctx = struct(
48_c+=$(newline)product_variables = struct(
49_c+=$(foreach v,$(_product_var_list),$(newline)  $(v) = $(call product_variable_starlark_value,$(v)),)
50_c+=$(newline)),
51_c+=$(newline)board_variables = struct(
52_c+=$(foreach v,$(known_board_variables),$(newline)  $(v) = $(call product_variable_starlark_value,$(v)),)
53_c+=$(newline))
54_c+=$(newline))
55_c+=$(foreach f,$(PRODUCT_VALIDATION_CHECKS),$(newline)validate_product_variables_$(call filename_to_starlark,$(f))(_ctx))
56_c+=$(newline)variables_to_export_to_make = {}
57$(KATI_file_no_rerun >$(OUT_DIR)/product_validation_checks_entrypoint.scl,$(_c))
58filename_to_starlark:=
59escape_starlark_string:=
60product_variable_starlark_value:=
61known_board_variables :=
62known_board_list_variables :=
63
64# Exclude the entrypoint file as a dependency (by passing it as the 2nd argument) so that we don't
65# rerun kati every build. Even though we're using KATI_file_no_rerun, product config is run every
66# build, so the file will still be rewritten.
67#
68# We also need to pass --allow_external_entrypoint to rbcrun in case the OUT_DIR is set to something
69# outside of the source tree.
70$(call run-starlark,$(OUT_DIR)/product_validation_checks_entrypoint.scl,$(OUT_DIR)/product_validation_checks_entrypoint.scl,--allow_external_entrypoint)
71
72endif # ifdef PRODUCT_VALIDATION_CHECKS
73