include build.env
-include build.env.work
export

TAG ?= dev

PACKAGES_DIR := packages
OUT_DIR      := out

E2FSPROGS_TAR := $(PACKAGES_DIR)/e2fsprogs-$(E2FSPROGS_VERSION).tar.gz
BUSYBOX_TAR   := $(PACKAGES_DIR)/busybox-$(BUSYBOX_VERSION).tar.gz
ALPINE_TAR    := $(PACKAGES_DIR)/alpine-minirootfs-$(ALPINE_VER)-$(ALPINE_ARCH).tar.gz
NXP_TAR       := $(PACKAGES_DIR)/nxp/kernel/$(NXP_VERSION).tar.gz
FMLIB_TAR     := $(PACKAGES_DIR)/nxp/fmlib/$(FMLIB_VERSION).tar.gz
FMC_TAR       := $(PACKAGES_DIR)/nxp/fmc/$(FMC_VERSION).tar.gz
VPP_TAR       := $(PACKAGES_DIR)/nxp/vpp/$(VPP_VERSION).tar.gz
DPDK_TAR      := $(PACKAGES_DIR)/nxp/dpdk/$(DPDK_VERSION).tar.gz
CRIO_TAR      := $(PACKAGES_DIR)/$(CRIO_VERSION).tar.gz

# Kubernetes components
KUBELET_BIN := $(PACKAGES_DIR)/kubernetes/kubelet-$(KUBE_VERSION)
KUBEADM_BIN := $(PACKAGES_DIR)/kubernetes/kubeadm-$(KUBE_VERSION)
KUBECTL_BIN := $(PACKAGES_DIR)/kubernetes/kubectl-$(KUBE_VERSION)

CLITOOLS_BIN := bin/ctl-linux-$(ARCH)-$(TAG)

CONFIGS_DIR          := configs
SCRIPTS_DIR          := scripts
CLUSTER_ENV_DEFAULT  := $(CONFIGS_DIR)/cluster.env.default
CLUSTER_ENV          := $(OUT_DIR)/cluster.env
NODE_ENV_DEFAULT     := configs/node.env.default
NODE_ENV             := $(OUT_DIR)/node.env

BOARD_ITB     := $(OUT_DIR)/board.itb
INITRAMFS     := $(OUT_DIR)/initramfs.cpio.gz
CATALOG       := $(OUT_DIR)/catalog-$(KUBE_VERSION)-$(TAG).txt
RELEASE_IMAGE := $(OUT_DIR)/monok8s-$(KUBE_VERSION)-$(TAG).img.gz
PART_IMAGE    := $(OUT_DIR)/monok8s-$(KUBE_VERSION)-$(TAG).ext4.zst
KERNEL_IMAGE  := $(OUT_DIR)/Image.gz

BUILD_BASE_STAMP        := $(OUT_DIR)/.build-base-$(TAG).stamp
DOWNLOAD_PACKAGES_STAMP := $(PACKAGES_DIR)/.stamp-$(KUBE_VERSION)-$(CRIO_VERSION)

ALPINE_SERIES := $(word 1,$(subst ., ,$(ALPINE_VER))).$(word 2,$(subst ., ,$(ALPINE_VER)))

BUILD_VERSION   ?= $(KUBE_VERSION)
BUILD_DATE      := $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
BUILD_GIT       := $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
BUILD_INFO_FILE := $(OUT_DIR)/build-info

# ---- File groups -------------------------------------------------------------

# Use find so adding new files under these dirs is automatically picked up.
ALPINE_SRCS    := $(shell find alpine -type f 2>/dev/null)
INITRAMFS_SRCS := $(shell find initramfs -type f 2>/dev/null)
KERNEL_SRCS    := $(shell find kernel-build -type f 2>/dev/null)

CLITOOLS_SRCS  := $(shell find clitools -type f 2>/dev/null)

BUILD_BASE_DEPS := \
	docker/build-base.Dockerfile \
	build.env \
	makefile

KERNEL_DEPS := \
	$(BUILD_BASE_STAMP) \
	docker/kernel-build.Dockerfile \
	kernel-extra.config \
	$(KERNEL_SRCS) \
	build.env \
	makefile

INITRAMFS_DEPS := \
	$(KERNEL_IMAGE) \
	docker/initramfs.Dockerfile \
	$(INITRAMFS_SRCS) \
	$(BUILD_INFO_FILE) \
	build.env \
	makefile

