diff --git a/.gitignore b/.gitignore
index ef8665c64f21..75f246e0565f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -52,6 +52,7 @@
*.zst
Module.symvers
modules.order
+check-tristates.objs
#
# Top-level generic files
diff --git a/Documentation/dontdiff b/Documentation/dontdiff
index ed1fbc711f33..17686f59039c 100644
--- a/Documentation/dontdiff
+++ b/Documentation/dontdiff
@@ -183,6 +183,7 @@ modules.builtin
modules.builtin.*
modules.nsdeps
modules.order
+check-tristates.objs
modversions.h*
nconf
nconf-cfg
diff --git a/Makefile b/Makefile
index 248f780cb75b..4d8a4c231cc1 100644
--- a/Makefile
+++ b/Makefile
@@ -1689,6 +1689,7 @@ help:
@echo ' coccicheck - Check with Coccinelle'
@echo ' clang-analyzer - Check with clang static analyzer'
@echo ' clang-tidy - Check with clang-tidy'
+ @echo ' tristatecheck - Check for non-tristates with MODULE_ declarations'
@echo ''
@echo 'Tools:'
@echo ' nsdeps - Generate missing symbol namespace dependencies'
@@ -2012,7 +2013,7 @@ clean: $(clean-dirs)
-o -name '*.lex.c' -o -name '*.tab.[ch]' \
-o -name '*.asn1.[ch]' \
-o -name '*.symtypes' -o -name 'modules.order' \
- -o -name '.tmp_*' \
+ -o -name 'check-tristates.objs' -o -name '.tmp_*' \
-o -name '*.c.[012]*.*' \
-o -name '*.ll' \
-o -name '*.gcno' \
@@ -2082,7 +2083,25 @@ coccicheck:
export_report:
$(PERL) $(srctree)/scripts/export_report.pl
-PHONY += checkstack kernelrelease kernelversion image_name
+tristatecheck: SHELL=/bin/bash
+tristatecheck: check-tristates.objs modules.builtin.objs
+ $(Q) for name in $$(comm -23 <(sed 's,:,,' modules.builtin.objs | tr ' ' '\n' | sort -u) \
+ <(sed 's,:,,' check-tristates.objs | tr ' ' '\n' | sort -u)); do \
+ grep -w $$name modules.builtin.objs | while read -r line; do \
+ case $$line in \
+ $$name:\ ) printf "%s " "$$line" | sed 's,: ,,';; \
+ $$name:\ *) printf "%s " "$$line" | sed 's,^.*: ,,';; \
+ *) echo $$name;; \
+ esac; \
+ done; \
+ done | tr ' ' '\n' | sort -u | while read -r name; do \
+ test -f $${name%.o}.c && echo $${name%.o}.c; \
+ done | xargs grep -l '^ *MODULE_' || true
+
+check-tristates.objs: $(build-dir)
+ $(Q)$(MAKE) $(tristatecheck)=$^ tristates-file=$@
+
+PHONY += checkstack kernelrelease kernelversion image_name tristatecheck
# UML needs a little special treatment here. It wants to use the host
# toolchain, so needs $(SUBARCH) passed to checkstack.pl. Everyonediff --git a/scripts/Kbuild.include b/scripts/Kbuild.include
index 2bc08ace38a3..8042c067312e 100644
--- a/scripts/Kbuild.include
+++ b/scripts/Kbuild.include
@@ -78,6 +78,12 @@ endef
# $(Q)$(MAKE) $(build)=dir
build := -f $(srctree)/scripts/Makefile.build obj
+###
+# Shorthand for $(Q)$(MAKE) -f scripts/check-tristates.mk need-tristates=1 obj=
+# Usage:
+# $(Q)$(MAKE) $(tristatecheck)=dir
+tristatecheck := -f $(srctree)/scripts/check-tristates.mk need-tristates=1 obj
+
###
# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.dtbinst obj=
# Usage:
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index f7e5a83572fa..3bbfa99259cd 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -39,11 +39,17 @@ else
obj-m := $(filter-out %/, $(obj-m))
endif
-ifdef need-builtin
+ifdef need-tristates
+subdir-ym := $(sort $(subdir-y) $(subdir-m) \
+ $(patsubst %/,%, $(filter %/, $(obj-y) $(obj-m) $(obj-Y) $(obj-M))))
obj-y := $(patsubst %/, %/built-in.a, $(obj-y))
else
+ifdef need-builtin
+obj-y := $(patsubst %/, %/built-in.a, $(obj-y))
+else
obj-y := $(filter-out %/, $(obj-y))
endif
+endif
# Expand $(foo-objs) $(foo-y) etc. by replacing their individuals
suffix-search = $(strip $(foreach s, $3, $($(1:%$(strip $2)=%$s))))
diff --git a/scripts/check-tristates.mk b/scripts/check-tristates.mk
new file mode 100644
index 000000000000..fa9fadb79143
--- /dev/null
+++ b/scripts/check-tristates.mk
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: GPL-2.0
+# ==========================================================================
+# Generating check-tristates.objs
+# ==========================================================================
+
+src := $(obj)
+
+PHONY := __tristates
+__tristates:
+
+include include/config/auto.conf
+# tristate.conf sets tristate variables to uppercase 'Y' or 'M'
+# That way, we get the list of built-in modules in obj-Y
+include include/config/tristate.conf
+
+include scripts/Kbuild.include
+
+ifdef building_out_of_srctree
+# Create output directory if not already present
+_dummy := $(shell [ -d $(obj) ] || mkdir -p $(obj))
+endif
+
+# The filename Kbuild has precedence over Makefile
+kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
+kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
+include $(kbuild-file)
+
+include scripts/Makefile.lib
+
+check-tristates-subdirs := $(patsubst %,%/check-tristates.objs, $(subdir-ym))
+check-tristates-target := $(obj)/check-tristates.objs
+
+__tristates: $(obj)/$(tristates-file) $(subdir-ym)
+ @:
+
+$(check-tristates-target): $(subdir-ym) FORCE
+ $(Q) rm -f $@
+ $(Q) $(foreach mod-o, $(filter %.o,$(obj-Y)),\
+ printf "%s: " $(addprefix $(obj)/,$(mod-o)) >> $@; \
+ printf " %s" $(sort $(strip $(addprefix $(obj)/,$($(mod-o:.o=-objs)) \
+ $($(mod-o:.o=-y)) $($(mod-o:.o=-Y))))) >> $@; \
+ printf "\n" >> $@; ) \
+ cat /dev/null $(check-tristates-subdirs) >> $@;
+
+PHONY += FORCE
+
+FORCE:
+
+# Descending
+# ---------------------------------------------------------------------------
+
+PHONY += $(subdir-ym)
+$(subdir-ym):
+ $(Q)$(MAKE) $(tristatecheck)=$@ tristates-file=$(tristates-file)
+
+.PHONY: $(PHONY)
--
2.38.0.266.g481848f278