diff options
| author | Richard Qin <richard.qin.x@gmail.com> | 2026-07-10 23:20:03 +0800 |
|---|---|---|
| committer | Richard Qin <richard.qin.x@gmail.com> | 2026-07-10 23:20:03 +0800 |
| commit | 48ef95c24e9ac3d11200b61af617a01b10b507d1 (patch) | |
| tree | 57f0e26ab98e795b19839b6eece98ca58a4b702d /scripts/build/targets.mk | |
| parent | b9ff206e6bd3f4d67851263a6f2acb5f575cf3b0 (diff) | |
| download | lume-48ef95c24e9ac3d11200b61af617a01b10b507d1.tar.gz lume-48ef95c24e9ac3d11200b61af617a01b10b507d1.tar.xz | |
build: introduce declarative non-recursive build graph
Diffstat (limited to 'scripts/build/targets.mk')
| -rw-r--r-- | scripts/build/targets.mk | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/scripts/build/targets.mk b/scripts/build/targets.mk new file mode 100644 index 0000000..1a66379 --- /dev/null +++ b/scripts/build/targets.mk @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2026 Richard Qin + +# Declarative target registry for the LumeOS build. +# +# Build.mk files populate target.<name>.* and append the name to targets. A +# target name is metadata, not a Make rule, so names may contain '/' and ':'. + +target-kinds := \ + interface-library header-library object-library source-set \ + static-library whole-archive host-library phys-library host-tool generated \ + binary-blob kernel-test host-test product +targets := +generated-actions := autoconf asm-offsets linker-script initramfs + +empty := +space := $(empty) $(empty) +comma := , + +# Convert a logical target/source name into a path-safe component. +target-key = $(subst :,_,$(subst /,_,$(1))) + +# Recursively collect a target and all of its dependencies. Dependency cycles +# are rejected separately before this is evaluated for build roots. +target-deps = $(sort $(target.$(1).deps) $(target.$(1).public-deps) \ + $(target.$(1).private-deps) $(target.$(1).interface-deps) \ + $(target.$(1).link-deps) $(target.$(1).tool-deps) \ + $(target.$(1).generated-deps) $(target.$(1).runtime-deps) \ + $(target.$(1).order-only-deps)) +target-closure = $(strip \ + $(if $(filter $(1),$(2)),$(error target dependency cycle: $(2) -> $(1))) \ + $(1) $(foreach dep,$(call target-deps,$(1)),$(call target-closure,$(dep),$(2) $(1)))) +target-link-deps = $(sort $(target.$(1).deps) $(target.$(1).public-deps) \ + $(target.$(1).private-deps) $(target.$(1).interface-deps) \ + $(target.$(1).link-deps)) +target-link-order = $(strip $(1) $(foreach dep,$(call target-link-deps,$(1)),\ + $(call target-link-order,$(dep)))) +unique-words = $(if $(strip $(1)),$(firstword $(1)) \ + $(call unique-words,$(filter-out $(firstword $(1)),$(1)))) + +duplicate-words = $(sort $(foreach word,$(1),\ + $(if $(word 2,$(filter $(word),$(1))),$(word)))) +all-target-sources = $(foreach target,$(targets),$(target.$(target).sources)) +duplicate-target-sources = $(call duplicate-words,$(all-target-sources)) + +define validate-target +$(if $(filter $(target.$(1).kind),$(target-kinds)),,$(error target '$(1)' has invalid kind '$(target.$(1).kind)')) +$(if $(filter header-library interface-library product generated binary-blob,$(target.$(1).kind)),$(if $(strip $(target.$(1).sources)),$(error target '$(1)' of kind $(target.$(1).kind) declares sources),),) +$(if $(filter host-tool,$(target.$(1).kind)),$(if $(word 2,$(target.$(1).sources)),$(error host-tool '$(1)' currently requires exactly one source),),) +$(foreach src,$(target.$(1).sources),$(if $(wildcard $(src)),,$(error target '$(1)' references missing source '$(src)'))) +$(foreach dep,$(call target-deps,$(1)),$(if $(filter $(dep),$(targets)),,$(error target '$(1)' references unknown dependency '$(dep)'))) +$(foreach dep,$(call target-deps,$(1)),$(if $(strip $(target.$(dep).visibility)),\ + $(if $(filter all $(1),$(target.$(dep).visibility)),,\ + $(error target '$(1)' cannot depend on '$(dep)': visibility is '$(target.$(dep).visibility)')),)) +$(if $(filter generated,$(target.$(1).kind)),$(if $(strip $(target.$(1).outputs)),,$(error generated target '$(1)' has no outputs)),) +$(if $(filter generated,$(target.$(1).kind)),$(if $(filter $(target.$(1).action),$(generated-actions)),,$(error generated target '$(1)' has invalid action '$(target.$(1).action)')),) +endef + +define validate-targets +$(if $(strip $(targets)),,$(error no build targets were registered)) +$(if $(filter-out $(words $(sort $(targets))),$(words $(targets))),$(error duplicate build target name),) +$(foreach target,$(targets),$(call validate-target,$(target))) +endef + +# Usage requirements are deliberately separate from link inputs. Public +# include paths and defines propagate through deps; private values apply only +# while compiling the target itself. +target-public-closure = $(sort $(1) $(foreach dep,$(target.$(1).public-deps) \ + $(target.$(1).deps),$(call target-public-closure,$(dep)))) +target-public-includes = $(sort $(foreach target,$(call target-public-closure,$(1)),$(target.$(target).public-includes))) +target-public-after-includes = $(sort $(foreach target,$(call target-public-closure,$(1)),$(target.$(target).public-after-includes))) +target-public-defines = $(sort $(foreach target,$(call target-public-closure,$(1)),$(target.$(target).public-defines))) + +target-usage-roots = $(sort $(1) $(target.$(1).deps) $(target.$(1).public-deps) \ + $(target.$(1).private-deps) $(target.$(1).interface-deps)) +target-compile-includes = $(sort $(target.$(1).private-includes) \ + $(foreach root,$(call target-usage-roots,$(1)),$(call target-public-includes,$(root)))) +target-compile-after-includes = $(sort $(target.$(1).private-after-includes) \ + $(foreach root,$(call target-usage-roots,$(1)),$(call target-public-after-includes,$(root)))) +target-compile-defines = $(sort $(target.$(1).private-defines) \ + $(foreach root,$(call target-usage-roots,$(1)),$(call target-public-defines,$(root)))) |
