# ***
# WARNING: Do not EDIT or MERGE this file, it is generated by packagespec.
# ***
# build.mk builds the packages defined in packages.lock, first building all necessary
# builder images.
#
# NOTE: This file should always run as though it were in the repo root, so all paths
# are relative to the repo root.

# Include config.mk relative to repo root.
include $(shell git rev-parse --show-toplevel)/packages*.lock/config.mk

ifeq ($(PACKAGE_SPEC_ID),)
$(error You must set PACKAGE_SPEC_ID; 'make build' does this for you.)
endif

ifneq ($(PRODUCT_VERSION),)
$(error You cannot set PRODUCT_VERSION for local builds, did you mean PRODUCT_REVISION?)
endif

# PACKAGES_ROOT holds the package store, as well as other package aliases.
PACKAGES_ROOT := $(CACHE_ROOT)/packages
# PACKAGE_STORE is where we store all the package files themselves
# addressed by their input hashes.
PACKAGE_STORE := $(PACKAGES_ROOT)/store
# BY_ALIAS is where we store alias symlinks to the store.
BY_ALIAS      := $(PACKAGES_ROOT)/by-alias

# Include the layers driver.
include $(LOCKDIR)/layer.mk

# GET_IMAGE_MARKER_FILE gets the name of the Docker image marker file
# for the named build layer.
GET_IMAGE_MARKER_FILE = $($(1)_IMAGE)
# GET_IMAGE_NAME gets the Docker image name of the build layer.
GET_IMAGE_NAME = $($(1)_IMAGE_NAME)

# Determine the top-level build layer.
BUILD_LAYER_NAME      := $(shell $(call QUERY_PACKAGESPEC,.meta.builtin.BUILD_LAYERS[0].name))
BUILD_LAYER_IMAGE      = $(call GET_IMAGE_MARKER_FILE,$(BUILD_LAYER_NAME))
BUILD_LAYER_IMAGE_NAME = $(call GET_IMAGE_NAME,$(BUILD_LAYER_NAME))

BUILD_COMMAND := $(shell $(call QUERY_PACKAGESPEC,.["build-command"]))
BUILD_ENV     := $(shell $(call QUERY_PACKAGESPEC,.inputs | to_entries[] | "\(.key)=\(.value)"))
ALIASES       := $(shell $(call QUERY_PACKAGESPEC,.aliases[] | "\(.type)/\(.path)"))
ALIASES       := $(addprefix $(BY_ALIAS)/,$(ALIASES))

ifeq ($(BUILD_COMMAND),)
$(error Unable to find build command for package spec ID $(PACKAGE_SPEC_ID))
endif
ifeq ($(BUILD_ENV),)
$(error Unable to find build inputs for package spec ID $(PACKAGE_SPEC_ID))
endif

# Configure paths and filenames.
OUTPUT_DIR := $(PACKAGE_STORE)
_ := $(shell mkdir -p $(OUTPUT_DIR))
# PACKAGE_NAME is the input-addressed name of the package.
PACKAGE_NAME := $(PACKAGE_SOURCE_ID)-$(PACKAGE_SPEC_ID)
PACKAGE_ZIP_NAME := $(PACKAGE_NAME).zip
PACKAGE := $(OUTPUT_DIR)/$(PACKAGE_ZIP_NAME)
# PACKAGE_CONTENTS is used when a built package needs to be unzipped to examine
# its contents. It is a path to a directory where these contents will be unzipped
# to. This is not needed to produce builds, but is useful for post-build tasks
# when the package contents need to be checked.
PACKAGE_CONTENTS := $(PACKAGE)_contents
META_JSON_NAME := $(PACKAGE_ZIP_NAME).meta.json
META := $(OUTPUT_DIR)/$(META_JSON_NAME)

# In the container, place the output dir at root. This makes 'docker cp' easier.
CONTAINER_OUTPUT_DIR := /$(OUTPUT_DIR)

FULL_BUILD_COMMAND := export $(BUILD_ENV) && mkdir -p $(CONTAINER_OUTPUT_DIR) && $(BUILD_COMMAND)

