Skip to content

Commit c026a7f

Browse files
RahulHereRahulHere
authored andcommitted
Fix cross-compilation for Linux ARM64 and macOS x64
Linux ARM64: - Use cross-compilation instead of QEMU emulation (much faster) - Add Dockerfile.linux-arm64-cross with aarch64-linux-gnu toolchain - Install libssl-dev:arm64 and zlib1g-dev:arm64 for cross-compilation - Remove QEMU setup from GitHub Actions (no longer needed) macOS x64: - Install x86_64 Homebrew and dependencies when cross-compiling on Apple Silicon - Point CMake to use /usr/local (x86_64) instead of /opt/homebrew (ARM64) - Add workflow step to install x86_64 OpenSSL and libevent
1 parent f5b5a7a commit c026a7f

4 files changed

Lines changed: 224 additions & 31 deletions

File tree

.github/workflows/build-all.yml

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ env:
3434

3535
jobs:
3636
# Linux builds (x64 and ARM64)
37+
# ARM64 uses cross-compilation (no QEMU emulation) for fast builds
3738
build-linux:
3839
name: Build Linux ${{ matrix.arch }}
3940
runs-on: ubuntu-latest
@@ -48,16 +49,6 @@ jobs:
4849
with:
4950
submodules: recursive
5051

51-
- name: Set up QEMU
52-
uses: docker/setup-qemu-action@v3
53-
with:
54-
platforms: linux/arm64
55-
56-
- name: Set up Docker Buildx
57-
uses: docker/setup-buildx-action@v3
58-
with:
59-
platforms: linux/amd64,linux/arm64
60-
6152
- name: Make build script executable
6253
run: chmod +x docker-mcp/build-linux-${{ matrix.arch }}-docker.sh
6354

@@ -149,6 +140,24 @@ jobs:
149140
with:
150141
submodules: recursive
151142