ITB_DEPS := \
	$(INITRAMFS) \
	docker/itb.Dockerfile \
	board.its \
	build.env \
	makefile

CLITOOLS := \
	$(CLITOOLS_SRCS)

RELEASE_DEPS := \
	$(BUILD_BASE_STAMP) \
	$(BUILD_INFO_FILE) \
	$(BOARD_ITB) \
	$(CLITOOLS_BIN) \
	docker/alpine.Dockerfile \
	$(ALPINE_SRCS) \
	build.env \
	makefile

# ---- Directory creation ------------------------------------------------------

$(PACKAGES_DIR):
	mkdir -p $@ \
		$@/kubernetes \
		$@/nxp/vpp \
		$@/nxp/fmc \
		$@/nxp/fmlib \
		$@/nxp/kernel \
		$@/nxp/dpdk

$(OUT_DIR):
	mkdir -p $@

# ---- Package downloads -------------------------------------------------------
$(DOWNLOAD_PACKAGES_STAMP): docker/download-packages.Dockerfile build.env makefile | $(PACKAGES_DIR)
	docker build \
		-f docker/download-packages.Dockerfile \
		--build-arg KUBE_VERSION=$(KUBE_VERSION) \
		--build-arg ARCH=$(ARCH) \
		--build-arg BUSYBOX_VERSION=$(BUSYBOX_VERSION) \
		--build-arg E2FSPROGS_VERSION=$(E2FSPROGS_VERSION) \
		--build-arg DPDK_VERSION=$(DPDK_VERSION) \
		--build-arg FMLIB_VERSION=$(FMLIB_VERSION) \
		--build-arg FMC_VERSION=$(FMC_VERSION) \
		--build-arg VPP_VERSION=$(VPP_VERSION) \
		--build-arg ALPINE_SERIES=$(ALPINE_SERIES) \
		--build-arg ALPINE_ARCH=$(ALPINE_ARCH) \
		--build-arg ALPINE_VER=$(ALPINE_VER) \
		--build-arg NXP_VERSION=$(NXP_VERSION) \
		--build-arg CRIO_VERSION=$(CRIO_VERSION) \
		--output type=local,dest=./$(PACKAGES_DIR) .
	@touch $@

# ---- Build stages ------------------------------------------------------------
$(BUILD_INFO_FILE):
	@mkdir -p $(dir $@)
	@printf '%s\n' \
		'export $(BUILD_TAG)_VERSION="$(BUILD_VERSION)"' \
		'export $(BUILD_TAG)_BUILD_DATE="$(BUILD_DATE)"' \
		'export $(BUILD_TAG)_GIT="$(BUILD_GIT)"' \
		'export $(BUILD_TAG)_RELEASE_IMAGE="$(notdir $(RELEASE_IMAGE))"' \
		> $@

$(BUILD_BASE_STAMP): $(BUILD_BASE_DEPS) | $(OUT_DIR)
	docker build \
		-f docker/build-base.Dockerfile \
		--build-arg APT_PROXY=$(APT_PROXY) \
		--build-arg TAG=$(TAG) \
		-t $(DOCKER_IMAGE_ROOT)/build-base:$(TAG) .
	@iid=$$(docker image inspect \
		--format '{{.Id}}' \
		$(DOCKER_IMAGE_ROOT)/build-base:$(TAG) \
		| cut -d':' -f2 \
		| cut -c -8); \
	docker tag monok8s/build-base:$(TAG) monok8s/build-base:$$iid; \
	touch $@

$(KERNEL_IMAGE): $(KERNEL_DEPS) $(DOWNLOAD_PACKAGES_STAMP) | $(OUT_DIR)
	docker build \
		-f docker/kernel-build.Dockerfile \
		--build-arg DOCKER_IMAGE_ROOT=$(DOCKER_IMAGE_ROOT) \
		--build-arg TAG=$(TAG) \
		--build-arg ARCH=$(ARCH) \
		--build-arg CROSS_COMPILE=$(CROSS_COMPILE) \
		--build-arg NXP_VERSION=$(NXP_VERSION) \
		--build-arg DEVICE_TREE_TARGET=$(DEVICE_TREE_TARGET) \
		--output type=local,dest=./$(OUT_DIR) .
	test -f $@

