Skip to content

Commit 599c423

Browse files
author
Fernando Korndorfer
committed
Initial commit
0 parents  commit 599c423

5 files changed

Lines changed: 721 additions & 0 deletions

File tree

Dockerfile

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
ARG PYTHON_VERSION=3.13
2+
FROM amazonlinux:2023
3+
4+
# Install base dependencies
5+
RUN dnf -y install git zip tar gzip gcc gcc-c++ cmake \
6+
wget openssl-devel bzip2-devel libffi-devel \
7+
zlib-devel xz-devel sqlite-devel readline-devel xz && \
8+
dnf clean all
9+
10+
# Accept Python version as build arg (supports 3.10, 3.11, 3.12, 3.13, 3.14)
11+
ARG PYTHON_VERSION
12+
13+
# Install Python - either from dnf or build from source
14+
RUN if dnf list available python${PYTHON_VERSION} 2>/dev/null; then \
15+
echo "Installing Python ${PYTHON_VERSION} from dnf..."; \
16+
dnf -y install python${PYTHON_VERSION} python${PYTHON_VERSION}-pip python${PYTHON_VERSION}-devel && \
17+
dnf clean all; \
18+
else \
19+
echo "Building Python ${PYTHON_VERSION} from source..."; \
20+
cd /tmp && \
21+
wget https://www.python.org/ftp/python/${PYTHON_VERSION}.0/Python-${PYTHON_VERSION}.0.tar.xz && \
22+
tar xf Python-${PYTHON_VERSION}.0.tar.xz && \
23+
cd Python-${PYTHON_VERSION}.0 && \
24+
./configure --enable-optimizations --with-ensurepip=install && \
25+
make -j$(nproc) && \
26+
make altinstall && \
27+
cd / && \
28+
rm -rf /tmp/Python-${PYTHON_VERSION}.0*; \
29+
fi
30+
31+
# Create symlink for easier access
32+
RUN if [ -f /usr/local/bin/python${PYTHON_VERSION} ]; then \
33+
ln -sf /usr/local/bin/python${PYTHON_VERSION} /usr/local/bin/python3; \
34+
ln -sf /usr/local/bin/pip${PYTHON_VERSION} /usr/local/bin/pip3; \
35+
elif [ -f /usr/bin/python${PYTHON_VERSION} ]; then \
36+
ln -sf /usr/bin/python${PYTHON_VERSION} /usr/local/bin/python3; \
37+
ln -sf /usr/bin/pip${PYTHON_VERSION} /usr/local/bin/pip3; \
38+
fi
39+
40+
# Upgrade pip (skip if pip not available as module)
41+
RUN python3 -m pip install --upgrade pip 2>/dev/null || \
42+
python3 -m ensurepip && python3 -m pip install --upgrade pip
43+
44+
ADD package.sh /
45+
RUN chmod +x /package.sh
46+
47+
ENTRYPOINT ["/package.sh"]

