Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 138 additions & 0 deletions .github/workflows/bootstrap-macos.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
name: bootstrap-macos

# One-shot workflow to produce the first macOS mcpp binary.
# Uses xmake + xlings LLVM to compile mcpp from source.
# Once a macOS binary exists, mcpp can self-host for future releases.

on:
workflow_dispatch:

jobs:
bootstrap:
name: Bootstrap mcpp (macOS ARM64)
runs-on: macos-15
timeout-minutes: 30
env:
XLINGS_NON_INTERACTIVE: '1'
XLINGS_VERSION: '0.4.30'
steps:
- uses: actions/checkout@v4

- name: System info
run: |
uname -a
sw_vers
xcrun --show-sdk-path

- name: Install xlings
run: |
WORK=$(mktemp -d)
tarball="xlings-${XLINGS_VERSION}-macosx-arm64.tar.gz"
curl -fsSL -o "${WORK}/${tarball}" \
"https://github.com/d2learn/xlings/releases/download/v${XLINGS_VERSION}/${tarball}"
tar -xzf "${WORK}/${tarball}" -C "${WORK}"
"${WORK}/xlings-${XLINGS_VERSION}-macosx-arm64/subos/default/bin/xlings" self install
echo "$HOME/.xlings/subos/default/bin" >> "$GITHUB_PATH"
echo "$HOME/.xlings/bin" >> "$GITHUB_PATH"

- name: Install LLVM + xmake
run: |
xlings install llvm -y || xlings install llvm@20.1.7 -y
brew install xmake
LLVM_ROOT=$(find "$HOME/.xlings" -path "*/xpkgs/xim-x-llvm/*/bin/clang++" | head -1 | xargs dirname | xargs dirname)
echo "LLVM_ROOT=$LLVM_ROOT" >> "$GITHUB_ENV"
"$LLVM_ROOT/bin/clang++" --version
xmake --version

- name: Build mcpp with xmake
run: |
# Generate xmake.lua if not present
if [ ! -f xmake.lua ]; then
cat > xmake.lua << 'EOF'
add_rules("mode.release")
set_languages("c++23")

package("cmdline")
set_homepage("https://github.com/mcpplibs/cmdline")
set_description("Modern C++ command-line parsing library")
set_license("Apache-2.0")
add_urls("https://github.com/mcpplibs/cmdline/archive/refs/tags/$(version).tar.gz")
add_versions("0.0.1", "3fb2f5495c1a144485b3cbb2e43e27059151633460f702af0f3851cbff387ef0")
on_install(function (package)
import("package.tools.xmake").install(package)
end)
package_end()

add_requires("cmdline 0.0.1")

target("mcpp")
set_kind("binary")
add_files("src/main.cpp")
add_files("src/**.cppm")
add_packages("cmdline")
add_includedirs("src/libs/json")
set_policy("build.c++.modules", true)
EOF
fi

# Configure with xlings LLVM
xmake f -y -m release --toolchain=llvm --sdk="$LLVM_ROOT"
# Build
xmake build -y mcpp

- name: Verify built binary
run: |
MCPP=$(find build -name mcpp -type f -perm +111 | head -1)
test -x "$MCPP"
file "$MCPP"
"$MCPP" --version
echo "MCPP=$MCPP" >> "$GITHUB_ENV"

- name: Package
id: package
run: |
VERSION=$(awk -F '"' '/^version[[:space:]]*=/{print $2; exit}' mcpp.toml)
TARBALL="mcpp-${VERSION}-darwin-arm64.tar.gz"
WRAPPER="mcpp-${VERSION}-darwin-arm64"

mkdir -p "dist/$WRAPPER/bin"
cp "$MCPP" "dist/$WRAPPER/bin/mcpp"
strip "dist/$WRAPPER/bin/mcpp" 2>/dev/null || true
cp LICENSE "dist/$WRAPPER/" 2>/dev/null || true
cp README.md "dist/$WRAPPER/" 2>/dev/null || true

cat > "dist/$WRAPPER/mcpp" << 'LAUNCHER'
#!/bin/sh
exec "$(dirname "$0")/bin/mcpp" "$@"
LAUNCHER
chmod +x "dist/$WRAPPER/mcpp"

# Bundle xlings
XLINGS_BIN="$HOME/.xlings/subos/default/bin/xlings"
if [ -x "$XLINGS_BIN" ]; then
mkdir -p "dist/$WRAPPER/registry/bin"
cp "$XLINGS_BIN" "dist/$WRAPPER/registry/bin/xlings"
fi