$(INITRAMFS): $(INITRAMFS_DEPS) $(DOWNLOAD_PACKAGES_STAMP) | $(OUT_DIR)
	docker build \
		-f docker/initramfs.Dockerfile \
		--build-arg DOCKER_IMAGE_ROOT=$(DOCKER_IMAGE_ROOT) \
		--build-arg TAG=$(TAG) \
		--build-arg ARCH=$(ARCH) \
		--build-arg CROSS_COMPILE=$(CROSS_COMPILE) \
		--build-arg BUSYBOX_VERSION=$(BUSYBOX_VERSION) \
		--build-arg E2FSPROGS_VERSION=$(E2FSPROGS_VERSION) \
		--build-arg BUILD_TAG=$(BUILD_TAG) \
		--output type=local,dest=./$(OUT_DIR) .
	test -f $@

$(CLITOOLS_BIN): $(CLITOOLS_SRCS)
	$(MAKE) -C clitools build-agent

vpp: $(BUILD_BASE_STAMP) $(VPP_TAR) $(DPDK_TAR) $(FMLIB_TAR) $(FMC_TAR) $(NXP_TAR)
	@build_base_tag=$$(docker image inspect \
		--format '{{.Id}}' \
		$(DOCKER_IMAGE_ROOT)/build-base:$(TAG) \
		| cut -d':' -f2 \
		| cut -c -8); \
	@mkdir -p $(OUT_DIR)/vpp
	docker build \
		-f docker/vpp.Dockerfile \
		--build-arg APT_PROXY=$(APT_PROXY) \
		--build-arg DOCKER_IMAGE_ROOT=$(DOCKER_IMAGE_ROOT) \
		--build-arg TAG=$(TAG) \
		--build-arg BUILD_BASE_TAG=$$build_base_tag \
		--build-arg NXP_VERSION=$(NXP_VERSION) \
		--build-arg FMLIB_TAR=$(FMLIB_TAR) \
		--build-arg FMLIB_VERSION=$(FMLIB_VERSION) \
		--build-arg FMC_TAR=$(FMC_TAR) \
		--build-arg FMC_VERSION=$(FMC_VERSION) \
		--build-arg VPP_TAR=$(VPP_TAR) \
		--build-arg VPP_VERSION=$(VPP_VERSION) \
		--build-arg VPP_UPSTREAM_VERSION=$(VPP_UPSTREAM_VERSION) \
		--build-arg DPDK_TAR=$(DPDK_TAR) \
		--build-arg DPDK_VERSION=$(DPDK_VERSION) \
		-t $(DOCKER_IMAGE_ROOT)/vpp-source:$(TAG) . \
		# --output type=local,dest=./$(OUT_DIR) .
	docker buildx build --platform linux/arm64 \
		-f docker/vpp-container.Dockerfile \
		--build-arg APT_PROXY=$(APT_PROXY) \
		--build-arg DOCKER_IMAGE_ROOT=$(DOCKER_IMAGE_ROOT) \
		--build-arg TAG=$(TAG) \
		--build-arg VPP_VERSION=$(VPP_VERSION) \
		-t $(DOCKER_IMAGE_ROOT)/vpp:$(TAG) .

$(BOARD_ITB): $(ITB_DEPS) | $(OUT_DIR)
	docker build \
		-f docker/itb.Dockerfile \
		--build-arg DOCKER_IMAGE_ROOT=$(DOCKER_IMAGE_ROOT) \
		--build-arg TAG=$(TAG) \
		--build-arg ARCH=$(ARCH) \
		--build-arg DEVICE_TREE_TARGET=$(DEVICE_TREE_TARGET) \
		--output type=local,dest=./$(OUT_DIR) .
	test -f $@