143+
- name: Install x86_64 dependencies (for cross-compilation)
144+
if: matrix.arch == 'x64'
145+
run: |
146+
echo "Installing x86_64 Homebrew and dependencies for cross-compilation..."
147+
# Install x86_64 Homebrew if not present
148+
if [ ! -f /usr/local/bin/brew ]; then
149+
echo "Installing x86_64 Homebrew..."
150+
arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" </dev/null
151+
fi
152+
# Install x86_64 dependencies
153+
echo "Installing x86_64 OpenSSL, libevent, and nghttp2..."
154+
arch -x86_64 /usr/local/bin/brew install openssl@3 libevent libnghttp2 || true
155+
# Verify installation
156+
echo "x86_64 library locations:"
157+
arch -x86_64 /usr/local/bin/brew --prefix openssl@3 || echo "/usr/local/opt/openssl@3"
158+
arch -x86_64 /usr/local/bin/brew --prefix libevent || echo "/usr/local/opt/libevent"
159+
arch -x86_64 /usr/local/bin/brew --prefix libnghttp2 || echo "/usr/local/opt/libnghttp2"
160+
152161
- name: Make build script executable
153162
run: chmod +x docker-mcp/build-mac-${{ matrix.arch }}.sh
154163

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
# Dockerfile for Linux ARM64 cross-compilation of libgopher-mcp
2+
# Uses x64 host with aarch64-linux-gnu cross-compiler for fast builds
3+
# No QEMU emulation needed - runs at native x64 speed
4+
FROM ubuntu:20.04
5+
6+
# Prevent interactive prompts during package installation
7+
ENV DEBIAN_FRONTEND=noninteractive
8+
9+
# Add ARM64 architecture for cross-compilation dependencies
10+
RUN dpkg --add-architecture arm64
11+
12+
# Configure repositories for cross-compilation
13+
# ARM64 packages come from ports.ubuntu.com, not archive.ubuntu.com
14+
RUN echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu focal main restricted universe multiverse" > /etc/apt/sources.list && \
15+
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu focal-updates main restricted universe multiverse" >> /etc/apt/sources.list && \
16+
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu focal-security main restricted universe multiverse" >> /etc/apt/sources.list && \
17+
echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports focal main restricted universe multiverse" >> /etc/apt/sources.list && \
18+
echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports focal-updates main restricted universe multiverse" >> /etc/apt/sources.list && \
19+
echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports focal-security main restricted universe multiverse" >> /etc/apt/sources.list
20+
21+
# Update package lists for both architectures
22+
RUN apt-get update
23+
24+
# Install build tools (native x64)
25+
RUN apt-get install -y \
26+
build-essential \
27+
cmake \
28+
pkg-config \
29+
git \
30+
gcc-aarch64-linux-gnu \
31+
g++-aarch64-linux-gnu \
32+
binutils-aarch64-linux-gnu
33+
34+
# Install ARM64 cross-compilation libraries
35+
RUN apt-get install -y \
36+
libssl-dev:arm64 \
37+
zlib1g-dev:arm64 \
38+
libevent-dev:arm64 \
39+
&& rm -rf /var/lib/apt/lists/*
40+
41+
WORKDIR /build
42+
43+
# Copy the entire project
44+
COPY . /build/
45+
46+
# Create CMake toolchain file for ARM64 cross-compilation
47+
# Note: Do NOT set CMAKE_SYSROOT on Ubuntu - ARM64 libs are in multiarch paths
48+
RUN printf '%s\n' \
49+
'set(CMAKE_SYSTEM_NAME Linux)' \
50+
'set(CMAKE_SYSTEM_PROCESSOR aarch64)' \
51+
'' \
52+
'# Cross-compiler settings' \
53+
'set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)' \
54+
'set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)' \
55+
'set(CMAKE_AR aarch64-linux-gnu-ar)' \
56+
'set(CMAKE_RANLIB aarch64-linux-gnu-ranlib)' \
57+
'set(CMAKE_STRIP aarch64-linux-gnu-strip)' \
58+
'' \
59+
'# Target environment - where to find ARM64 libraries' \
60+
'# Ubuntu uses multiarch paths, not a separate sysroot' \
61+
'set(CMAKE_FIND_ROOT_PATH /usr/aarch64-linux-gnu /usr/lib/aarch64-linux-gnu)' \
62+
'' \
63+
'# OpenSSL paths for ARM64' \
64+
'set(OPENSSL_ROOT_DIR /usr/lib/aarch64-linux-gnu)' \
65+
'set(OPENSSL_INCLUDE_DIR /usr/include)' \
66+
'set(OPENSSL_CRYPTO_LIBRARY /usr/lib/aarch64-linux-gnu/libcrypto.so)' \
67+
'set(OPENSSL_SSL_LIBRARY /usr/lib/aarch64-linux-gnu/libssl.so)' \
68+
'' \
69+
'# Search for programs in the build host directories' \
70+
'set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)' \
71+
'' \
72+
'# Search for libraries and headers in the target directories' \
73+
'set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)' \
74+
'set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH)' \
75+
'set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)' \
76+
'' \
77+
'# Position independent code for shared libraries' \
78+
'set(CMAKE_POSITION_INDEPENDENT_CODE ON)' \
79+
> /build/toolchain-aarch64.cmake
80+
81+
# Create build directory and build with cross-compilation
82+
RUN mkdir -p cmake-build && cd cmake-build && \
83+
PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig \
84+
PKG_CONFIG_LIBDIR=/usr/lib/aarch64-linux-gnu/pkgconfig \
85+
cmake -DCMAKE_BUILD_TYPE=Release \
86+
-DCMAKE_TOOLCHAIN_FILE=/build/toolchain-aarch64.cmake \
87+
-DCMAKE_CXX_STANDARD=17 \
88+
-DCMAKE_POSITION_INDEPENDENT_CODE=ON \
89+
-DBUILD_SHARED_LIBS=ON \
90+
-DBUILD_STATIC_LIBS=ON \
91+
-DBUILD_TESTS=OFF \
92+
-DBUILD_C_API=ON \
93+
-DBUILD_BINDINGS_EXAMPLES=OFF \
94+
-DBUILD_EXAMPLES=OFF \
95+
-DCMAKE_INSTALL_PREFIX=/build/install_prefix_dir \
96+
-DOPENSSL_ROOT_DIR=/usr \
97+
-DOPENSSL_INCLUDE_DIR=/usr/include \
98+
-DOPENSSL_CRYPTO_LIBRARY=/usr/lib/aarch64-linux-gnu/libcrypto.so \
99+
-DOPENSSL_SSL_LIBRARY=/usr/lib/aarch64-linux-gnu/libssl.so \
100+
/build && \
101+
make -j$(nproc) && \
102+
make install
103+
104+
# Create output directory and organize files
105+
RUN mkdir -p /output && \
106+
cp /build/install_prefix_dir/lib/libgopher-mcp*.so* /output/ 2>/dev/null || true && \
107+
cp /build/install_prefix_dir/lib/libgopher_mcp_c*.so* /output/ 2>/dev/null || true && \
108+
cp /build/install_prefix_dir/lib/libfmt*.so* /output/ 2>/dev/null || true && \
109+
cp /build/install_prefix_dir/lib/libllhttp*.so* /output/ 2>/dev/null || true && \
110+
cp -r /build/install_prefix_dir/include /output/ 2>/dev/null || true
111+
112+
# Build verification tool using cross-compiler
113+
RUN printf '%s\n' \
114+
'#include <stdio.h>' \
115+
'#include <dlfcn.h>' \
116+
'#include <stdlib.h>' \
117+
'' \
118+
'int main() {' \
119+
' printf("libgopher-mcp verification tool (Linux ARM64)\\n");' \
120+
' printf("==============================================\\n\\n");' \
121+
' void* handle = dlopen("./libgopher_mcp_c.so", RTLD_NOW);' \
122+
' if (!handle) {' \
123+
' printf("Note: C API library not found: %s\\n", dlerror());' \
124+
' handle = dlopen("./libgopher-mcp.so", RTLD_NOW);' \
125+
' if (!handle) {' \
126+
' printf("X Failed to load main library: %s\\n", dlerror());' \
127+
' return 1;' \
128+
' }' \
129+
' printf("OK Main library loaded successfully\\n");' \
130+
' } else {' \
131+
' printf("OK C API library loaded successfully\\n");' \
132+
' }' \
133+
' void* init_func = dlsym(handle, "mcp_init");' \
134+
' if (init_func) {' \
135+
' printf("OK mcp_init function found\\n");' \
136+
' } else {' \
137+
' printf("-- mcp_init function not found\\n");' \
138+
' }' \
139+
' void* cleanup_func = dlsym(handle, "mcp_cleanup");' \
140+
' if (cleanup_func) {' \
141+
' printf("OK mcp_cleanup function found\\n");' \
142+
' } else {' \
143+
' printf("-- mcp_cleanup function not found\\n");' \
144+
' }' \
145+
' dlclose(handle);' \
146+
' printf("\\nOK Verification complete\\n");' \
147+
' return 0;' \
148+
'}' > /tmp/verify_mcp.c && \
149+
aarch64-linux-gnu-gcc -o /output/verify_mcp /tmp/verify_mcp.c -ldl -O2
150+
151+
# Verify the output is actually ARM64
152+
RUN file /output/verify_mcp && \
153+
file /output/libgopher-mcp*.so* | head -1
154+
155+
# Default command to copy files to host
156+
CMD cp -r /output/* /host-output/ && \
157+
echo "ARM64 cross-compilation complete!" && \
158+
ls -la /output/

docker-mcp/build-linux-arm64-docker.sh

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
#!/bin/bash
22

33
# Cross-compile libgopher-mcp for Linux ARM64 using Docker
4-
# This script can run on any platform with Docker (macOS, Linux x64, Windows)
5-
# Uses Docker buildx for ARM64 emulation
4+
# Uses x64 container with aarch64-linux-gnu cross-compiler (fast, no QEMU emulation)
65

76
set -e
87

@@ -15,7 +14,7 @@ NC='\033[0m'
1514

1615
echo -e "${MAGENTA}========================================${NC}"
1716
echo -e "${MAGENTA}Building libgopher-mcp for Linux ARM64${NC}"
18-
echo -e "${MAGENTA}Using Docker for cross-platform build${NC}"
17+
echo -e "${MAGENTA}Using cross-compilation (no QEMU)${NC}"
1918
echo -e "${MAGENTA}========================================${NC}"
2019
echo ""
2120

@@ -30,28 +29,19 @@ if ! command -v docker &> /dev/null; then
3029
exit 1
3130
fi
3231

33-
# Check for buildx support
34-
if ! docker buildx version &> /dev/null; then
35-
echo -e "${RED}Error: Docker buildx is not available${NC}"
36-
echo "Please update Docker Desktop to a recent version"
37-
exit 1
38-
fi
39-
4032
# Clean and create output directory
4133
echo -e "${YELLOW}Cleaning previous builds...${NC}"
4234
rm -rf "$OUTPUT_DIR"
4335
mkdir -p "$OUTPUT_DIR"
4436

45-
echo -e "${YELLOW}Building ARM64 library using Docker...${NC}"
46-
echo "This may take several minutes on first run (downloading base image and dependencies)"
37+
echo -e "${YELLOW}Building ARM64 library using cross-compilation...${NC}"
38+
echo "This runs at native x64 speed (no QEMU emulation)"
4739
echo ""
4840

49-
# Build using Docker buildx with ARM64 platform
50-
docker buildx build \
51-
--platform linux/arm64 \
52-
--load \
53-
-t gopher-mcp:linux-arm64 \
54-
-f "$SCRIPT_DIR/Dockerfile.linux-arm64" \
41+
# Build using Docker with cross-compilation (no --platform flag needed)
42+
docker build \
43+
-t gopher-mcp:linux-arm64-cross \
44+
-f "$SCRIPT_DIR/Dockerfile.linux-arm64-cross" \
5545
"$PROJECT_ROOT"
5646

5747
if [ $? -ne 0 ]; then
@@ -62,11 +52,10 @@ fi
6252
echo ""
6353
echo -e "${YELLOW}Extracting built files...${NC}"
6454

65-
# Run container and copy files to host
55+
# Run container and copy files to host (no --platform flag needed)
6656
docker run --rm \
67-
--platform linux/arm64 \
6857
-v "$OUTPUT_DIR:/host-output" \
69-
gopher-mcp:linux-arm64
58+
gopher-mcp:linux-arm64-cross
7059

7160
# Check results
7261
if [ -f "$OUTPUT_DIR/libgopher-mcp.so" ] || [ -f "$OUTPUT_DIR/libgopher-mcp.so.0.1.0" ]; then

docker-mcp/build-mac-x64.sh

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,42 @@ cd "$BUILD_DIR"
4242
# Configure CMake with macOS-specific settings
4343
echo -e "${YELLOW}Configuring CMake for macOS x86_64...${NC}"
4444

45+
# When cross-compiling x64 on Apple Silicon, we need to use x86_64 libraries
46+
# Install x86_64 Homebrew and dependencies if needed
47+
CURRENT_ARCH=$(uname -m)
48+
X86_BREW="/usr/local/bin/brew"
49+
X86_PREFIX="/usr/local"
50+
51+
if [ "$CURRENT_ARCH" = "arm64" ]; then
52+
echo -e "${YELLOW}Cross-compiling x86_64 on Apple Silicon...${NC}"
53+
54+
# Check if x86_64 Homebrew exists, if not install it
55+
if [ ! -f "$X86_BREW" ]; then
56+
echo -e "${YELLOW}Installing x86_64 Homebrew...${NC}"
57+
arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
58+
fi
59+
60+
# Install x86_64 dependencies
61+
echo -e "${YELLOW}Installing x86_64 dependencies via Homebrew...${NC}"
62+
arch -x86_64 $X86_BREW install openssl@3 libevent libnghttp2 2>/dev/null || true
63+
64+
# Get paths to x86_64 libraries
65+
X86_OPENSSL_PREFIX=$(arch -x86_64 $X86_BREW --prefix openssl@3 2>/dev/null || echo "/usr/local/opt/openssl@3")
66+
X86_LIBEVENT_PREFIX=$(arch -x86_64 $X86_BREW --prefix libevent 2>/dev/null || echo "/usr/local/opt/libevent")
67+
X86_NGHTTP2_PREFIX=$(arch -x86_64 $X86_BREW --prefix libnghttp2 2>/dev/null || echo "/usr/local/opt/libnghttp2")
68+
69+
echo "Using x86_64 OpenSSL from: $X86_OPENSSL_PREFIX"
70+
echo "Using x86_64 libevent from: $X86_LIBEVENT_PREFIX"
71+
echo "Using x86_64 nghttp2 from: $X86_NGHTTP2_PREFIX"
72+
73+
# Set CMake flags to use x86_64 libraries and ignore ARM64 ones
74+
EXTRA_CMAKE_FLAGS="-DCMAKE_IGNORE_PATH=/opt/homebrew;/opt/homebrew/lib;/opt/homebrew/include"
75+
EXTRA_CMAKE_FLAGS="$EXTRA_CMAKE_FLAGS -DOPENSSL_ROOT_DIR=$X86_OPENSSL_PREFIX"
76+
EXTRA_CMAKE_FLAGS="$EXTRA_CMAKE_FLAGS -DCMAKE_PREFIX_PATH=$X86_PREFIX;$X86_OPENSSL_PREFIX;$X86_LIBEVENT_PREFIX;$X86_NGHTTP2_PREFIX"
77+
else
78+
EXTRA_CMAKE_FLAGS=""
79+
fi
80+
4581
cmake \
4682
-DCMAKE_BUILD_TYPE=Release \
4783
-DCMAKE_CXX_STANDARD=17 \
@@ -59,6 +95,7 @@ cmake \
5995
-DCMAKE_MACOSX_RPATH=ON \
6096
-DCMAKE_INSTALL_RPATH="@loader_path" \
6197
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
98+
${EXTRA_CMAKE_FLAGS} \
6299
"${PROJECT_ROOT}"
63100

64101
# Build the library

0 commit comments

Comments
 (0)