build-multiarch.sh

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
#!/bin/bash
2+
set -e
3+
4+
# Default values
5+
PYTHON_VERSION="3.14"
6+
IMAGE_NAME="lambda-zipper"
7+
REQUIREMENTS_FILE="requirements.txt"
8+
SINGLE_PACKAGE=""
9+
BUILD_AMD64=true
10+
BUILD_ARM64=true
11+
SINGLE_FILE=true
12+
13+
# Parse arguments
14+
while [[ $# -gt 0 ]]; do
15+
case $1 in
16+
--python)
17+
PYTHON_VERSION="$2"
18+
shift 2
19+
;;
20+
-r|--requirements)
21+
REQUIREMENTS_FILE="$2"
22+
shift 2
23+
;;
24+
--package)
25+
SINGLE_PACKAGE="$2"
26+
REQUIREMENTS_FILE=""
27+
shift 2
28+
;;
29+
--image)
30+
IMAGE_NAME="$2"
31+
shift 2
32+
;;
33+
--skip-x86|--skip-amd64)
34+
BUILD_AMD64=false
35+
shift
36+
;;
37+
--skip-arm64)
38+
BUILD_ARM64=false
39+
shift
40+
;;
41+
--individual)
42+
SINGLE_FILE=false
43+
shift
44+
;;
45+
--help)
46+
echo "Usage: ./build-multiarch.sh [OPTIONS]"
47+
echo ""
48+
echo "Default behavior:"
49+
echo " - Uses Python 3.14 (latest)"
50+
echo " - Reads from requirements.txt"
51+
echo " - Builds both x86_64 and arm64 architectures"
52+
echo " - Creates single combined archive per architecture"
53+
echo ""
54+
echo "Options:"
55+
echo " --python VERSION Python version to use (default: 3.14)"
56+
echo " Supported: 3.10, 3.11, 3.12, 3.13, 3.14"
57+
echo " -r, --requirements FILE Path to requirements.txt (default: requirements.txt)"
58+
echo " --package NAME Single package name (disables requirements.txt mode)"
59+
echo " --image NAME Docker image name (default: lambda-zipper)"
60+
echo " --skip-x86, --skip-amd64 Skip x86_64 build"
61+
echo " --skip-arm64 Skip arm64 build"
62+
echo " --individual Create individual archives per package (not combined)"
63+
echo " --help Show this help message"
64+
echo ""
65+
echo "Examples:"
66+
echo " ./build-multiarch.sh # Build with all defaults"
67+
echo " ./build-multiarch.sh --python 3.13 # Use Python 3.13"
68+
echo " ./build-multiarch.sh --skip-arm64 # Build only x86_64"
69+
echo " ./build-multiarch.sh --individual # Create separate archives per package"
70+
echo " ./build-multiarch.sh --package numpy # Build single package"
71+
exit 0
72+
;;
73+
*)
74+
echo "Unknown option: $1"
75+
echo "Use --help for usage information"
76+
exit 1
77+
;;
78+
esac
79+
done
80+
81+
# Validate Python version
82+
if [[ ! "$PYTHON_VERSION" =~ ^3\.(10|11|12|13|14)$ ]]; then
83+
echo "Error: Unsupported Python version: $PYTHON_VERSION"
84+
echo "Supported versions: 3.10, 3.11, 3.12, 3.13, 3.14"
85+
exit 1
86+
fi
87+
88+
# Validate architecture selection
89+
if [ "$BUILD_AMD64" = false ] && [ "$BUILD_ARM64" = false ]; then
90+
echo "Error: Cannot skip both architectures. At least one must be built."
91+
exit 1
92+
fi
93+
94+
# Check if requirements file or package is specified
95+
if [ -z "$SINGLE_PACKAGE" ]; then
96+
if [ ! -f "$REQUIREMENTS_FILE" ]; then
97+
echo "Error: Requirements file not found: $REQUIREMENTS_FILE"
98+
echo "Use --package for single package mode or provide a valid requirements.txt file"
99+
exit 1
100+
fi
101+
fi
102+
103+
104+
echo "==================================="
105+
echo "Lambda Package Builder"
106+
echo "==================================="
107+
echo "Python version: ${PYTHON_VERSION}"
108+
if [ -n "$REQUIREMENTS_FILE" ]; then
109+
echo "Requirements: ${REQUIREMENTS_FILE}"
110+
echo "Output mode: $([ "$SINGLE_FILE" = true ] && echo "Single combined archive per arch" || echo "Individual archives per package")"
111+
else
112+
echo "Package: ${SINGLE_PACKAGE}"
113+
fi
114+
echo "Architectures: $([ "$BUILD_AMD64" = true ] && echo -n "x86_64 " || echo -n "")$([ "$BUILD_ARM64" = true ] && echo -n "arm64" || echo -n "")"
115+
echo "==================================="
116+
echo ""
117+
118+
# Create output directory
119+
mkdir -p output
120+
121+
# Build and run for x86_64 (amd64)
122+
if [ "$BUILD_AMD64" = true ]; then
123+
echo "Building for x86_64 (amd64)..."
124+
docker buildx build \
125+
--platform linux/amd64 \
126+
--build-arg PYTHON_VERSION=${PYTHON_VERSION} \
127+
-t ${IMAGE_NAME}:amd64-py${PYTHON_VERSION} \
128+
--load \
129+
.
130+
131+
if [ -n "$REQUIREMENTS_FILE" ]; then
132+
if [ "$SINGLE_FILE" = true ]; then
133+
docker run --rm \
134+
-e SINGLE_FILE=true \
135+
-v "$(pwd)/${REQUIREMENTS_FILE}:/input/requirements.txt" \
136+
-v "$(pwd)/output:/package" \
137+
${IMAGE_NAME}:amd64-py${PYTHON_VERSION}
138+
else
139+
docker run --rm \
140+
-v "$(pwd)/${REQUIREMENTS_FILE}:/input/requirements.txt" \
141+
-v "$(pwd)/output:/package" \
142+
${IMAGE_NAME}:amd64-py${PYTHON_VERSION}
143+
fi
144+
else
145+
docker run --rm \
146+
-v "$(pwd)/output:/package" \
147+
${IMAGE_NAME}:amd64-py${PYTHON_VERSION} \
148+
${SINGLE_PACKAGE}
149+
fi
150+
echo ""
151+
fi
152+
153+
# Build and run for arm64 (aarch64)
154+
if [ "$BUILD_ARM64" = true ]; then
155+
echo "Building for arm64 (aarch64)..."
156+
docker buildx build \
157+
--platform linux/arm64 \
158+
--build-arg PYTHON_VERSION=${PYTHON_VERSION} \
159+
-t ${IMAGE_NAME}:arm64-py${PYTHON_VERSION} \
160+
--load \
161+
.
162+
163+
if [ -n "$REQUIREMENTS_FILE" ]; then
164+
if [ "$SINGLE_FILE" = true ]; then
165+
docker run --rm \
166+
-e SINGLE_FILE=true \
167+
-v "$(pwd)/${REQUIREMENTS_FILE}:/input/requirements.txt" \
168+
-v "$(pwd)/output:/package" \
169+
${IMAGE_NAME}:arm64-py${PYTHON_VERSION}
170+
else
171+
docker run --rm \
172+
-v "$(pwd)/${REQUIREMENTS_FILE}:/input/requirements.txt" \
173+
-v "$(pwd)/output:/package" \
174+
${IMAGE_NAME}:arm64-py${PYTHON_VERSION}
175+
fi
176+
else
177+
docker run --rm \
178+
-v "$(pwd)/output:/package" \
179+
${IMAGE_NAME}:arm64-py${PYTHON_VERSION} \
180+
${SINGLE_PACKAGE}
181+
fi
182+
echo ""
183+
fi
184+
185+
echo ""
186+
echo "==================================="
187+
echo "Build completed successfully!"
188+
echo "Output files in: ./output/"
189+
ls -lh output/
190+
echo "==================================="

