
# helper tools
AWK ?= gawk
INSTALL ?= install
M4 ?= m4
SED ?= sed
EINFO ?= echo
PYTHON ?= python3
CUT ?= cut

NAME ?= $(strip $(shell $(AWK) -F= '/^SELINUXTYPE/{ print $$2 }' /etc/selinux/config))
SHAREDIR ?= /usr/share/selinux
HEADERDIR ?= $(SHAREDIR)/$(NAME)/include

include $(HEADERDIR)/build.conf

# executables
PREFIX := /usr
BINDIR := $(PREFIX)/bin
SBINDIR := $(PREFIX)/sbin
CHECKMODULE := $(BINDIR)/checkmodule
SEMODULE := $(SBINDIR)/semodule
SEMOD_PKG := $(BINDIR)/semodule_package
XMLLINT := $(BINDIR)/xmllint

# set default build options if missing
TYPE ?= standard
DIRECT_INITRC ?= n
POLY ?= n
QUIET ?= y

genxml := $(PYTHON) $(HEADERDIR)/support/segenxml.py

docs := doc
polxml := $(docs)/policy.xml
xmldtd := $(HEADERDIR)/support/policy.dtd
basepolxml := $(HEADERDIR)/policy.xml

# enable MLS if requested.
ifeq "$(TYPE)" "mls"
	M4PARAM += -D enable_mls
	CHECKPOLICY += -M
	CHECKMODULE += -M
endif

# enable MLS if MCS requested.
ifeq "$(TYPE)" "mcs"
	M4PARAM += -D enable_mcs
	CHECKPOLICY += -M
	CHECKMODULE += -M
endif

# use specific module version if specified
ifneq ($(OUTPUT_MODULE),)
	CHECKMODULE += -c $(OUTPUT_MODULE)
endif

# enable distribution-specific policy
ifneq ($(DISTRO),)
	M4PARAM += -D distro_$(DISTRO)
endif

ifeq "$(SYSTEMD)" "y"
	M4PARAM += -D init_systemd
endif

ifeq ($(DIRECT_INITRC),y)
	M4PARAM += -D direct_sysadm_daemon
endif

ifeq "$(UBAC)" "y"
	M4PARAM += -D enable_ubac
endif

ifneq "$(CUSTOM_BUILDOPT)" ""
	M4PARAM += $(foreach opt,$(CUSTOM_BUILDOPT),-D $(opt))
endif

# default MLS/MCS sensitivity and category settings.
MLS_SENS ?= 16
MLS_CATS ?= 1024
MCS_CATS ?= 1024

ifeq ($(QUIET),y)
	verbose := @
endif

M4PARAM += -D mls_num_sens=$(MLS_SENS) -D mls_num_cats=$(MLS_CATS) -D mcs_num_cats=$(MCS_CATS)

# policy headers
m4support = $(wildcard $(HEADERDIR)/support/*.spt)

header_layers := $(filter-out $(HEADERDIR)/support,$(shell find $(HEADERDIR)/* -maxdepth 0 -type d))
header_interfaces := $(foreach layer,$(header_layers),$(wildcard $(layer)/*.if))

local_layers := $(filter-out CVS tmp $(docs),$(shell find * -maxdepth 0 -type d))

3rd_party_mods := $(wildcard *.te)
detected_mods := $(3rd_party_mods) $(foreach layer,$(local_layers),$(wildcard $(layer)/*.te))

detected_ifs := $(detected_mods:.te=.if)
detected_fcs := $(detected_mods:.te=.fc)
all_packages := $(notdir $(detected_mods:.te=.pp))

# figure out what modules we may want to reload
loaded_mods = $(addsuffix .pp,$(shell $(SEMODULE) -l | $(CUT) -f1))
sys_mods = $(wildcard $(SHAREDIR)/$(NAME)/*.pp)
match_sys = $(filter $(addprefix $(SHAREDIR)/$(NAME)/,$(loaded_mods)),$(sys_mods))
match_loc = $(filter $(all_packages),$(loaded_mods))

vpath %.te $(local_layers)
vpath %.if $(local_layers)
vpath %.fc $(local_layers)

.PHONY: clean all xml load reload
.SUFFIXES:
.SUFFIXES: .pp
# broken in make 3.81:
#.SECONDARY:

########################################
#
# Main targets
#

all: $(all_packages)

xml: $(polxml)

########################################
#
# Attempt to reinstall all installed packages
#
refresh:
	@$(EINFO) "Refreshing $(NAME) modules"
	$(verbose) $(SEMODULE) -b $(SHAREDIR)/$(NAME)/base.pp $(foreach mod,$(match_sys) $(match_loc),-i $(mod))

########################################
#
# Load module packages
#

load: tmp/loaded
tmp/loaded: $(all_packages)
	@$(EINFO) "Loading $(NAME) modules: $(basename $(notdir $?))"
	$(verbose) $(SEMODULE) $(foreach mod,$?,-i $(mod))
	@mkdir -p tmp
	@touch tmp/loaded

reload: $(all_packages)
	@$(EINFO) "Loading $(NAME) modules: $(basename $(notdir $^))"
	$(verbose) $(SEMODULE) $(foreach mod,$^,-i $(mod))
	@mkdir -p tmp
	@touch tmp/loaded

########################################
#
# Build module packages
#
tmp/%.mod: $(m4support) tmp/all_interfaces.conf %.te
	@$(EINFO) "Compiling $(NAME) $(basename $(@F)) module"
	@test -d $(@D) || mkdir -p $(@D)
	$(verbose) $(M4) $(M4PARAM) -s $^ > $(@:.mod=.tmp)
	$(verbose) $(CHECKMODULE) -m $(@:.mod=.tmp) -o $@

tmp/%.mod.fc: $(m4support) %.fc
	@test -d $(@D) || mkdir -p $(@D)
	$(verbose) $(M4) $(M4PARAM) $^ > $@

%.pp: tmp/%.mod tmp/%.mod.fc
	@echo "Creating $(NAME) $(@F) policy package"
	$(verbose) $(SEMOD_PKG) -o $@ -m $< -f $<.fc

tmp/all_interfaces.conf: $(m4support) $(header_interfaces) $(detected_ifs)
	@test -d $(@D) || mkdir -p $(@D)
	@echo "ifdef(\`__if_error',\`m4exit(1)')" > tmp/iferror.m4
	@echo "divert(-1)" > $@
	$(verbose) $(M4) $^ tmp/iferror.m4 | sed -e s/dollarsstar/\$$\*/g >> $@
	@echo "divert" >> $@

# so users dont have to make empty .fc and .if files
$(detected_fcs):
	@test -f $@ || touch $@

$(detected_ifs):
	@test -f $@ || echo "## <summary>$(basename $(@D))</summary>" > $@

########################################
#
# Documentation generation
#
$(polxml): $(basepolxml) $(detected_mods) $(detected_ifs) $(foreach dir,$(local_layers),$(wildcard $(dir)/metadata.xml))
	@echo "Creating $(@F)"
	@test -d $(@D) || mkdir -p $(@D)
	$(verbose) $(genxml) -w -o $@ --policy-file $(basepolxml) $(local_layers) $(if $(strip $(3rd_party_mods)),.)
	$(verbose) if test -x $(XMLLINT) && test -f $(xmldtd); then \
		$(XMLLINT) --noout --path $(dir $(xmldtd)) --dtdvalid $(xmldtd) $@ ;\
	fi

########################################
#
# Clean the environment
#

clean:
	rm -fR tmp
	rm -f *.pp