$(RELEASE_IMAGE): $(RELEASE_DEPS) $(DOWNLOAD_PACKAGES_STAMP) | $(OUT_DIR)
	@build_base_tag=$$(docker image inspect \
		--format '{{.Id}}' \
		$(DOCKER_IMAGE_ROOT)/build-base:$(TAG) \
		| cut -d':' -f2 \
		| cut -c -8); \
	docker build \
		-f docker/alpine.Dockerfile \
		--no-cache \
		--build-arg DOCKER_IMAGE_ROOT=$(DOCKER_IMAGE_ROOT) \
		--build-arg TAG=$(TAG) \
		--build-arg ALPINE_ARCH=$(ALPINE_ARCH) \
		--build-arg ALPINE_VER=$(ALPINE_VER) \
		--build-arg KUBE_VERSION=$(KUBE_VERSION) \
		--build-arg CRIO_VERSION=$(CRIO_VERSION) \
		--build-arg DEVICE_TREE_TARGET=$(DEVICE_TREE_TARGET) \
		--build-arg BUILD_BASE_TAG=$$build_base_tag \
		-t $(DOCKER_IMAGE_ROOT)/buildenv-alpine:$(TAG) .

	@cid=$$(docker create \
		--privileged \
		-v /cache/apk:/var/cache/apk \
		-v /cache/k8s-images:/var/cache/k8s-images \
		-v /var/run/docker.sock:/var/run/docker.sock \
		--device=/dev/loop0:/dev/loop0 \
		-e ALPINE_HOSTNAME=$(ALPINE_HOSTNAME) \
		-e ARCH=$(ARCH) \
		-e BUILD_DATE=$(BUILD_DATE) \
		-e BUILD_GIT=$(BUILD_GIT) \
		-e BUILD_VERSION=$(BUILD_VERSION) \
		-e DEVICE_TREE_TARGET=$(DEVICE_TREE_TARGET) \
		-e KUBE_IMG_CACHE=/var/cache/k8s-images \
		-e KUBE_VERSION=$(KUBE_VERSION) \
		-e RELEASE_IMAGE=$(RELEASE_IMAGE) \
		-e PART_IMAGE=$(PART_IMAGE) \
		-e ROOTFS=/out/rootfs \
		-e TAG=$(TAG) \
		$(DOCKER_IMAGE_ROOT)/buildenv-alpine:$(TAG) \
		bash -lc '/build-rootfs.sh'); \
	docker start -a $$cid; \
	docker cp $$cid:/build/output.img.gz $@; \
	docker cp $$cid:/build/rootfs.ext4.zst $(PART_IMAGE); \
	docker cp $$cid:/build/catalog.txt $(CATALOG); \
	docker rm $$cid

	test -f $@


# ---- config targets ------------------------------------------------------------

cluster-config: $(CLUSTER_ENV_DEFAULT) $(SCRIPTS_DIR)/merge-env.sh | $(OUT_DIR)
	sh $(SCRIPTS_DIR)/merge-env.sh $(CLUSTER_ENV_DEFAULT) $(CLUSTER_ENV)

cluster-defconfig: $(CLUSTER_ENV_DEFAULT) | $(OUT_DIR)
	cp $(CLUSTER_ENV_DEFAULT) $(CLUSTER_ENV)

cluster-print:
	@cat $(CLUSTER_ENV)

# ---- User targets ------------------------------------------------------------

release: $(RELEASE_IMAGE)

kernel: $(KERNEL_IMAGE)
initramfs: $(INITRAMFS)
itb: $(BOARD_ITB)
build-base: $(BUILD_BASE_STAMP)
clitools: $(CLITOOLS_BIN)

clean:
	$(MAKE) -C clean clitools
	rm -f \
		$(BUILD_BASE_STAMP) \
		$(KERNEL_IMAGE) \
		$(INITRAMFS) \
		$(BOARD_ITB) \
		$(RELEASE_IMAGE) \
		$(CLITOOLS_BIN)

distclean: clean
	rm -rf $(OUT_DIR)

dockerclean:
	$(MAKE) -C clitools dockerclean
	@echo "Removing tagged images..."
	- docker rmi \
		$(DOCKER_IMAGE_ROOT)/build-base:$(TAG) \
		$(DOCKER_IMAGE_ROOT)/vpp-source:$(TAG) \
		$(DOCKER_IMAGE_ROOT)/vpp:$(TAG) \
		$(DOCKER_IMAGE_ROOT)/buildenv-alpine:$(TAG) \
		2>/dev/null || true

	@echo "Removing build-base IID tags..."
	@docker images --format '{{.Repository}}:{{.Tag}}' \
		| grep '^$(DOCKER_IMAGE_ROOT)/build-base:' \
		| grep -v ':$(TAG)$$' \
		| xargs -r docker rmi 2>/dev/null || true

	@echo "Removing dangling images..."
	- docker image prune -f

pkgclean:
	$(MAKE) -C clitools pkgclean
	rm -rf $(PACKAGES_DIR)

.PHONY: release kernel initramfs itb build-base clitools clean distclean pkgclean \
	vpp \
	cluster-config cluster-defconfig cluster-print