(cd dist && tar -czf "$TARBALL" "$WRAPPER")
(cd dist && shasum -a 256 "$TARBALL" > "$TARBALL.sha256")

echo "tarball=dist/$TARBALL" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
ls -la dist/

- name: Smoke test
run: |
SMOKE=$(mktemp -d)
tar -xzf "${{ steps.package.outputs.tarball }}" -C "$SMOKE"
VERSION="${{ steps.package.outputs.version }}"
"$SMOKE/mcpp-${VERSION}-darwin-arm64/bin/mcpp" --version
"$SMOKE/mcpp-${VERSION}-darwin-arm64/mcpp" --version

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: mcpp-darwin-arm64
path: |
dist/mcpp-*-darwin-arm64.tar.gz
dist/mcpp-*-darwin-arm64.tar.gz.sha256
21 changes: 21 additions & 0 deletions .github/workflows/ci-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -267,3 +267,24 @@ jobs:
Darwin-x86_64) echo "PASS: would select darwin-x86_64" ;;
*) echo "FAIL: unexpected platform"; exit 1 ;;
esac

- name: Install xmake (for bootstrap)
run: |
brew install xmake
xmake --version

- name: Bootstrap mcpp from source (xmake)
run: |
export LLVM_ROOT="$LLVM_ROOT"
bash scripts/bootstrap-macos.sh "$LLVM_ROOT"
./target/bootstrap/bin/mcpp --version

- name: Self-host (mcpp builds mcpp)
run: |
# Put bootstrapped mcpp on PATH so build.ninja can find it for dyndep
export PATH="$PWD/target/bootstrap/bin:$PATH"
mcpp build
SELFHOST=$(find target -path "*/bin/mcpp" -not -path "*/bootstrap/*" -not -path "*/build/*" | head -1)
test -x "$SELFHOST"
"$SELFHOST" --version
echo ":: Self-host successful!"
143 changes: 143 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -255,3 +255,146 @@ jobs:
dist/SHA256SUMS
dist/mcpp-${{ steps.stage.outputs.version }}.tar.gz
dist/mcpp.lua

build-macos:
name: build (macOS / ARM64)
runs-on: macos-15
needs: build-release
permissions:
contents: write
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Resolve tag
id: resolve
run: |
if [ "${{ github.event_name }}" = "push" ]; then
TAG="${{ github.ref_name }}"
elif [ -n "${{ github.event.inputs.tag }}" ]; then
TAG="${{ github.event.inputs.tag }}"
else
VER=$(awk -F '"' '/^version[[:space:]]*=/{print $2; exit}' mcpp.toml)
TAG="v$VER"
fi
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "version=${TAG#v}" >> "$GITHUB_OUTPUT"
if [ "${{ github.event_name }}" = "workflow_dispatch" ] \
&& git rev-parse --verify "refs/tags/$TAG" >/dev/null 2>&1; then
git checkout --detach "refs/tags/$TAG"
fi

- name: Cache xlings
uses: actions/cache@v4
with:
path: ~/.xlings
key: xlings-macos15-release-${{ hashFiles('.xlings.json') }}
restore-keys: |
xlings-macos15-release-
xlings-macos15-arm64-

- name: Bootstrap xlings + LLVM
env:
XLINGS_NON_INTERACTIVE: '1'
XLINGS_VERSION: '0.4.30'
run: |
if [ ! -x "$HOME/.xlings/subos/default/bin/xlings" ]; then
WORK=$(mktemp -d)
tarball="xlings-${XLINGS_VERSION}-macosx-arm64.tar.gz"
curl -fsSL -o "${WORK}/${tarball}" \
"https://github.com/d2learn/xlings/releases/download/v${XLINGS_VERSION}/${tarball}"
tar -xzf "${WORK}/${tarball}" -C "${WORK}"
"${WORK}/xlings-${XLINGS_VERSION}-macosx-arm64/subos/default/bin/xlings" self install
fi
export PATH="$HOME/.xlings/subos/default/bin:$PATH"
xlings --version
# Install LLVM
xlings install llvm -y || xlings install llvm@20.1.7 -y
LLVM_ROOT=$(find "$HOME/.xlings" -path "*/xpkgs/xim-x-llvm/*/bin/clang++" | head -1 | xargs dirname | xargs dirname)
echo "LLVM_ROOT=$LLVM_ROOT" >> "$GITHUB_ENV"