package.sh

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#!/bin/bash
2+
set -e
3+
4+
ARCH=$(uname -m)
5+
PYTHON_VERSION=$(python3 --version | awk '{print $2}' | cut -d. -f1,2)
6+
7+
echo "Building packages for Python ${PYTHON_VERSION} on ${ARCH}"
8+
9+
# Check if requirements.txt is provided
10+
if [ -f "/input/requirements.txt" ]; then
11+
12+
# Check if SINGLE_FILE mode is enabled
13+
if [ "${SINGLE_FILE}" = "true" ]; then
14+
echo "Processing requirements.txt - creating single combined archive"
15+
16+
# Create temp directory for all packages
17+
mkdir -p /temp
18+
19+
# Install all packages at once
20+
echo "Installing all packages from requirements.txt..."
21+
python3 -m pip install -r /input/requirements.txt -t /temp --no-cache-dir
22+
23+
# Create combined archive with Lambda Layer structure
24+
OUTPUT_FILE="/package/combined-python${PYTHON_VERSION}-${ARCH}.zip"
25+
cd /temp
26+
27+
# Create python/lib/pythonX.Y/site-packages structure for Lambda layers
28+
mkdir -p python/lib/python${PYTHON_VERSION}/site-packages
29+
mv * python/lib/python${PYTHON_VERSION}/site-packages/ 2>/dev/null || true
30+
31+
zip -r -q "${OUTPUT_FILE}" python/
32+
echo "✓ Created: ${OUTPUT_FILE}"
33+
34+
# Cleanup
35+
cd /
36+
rm -rf /temp
37+
38+
echo ""
39+
echo "========================================="
40+
echo "Combined package built successfully!"
41+
echo "========================================="
42+
else
43+
echo "Processing requirements.txt - creating individual archives for each package"
44+
45+
# Read each line from requirements.txt
46+
while IFS= read -r line || [ -n "$line" ]; do
47+
# Skip empty lines and comments
48+
[[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue
49+
50+
# Extract package name (remove version specifiers)
51+
PACKAGE=$(echo "$line" | sed 's/[<>=!].*//' | tr -d ' ')
52+
53+
echo ""
54+
echo "========================================="
55+
echo "Building package: ${PACKAGE}"
56+
echo "========================================="
57+
58+
# Create temp directory for this package
59+
mkdir -p /temp
60+
61+
# Install the package
62+
python3 -m pip install "$line" -t /temp --no-cache-dir
63+
64+
# Create archive with Lambda Layer structure
65+
OUTPUT_FILE="/package/${PACKAGE}-python${PYTHON_VERSION}-${ARCH}.zip"
66+
cd /temp
67+
68+
# Create python/lib/pythonX.Y/site-packages structure for Lambda layers
69+
mkdir -p python/lib/python${PYTHON_VERSION}/site-packages
70+
mv * python/lib/python${PYTHON_VERSION}/site-packages/ 2>/dev/null || true
71+
72+
zip -r -q "${OUTPUT_FILE}" python/
73+
echo "✓ Created: ${OUTPUT_FILE}"
74+
75+
# Cleanup for next package
76+
cd /
77+
rm -rf /temp
78+
79+
done < /input/requirements.txt
80+
81+
echo ""
82+
echo "========================================="
83+
echo "All packages built successfully!"
84+
echo "========================================="
85+
fi
86+
else
87+
# Single package mode (backward compatibility)
88+
if [ -z "$1" ]; then
89+
echo "Usage: docker run --rm -v \$(pwd):/package lambdazipper PACKAGE_NAME"
90+
echo " or: docker run --rm -v \$(pwd)/requirements.txt:/input/requirements.txt -v \$(pwd):/package lambdazipper"
91+
exit 1
92+
fi
93+
94+
PACKAGE=$1
95+
echo "Building single package: ${PACKAGE}"
96+
97+
mkdir -p /temp
98+
python3 -m pip install ${PACKAGE} -t /temp --no-cache-dir
99+
100+
OUTPUT_FILE="/package/${PACKAGE}-python${PYTHON_VERSION}-${ARCH}.zip"
101+
cd /temp
102+
103+
# Create python/lib/pythonX.Y/site-packages structure
104+
mkdir -p python/lib/python${PYTHON_VERSION}/site-packages
105+
mv * python/lib/python${PYTHON_VERSION}/site-packages/ 2>/dev/null || true
106+
107+
zip -r -q "${OUTPUT_FILE}" python/
108+
echo "✓ Created: ${OUTPUT_FILE}"
109+
110+
# Cleanup
111+
rm -rf /temp
112+
fi

0 commit comments

Comments
 (0)