### Docker run command configuration.

DOCKER_SHELL := /bin/bash -euo pipefail -c

DOCKER_RUN_ENV_FLAGS := \
	-e PACKAGE_SOURCE_ID=$(PACKAGE_SOURCE_ID) \
	-e OUTPUT_DIR=$(CONTAINER_OUTPUT_DIR) \
	-e PACKAGE_ZIP_NAME=$(PACKAGE_ZIP_NAME)

BUILD_CONTAINER_NAME := build-$(PACKAGE_SPEC_ID)-$(PACKAGE_SOURCE_ID)
DOCKER_RUN_FLAGS := $(DOCKER_RUN_ENV_FLAGS) --name $(BUILD_CONTAINER_NAME)
# DOCKER_RUN_COMMAND ties everything together to build the final package as a
# single docker run invocation.
DOCKER_RUN_COMMAND = docker run $(DOCKER_RUN_FLAGS) $(BUILD_LAYER_IMAGE_NAME) $(DOCKER_SHELL) '$(FULL_BUILD_COMMAND)'
# DOCKER_CP_COMMAND copies the built artefact from the build container.
DOCKER_CP_COMMAND = docker cp $(BUILD_CONTAINER_NAME):$(CONTAINER_OUTPUT_DIR)/$(PACKAGE_ZIP_NAME) $(PACKAGE)

# package builds the package according to the set PACKAGE_SPEC_ID and PRODUCT_REVISION.
.PHONY: package
package: $(ALIASES)
	@echo $(PACKAGE)

# package-contents builds the package according to PACKAGE_SPEC_ID and PRODUCT_REVISION,
# and then extracts the zip file into an adjacent directory.
.PHONY: package-contents
package-contents: $(PACKAGE_CONTENTS)
	@echo "$(PACKAGE_CONTENTS)/"

# copy-package-contents allows copying the contents of a package to a specific
# directory. You must set PACKAGE_CONTENTS_DEST_DIR to this directory.
# This is useful for implementing a top-level make target that places your
# build artifacts in a well-known location.
.PHONY: copy-package-contents
copy-package-contents: $(PACKAGE_CONTENTS)
	@[ -n "$(PACKAGE_CONTENTS_DEST_DIR)" ] || { \
		echo "==> ERROR: Must set PACKAGE_CONTENTS_DEST_DIR"; \
		exit 1; \
	}; \
	mkdir -p "$(PACKAGE_CONTENTS_DEST_DIR)"; \
	cp -r "$(PACKAGE_CONTENTS)"/* "$(PACKAGE_CONTENTS_DEST_DIR)"

.PHONY: package-meta
package-meta: $(META)
	@echo $(META)

$(META): $(LOCK)
	@$(call QUERY_PACKAGESPEC,.) > $@

# PACKAGE builds the package.
$(PACKAGE): $(BUILD_LAYER_IMAGE)
	@mkdir -p $$(dirname $@)
	@echo "==> Building package: $@"
	@echo "PACKAGE_SOURCE_ID: $(PACKAGE_SOURCE_ID)"
	@echo "PACKAGE_SPEC_ID:   $(PACKAGE_SPEC_ID)"
	@# Print alias info.
	@$(call QUERY_PACKAGESPEC,.aliases[] | "alias type:\(.type) path:\(.path)") | column -t
	@docker rm -f $(BUILD_CONTAINER_NAME) > /dev/null 2>&1 || true # Speculative cleanup.
	$(DOCKER_RUN_COMMAND)
	$(DOCKER_CP_COMMAND)
	@docker rm -f $(BUILD_CONTAINER_NAME)

$(PACKAGE_CONTENTS): $(PACKAGE)
	@mkdir -p "$@" && unzip "$<" -d "$@"

# ALIASES writes the package alias links.
# ALIASES must be phony to ensure they are updated to point to the
# latest builds.
.PHONY: $(ALIASES)
$(ALIASES): $(PACKAGE)
	@mkdir -p $(dir $@)
	@$(LN) -rfs $(PACKAGE) $@
	@echo "==> Package alias written: $@"