- name: Install xmake (for bootstrap)
run: brew install xmake

- name: Bootstrap-compile mcpp (xmake + LLVM)
run: |
export LLVM_ROOT="$LLVM_ROOT"
bash scripts/bootstrap-macos.sh "$LLVM_ROOT"
./target/bootstrap/bin/mcpp --version

- name: Self-host rebuild (mcpp builds mcpp)
run: |
# Put bootstrapped mcpp on PATH so build.ninja can find it for dyndep
export PATH="$PWD/target/bootstrap/bin:$PATH"
mcpp build
# Find the self-hosted binary
SELFHOST=$(find target -path "*/bin/mcpp" -not -path "*/bootstrap/*" -not -path "*/build/*" | head -1)
test -x "$SELFHOST"
"$SELFHOST" --version
echo "SELFHOST=$SELFHOST" >> "$GITHUB_ENV"

- name: Package macOS release
id: stage
run: |
VERSION="${{ steps.resolve.outputs.version }}"
TARBALL_NAME="mcpp-${VERSION}-darwin-arm64.tar.gz"
WRAPPER="mcpp-${VERSION}-darwin-arm64"

# Create release layout
STAGING=$(mktemp -d)
mkdir -p "$STAGING/$WRAPPER/bin"
cp "$SELFHOST" "$STAGING/$WRAPPER/bin/mcpp"
# Strip (Mach-O)
strip "$STAGING/$WRAPPER/bin/mcpp" 2>/dev/null || true
# Copy metadata
cp LICENSE "$STAGING/$WRAPPER/" 2>/dev/null || true
cp README.md "$STAGING/$WRAPPER/" 2>/dev/null || true

# Shell launcher (same as Linux)
cat > "$STAGING/$WRAPPER/mcpp" << 'LAUNCHER'
#!/bin/sh
exec "$(dirname "$0")/bin/mcpp" "$@"
LAUNCHER
chmod +x "$STAGING/$WRAPPER/mcpp"

# Bundle xlings for install.sh consumers
XLINGS_BIN="$HOME/.xlings/subos/default/bin/xlings"
if [ -x "$XLINGS_BIN" ]; then
mkdir -p "$STAGING/$WRAPPER/registry/bin"
cp "$XLINGS_BIN" "$STAGING/$WRAPPER/registry/bin/xlings"
chmod +x "$STAGING/$WRAPPER/registry/bin/xlings"
fi

# Create tarball
mkdir -p dist
(cd "$STAGING" && tar -czf "$GITHUB_WORKSPACE/dist/${TARBALL_NAME}" "$WRAPPER")
# Versionless alias
cp "dist/${TARBALL_NAME}" "dist/mcpp-darwin-arm64.tar.gz"
# SHA256
(cd dist && shasum -a 256 "${TARBALL_NAME}" > "${TARBALL_NAME}.sha256")
(cd dist && shasum -a 256 "mcpp-darwin-arm64.tar.gz" > "mcpp-darwin-arm64.tar.gz.sha256")

echo "tarball=${TARBALL_NAME}" >> "$GITHUB_OUTPUT"
ls -la dist/

- name: Smoke-test the tarball
run: |
VERSION="${{ steps.resolve.outputs.version }}"
TARBALL_NAME="${{ steps.stage.outputs.tarball }}"
WRAPPER="${TARBALL_NAME%.tar.gz}"
SMOKE=$(mktemp -d)
tar -xzf "dist/${TARBALL_NAME}" -C "$SMOKE"
"$SMOKE/$WRAPPER/bin/mcpp" --version
"$SMOKE/$WRAPPER/mcpp" --version | grep -q "$VERSION"

- name: Upload macOS artifacts to release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.resolve.outputs.tag }}
files: |
dist/mcpp-${{ steps.resolve.outputs.version }}-darwin-arm64.tar.gz
dist/mcpp-${{ steps.resolve.outputs.version }}-darwin-arm64.tar.gz.sha256
dist/mcpp-darwin-arm64.tar.gz
dist/mcpp-darwin-arm64.tar.gz.sha256
1 change: 1 addition & 0 deletions mcpp.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ include_dirs = ["src/libs/json"]

[toolchain]
default = "gcc@16.1.0"
macos = "llvm@20.1.7"

# Per-target overrides: `mcpp build --target x86_64-linux-musl` (or the
# four-segment form `x86_64-unknown-linux-musl`) picks musl-gcc 15.1 + full
Expand Down
Loading
Loading