1- PYTHON_VERSION ?= 3.13.3
1+ # ==============================================================================
2+ # Project: Python Build System (Containerized)
3+ # Description: Builds Python artifacts via a multi-stage container process.
4+ # ==============================================================================
5+
6+ # --- Configuration & Defaults -------------------------------------------------
7+
8+ # Shell safety flags: fail on error, fail on pipe error, fail on unset variables
9+ SHELL := /bin/bash
10+ .SHELLFLAGS := -eu -o pipefail -c
11+
12+ # Verbosity control: use 'make V=1' to see full commands
13+ ifeq ($(origin V ) , undefined)
14+ Q := @
15+ else
16+ Q :=
17+ endif
18+
19+ # Versioning
20+ PYTHON_VERSION ?= 3.13.3
221ACTIONS_PYTHON_VERSIONS ?= 3.13.3-14344076652
3- POWERSHELL_VERSION ?= v7.5.2
22+ POWERSHELL_VERSION ?= v7.5.2
423POWERSHELL_NATIVE_VERSION ?= v7.4.0
5- ARCH ?= $(shell uname -m)
6- UBUNTU_VERSION ?= 24.04
7- BASE_IMAGE ?= powershell:ubuntu-$(UBUNTU_VERSION )
24+ UBUNTU_VERSION ?= 24.04
25+
26+ # System Architecture (Computed Once)
27+ ARCH := $(shell uname -m)
28+
29+ # Container Engine Detection
830CONTAINER_ENGINE := $(shell command -v podman 2>/dev/null || command -v docker)
931
1032# Fail fast if no container runtime is available
1133ifeq ($(strip $(CONTAINER_ENGINE ) ) ,)
12- $(error No container runtime found ( install `docker` or `podman` and ensure it's on PATH) )
34+ $(error No container runtime found. Please install `docker` or `podman`)
1335endif
1436
15- # Deterministic temporary container name used to `create` a stopped
16- # container so artifacts can be `cp`'d out. We build this name at Make
17- # expansion time to avoid shell-variable quoting/escaping issues inside
18- # multiline recipe lines.
19- TEMP_CONTAINER_NAME := python-build-$(PYTHON_VERSION ) -$(ARCH ) -tmp
37+ # --- Internal Variables -------------------------------------------------------
2038
21- # Output and artifact naming
39+ BASE_IMAGE := powershell:ubuntu-$(UBUNTU_VERSION )
40+
41+ # Naming conventions
2242OUTPUT_DIR := python-versions/output
2343IMAGE_NAME := python:$(PYTHON_VERSION ) -ubuntu-$(UBUNTU_VERSION ) -$(ARCH )
2444ARTIFACT_NAME := python-$(PYTHON_VERSION ) -linux-$(ARCH ) .tar.gz
25- CONTAINER_NAME_PREFIX := python-build
2645
27- .PHONY : all powershell clean help
46+ # Deterministic temporary container name for artifact extraction
47+ TEMP_CONTAINER_NAME := python-build-$(PYTHON_VERSION ) -$(ARCH ) -tmp
2848
29- ifeq ($(findstring podman,$(CONTAINER_ENGINE ) ) ,podman)
30- ifeq ($(shell uname),Linux)
31- VOLUME_FLAG := -v $(abspath ./python-versions/output):/tmp/artifact:z
32- else
33- VOLUME_FLAG := -v ./python-versions/output:/tmp/artifact:z
34- endif
35- else
36- VOLUME_FLAG := -v ./python-versions/output:/tmp/artifact
37- endif
49+ # Prerequisite Files for PowerShell Build
50+ PS_DIR := PowerShell
51+ PS_PREREQS := \
52+ $(PS_DIR ) /Dockerfile \
53+ $(PS_DIR ) /patch/powershell-native-$(POWERSHELL_NATIVE_VERSION ) .patch \
54+ $(PS_DIR ) /patch/powershell-$(ARCH ) -$(POWERSHELL_VERSION ) .patch \
55+ $(PS_DIR ) /patch/powershell-gen-$(POWERSHELL_VERSION ) .tar.gz
3856
39- all : python-versions/output/python-$(PYTHON_VERSION ) -linux-$(ARCH ) .tar.gz
57+ # --- Targets ------------------------------------------------------------------
58+
59+ .PHONY : all powershell clean help
4060
41- .PHONY : powershell
61+ all : $( OUTPUT_DIR ) / $( ARTIFACT_NAME )
4262
43- python-versions/output/python-$(PYTHON_VERSION ) -linux-$(ARCH ) .tar.gz : powershell
44- cd python-versions; \
45- mkdir -p $(abspath ../$(OUTPUT_DIR ) ) ; \
46- $(CONTAINER_ENGINE ) build \
63+ # 1. Build the Python Artifact
64+ # Depends on the PowerShell base image being ready.
65+ $(OUTPUT_DIR ) /$(ARTIFACT_NAME ) : powershell | $(OUTPUT_DIR )
66+ @echo " --- Building Python $( PYTHON_VERSION) Image ---"
67+ $(Q ) cd python-versions && $(CONTAINER_ENGINE ) build \
4768 --network=host \
4869 --build-arg PYTHON_VERSION=$(PYTHON_VERSION ) \
4970 --build-arg ACTIONS_PYTHON_VERSIONS=$(ACTIONS_PYTHON_VERSIONS ) \
5071 --build-arg UBUNTU_VERSION=$(UBUNTU_VERSION ) \
5172 --build-arg TARGETARCH=$(ARCH ) \
5273 --build-arg BASE_IMAGE=$(BASE_IMAGE ) \
53- -t $(IMAGE_NAME ) . || exit 1; \
54- #
55- # Use a deterministic temporary container name so we can reliably copy
56- # artifacts out of the image even if the runtime prints nothing (some
57- # container engines/modes may not return an id to backticks reliably).
58- # note: TEMP_CONTAINER_NAME is defined above at Makefile parse time.
59- # Ensure no stale container exists with the same name
60- $(CONTAINER_ENGINE) rm -f $(TEMP_CONTAINER_NAME) 2>/dev/null || true; \
61- $(CONTAINER_ENGINE) create --name $(TEMP_CONTAINER_NAME) $(IMAGE_NAME) >/dev/null || (echo "ERROR: failed to create container $(TEMP_CONTAINER_NAME)" && exit 1); \
62- $(CONTAINER_ENGINE) cp $(TEMP_CONTAINER_NAME):/tmp/artifact/$(ARTIFACT_NAME) $(abspath ../$(OUTPUT_DIR))/$(ARTIFACT_NAME) || (echo "ERROR: failed to copy artifact from $(TEMP_CONTAINER_NAME)" && $(CONTAINER_ENGINE) rm -f $(TEMP_CONTAINER_NAME) >/dev/null 2>&1 || true; exit 1); \
63- $(CONTAINER_ENGINE) cp $(TEMP_CONTAINER_NAME):/tmp/artifact/python-$(PYTHON_VERSION)-$(ARCH).sbom.json $(abspath ../$(OUTPUT_DIR))/python-$(PYTHON_VERSION)-linux-$(UBUNTU_VERSION)-$(ARCH).sbom.json || true; \
64- $(CONTAINER_ENGINE) cp $(TEMP_CONTAINER_NAME):/tmp/artifact/trivy-$(PYTHON_VERSION)-$(ARCH)-vuln.json $(abspath ../$(OUTPUT_DIR))/trivy-python-$(PYTHON_VERSION)-linux-$(UBUNTU_VERSION)-$(ARCH)-vuln.json || true; \
65- $(CONTAINER_ENGINE) cp $(TEMP_CONTAINER_NAME):/tmp/artifact/trivy-$(PYTHON_VERSION)-$(ARCH)-secret.json $(abspath ../$(OUTPUT_DIR))/trivy-python-$(PYTHON_VERSION)-linux-$(UBUNTU_VERSION)-$(ARCH)-secret.json || true; \
66- $(CONTAINER_ENGINE) rm -f $(TEMP_CONTAINER_NAME) >/dev/null 2>&1 || true
67-
68- powershell : PowerShell/Dockerfile \
69- PowerShell/patch/powershell-native-$(POWERSHELL_NATIVE_VERSION ) .patch \
70- PowerShell/patch/powershell-$(ARCH ) -$(POWERSHELL_VERSION ) .patch \
71- PowerShell/patch/powershell-gen-$(POWERSHELL_VERSION ) .tar.gz
72- cd PowerShell; \
73- $(CONTAINER_ENGINE ) build \
74+ -t $(IMAGE_NAME ) .
75+
76+ @echo " --- Extracting Artifacts ---"
77+ @# Ensure no stale container exists
78+ $(Q )$(CONTAINER_ENGINE ) rm -f $(TEMP_CONTAINER_NAME ) 2> /dev/null || true
79+
80+ @# Create stopped container for copying
81+ $(Q )$(CONTAINER_ENGINE ) create --name $(TEMP_CONTAINER_NAME ) $(IMAGE_NAME ) > /dev/null
82+
83+ @# Copy artifacts using strict paths
84+ $(Q )$(CONTAINER_ENGINE ) cp $(TEMP_CONTAINER_NAME ) :/tmp/artifact/$(ARTIFACT_NAME ) $(abspath $(OUTPUT_DIR ) ) /$(ARTIFACT_NAME )
85+ $(Q )$(CONTAINER_ENGINE ) cp $(TEMP_CONTAINER_NAME ) :/tmp/artifact/python-$(PYTHON_VERSION ) -$(ARCH ) .sbom.json \
86+ $(abspath $(OUTPUT_DIR ) ) /python-$(PYTHON_VERSION ) -linux-$(UBUNTU_VERSION ) -$(ARCH ) .sbom.json || echo " Warning: SBOM missing"
87+ $(Q )$(CONTAINER_ENGINE ) cp $(TEMP_CONTAINER_NAME ) :/tmp/artifact/trivy-$(PYTHON_VERSION ) -$(ARCH ) -vuln.json \
88+ $(abspath $(OUTPUT_DIR ) ) /trivy-python-$(PYTHON_VERSION ) -linux-$(UBUNTU_VERSION ) -$(ARCH ) -vuln.json || echo " Warning: Trivy Vuln report missing"
89+ $(Q )$(CONTAINER_ENGINE ) cp $(TEMP_CONTAINER_NAME ) :/tmp/artifact/trivy-$(PYTHON_VERSION ) -$(ARCH ) -secret.json \
90+ $(abspath $(OUTPUT_DIR ) ) /trivy-python-$(PYTHON_VERSION ) -linux-$(UBUNTU_VERSION ) -$(ARCH ) -secret.json || echo " Warning: Trivy Secret report missing"
91+
92+ @# Cleanup
93+ $(Q )$(CONTAINER_ENGINE ) rm -f $(TEMP_CONTAINER_NAME ) > /dev/null
94+ @echo " Build complete: $( OUTPUT_DIR) /$( ARTIFACT_NAME) "
95+
96+ # 2. Build the Base PowerShell Image
97+ powershell : $(PS_PREREQS )
98+ @echo " --- Building PowerShell Base Image ---"
99+ $(Q ) cd $(PS_DIR ) && $(CONTAINER_ENGINE ) build \
74100 --network=host \
75101 --build-arg POWERSHELL_VERSION=$(POWERSHELL_VERSION ) \
76102 --build-arg POWERSHELL_NATIVE_VERSION=$(POWERSHELL_NATIVE_VERSION ) \
77103 --build-arg UBUNTU_VERSION=$(UBUNTU_VERSION ) \
78104 --build-arg TARGETARCH=$(ARCH ) \
79105 --tag powershell:ubuntu-$(UBUNTU_VERSION ) .
80106
81- # Pattern rule to help diagnose missing patch files
82- PowerShell/patch/% .tar.gz :
83- @if [ ! -f " $@ " ]; then \
84- echo " Error: Required patch file $@ is missing." ; \
85- exit 1; \
86- fi
107+ # Order-only prerequisite: Create output directory if it doesn't exist
108+ $(OUTPUT_DIR ) :
109+ $(Q ) mkdir -p $@
87110
88- # Show a short help message describing common targets
89- help :
90- echo " Usage: make [target]"
91- echo " Common targets:"
92- echo " all (default) - build the Python artifact and export to $( OUTPUT_DIR) "
93- echo " powershell - build the PowerShell base image used as builder base"
94- echo " clean - remove generated artifacts and temporary container"
95- echo " help - show this message"
96-
97- # Remove artifacts and any temporary container created by this Makefile.
98- # This is conservative: it removes files under $(OUTPUT_DIR) and attempts
99- # to remove the temporary container name used by the recipe. It does NOT
100- # remove images by default to avoid surprising the user.
111+ # Diagnostic pattern rule for missing patches
112+ $(PS_DIR ) /patch/% .tar.gz :
113+ $(error Required patch file $@ is missing)
114+
115+ # Cleanup
101116clean :
102- echo " Removing artifacts in $( OUTPUT_DIR) ..."
103- rm -rf $(OUTPUT_DIR ) || true
104- echo " Attempting to remove temporary container $( TEMP_CONTAINER_NAME) if present..."
105- $(CONTAINER_ENGINE ) rm -f $(TEMP_CONTAINER_NAME ) > /dev/null 2>&1 || true
106- echo " Clean complete."
117+ @echo " Cleaning up artifacts and temporary containers..."
118+ $(Q ) rm -rf $(OUTPUT_DIR )
119+ $(Q )$(CONTAINER_ENGINE ) rm -f $(TEMP_CONTAINER_NAME ) 2> /dev/null || true
120+ @echo " Clean complete."
121+
122+ # Help
123+ help :
124+ @echo " Usage: make [target] [V=1]"
125+ @echo " "
126+ @echo " Targets:"
127+ @echo " all (default) Build Python artifact and export to $( OUTPUT_DIR) "
128+ @echo " powershell Build the PowerShell base image (builder dependency)"
129+ @echo " clean Remove artifacts and temporary containers"
130+ @echo " help Show this help message"
131+ @echo " "
132+ @echo " Variables:"
133+ @echo " PYTHON_VERSION = $( PYTHON_VERSION) "
134+ @echo " ARCH = $( ARCH) "
135+ @echo " ENGINE = $( CONTAINER_ENGINE) "
0 commit comments