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
31 changes: 28 additions & 3 deletions .github/workflows/scripts-ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
pull_request:
paths:
- 'scripts/**'
- '.github/workflows/scripts-ios.yml'
- 'BUILDING.md'
push:
branches: [ master ]
Expand All @@ -13,19 +14,43 @@ on:
jobs:
build-ios:
runs-on: macos-15 # pinning macos-15 avoids surprises during the cutover window
timeout-minutes: 25 # hard-stop the job after 25 minutes
timeout-minutes: 60 # allow enough time for dependency installs and full build
concurrency: # ensure only one mac build runs at once
group: mac-ci
cancel-in-progress: false # queue new ones instead of canceling in-flight

steps:
- uses: actions/checkout@v4

- name: Ensure CocoaPods tooling
run: |
set -euo pipefail
if ! command -v pod >/dev/null 2>&1; then
sudo gem install cocoapods --no-document
fi
if ! ruby -rrubygems -e "exit(Gem::Specification::find_all_by_name('xcodeproj').empty? ? 1 : 0)"; then
sudo gem install xcodeproj --no-document
fi
pod --version

- name: Restore cn1-binaries cache
uses: actions/cache@v4
with:
path: ../cn1-binaries
key: cn1-binaries-${{ runner.os }}-${{ hashFiles('scripts/setup-workspace.sh') }}
restore-keys: |
cn1-binaries-${{ runner.os }}-

- name: Setup workspace
run: ./scripts/setup-workspace.sh -q -DskipTests
# per-step timeout
timeout-minutes: 15
timeout-minutes: 25

- name: Build iOS port
run: ./scripts/build-ios-port.sh -q -DskipTests
timeout-minutes: 15
timeout-minutes: 25

- name: Build sample iOS app and compile workspace
run: ./scripts/build-ios-app.sh -q -DskipTests
timeout-minutes: 30

233 changes: 233 additions & 0 deletions scripts/build-ios-app.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
#!/usr/bin/env bash
# Build a sample "Hello Codename One" iOS application using the locally-built Codename One iOS port
set -euo pipefail

bia_log() { echo "[build-ios-app] $1"; }

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$REPO_ROOT"

TMPDIR="${TMPDIR:-/tmp}"; TMPDIR="${TMPDIR%/}"
DOWNLOAD_DIR="${TMPDIR%/}/codenameone-tools"
ENV_DIR="$DOWNLOAD_DIR/tools"
EXTRA_MVN_ARGS=("$@")

ENV_FILE="$ENV_DIR/env.sh"
bia_log "Loading workspace environment from $ENV_FILE"
if [ -f "$ENV_FILE" ]; then
# shellcheck disable=SC1090
source "$ENV_FILE"
bia_log "Loaded environment: JAVA_HOME=${JAVA_HOME:-<unset>} JAVA17_HOME=${JAVA17_HOME:-<unset>} MAVEN_HOME=${MAVEN_HOME:-<unset>}"
else
bia_log "Workspace tools not found. Run scripts/setup-workspace.sh before this script." >&2
exit 1
fi

# --- Tool validations ---
if [ -z "${JAVA_HOME:-}" ] || [ ! -x "$JAVA_HOME/bin/java" ]; then
bia_log "JAVA_HOME is not set correctly. Please run scripts/setup-workspace.sh first." >&2
exit 1
fi
if [ -z "${JAVA17_HOME:-}" ] || [ ! -x "$JAVA17_HOME/bin/java" ]; then
bia_log "JAVA17_HOME is not set correctly. Please run scripts/setup-workspace.sh first." >&2
exit 1
fi
if [ -z "${MAVEN_HOME:-}" ] || [ ! -x "$MAVEN_HOME/bin/mvn" ]; then
bia_log "Maven is not available. Please run scripts/setup-workspace.sh first." >&2
exit 1
fi
if ! command -v xcodebuild >/dev/null 2>&1; then
bia_log "xcodebuild not found. Install Xcode command-line tools." >&2
exit 1
fi
if ! command -v pod >/dev/null 2>&1; then
bia_log "CocoaPods (pod) command not found. Install cocoapods before running this script." >&2
exit 1
fi

export PATH="$JAVA_HOME/bin:$MAVEN_HOME/bin:$PATH"

bia_log "Using JAVA_HOME at $JAVA_HOME"
bia_log "Using JAVA17_HOME at $JAVA17_HOME"
bia_log "Using Maven installation at $MAVEN_HOME"
bia_log "Using CocoaPods version $(pod --version 2>/dev/null || echo '<unknown>')"

CN1_VERSION=$(awk -F'[<>]' '/<version>/{print $3; exit}' maven/pom.xml)
bia_log "Detected Codename One version $CN1_VERSION"

WORK_DIR="$TMPDIR/cn1-hello-ios"
rm -rf "$WORK_DIR"; mkdir -p "$WORK_DIR"

GROUP_ID="com.codenameone.examples"
ARTIFACT_ID="hello-codenameone-ios"
MAIN_NAME="HelloCodenameOne"
PACKAGE_NAME="$GROUP_ID"

SOURCE_PROJECT="$REPO_ROOT/Samples/SampleProjectTemplate"
if [ ! -d "$SOURCE_PROJECT" ]; then
bia_log "Source project template not found at $SOURCE_PROJECT" >&2
exit 1
fi
bia_log "Using source project template at $SOURCE_PROJECT"

LOCAL_MAVEN_REPO="${LOCAL_MAVEN_REPO:-$HOME/.m2/repository}"
bia_log "Using local Maven repository at $LOCAL_MAVEN_REPO"
mkdir -p "$LOCAL_MAVEN_REPO"
MAVEN_CMD=(
"$MAVEN_HOME/bin/mvn" -B -ntp
-Dmaven.repo.local="$LOCAL_MAVEN_REPO"
-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn
)

# --- Generate app skeleton ---
bia_log "Generating Codename One application skeleton via codenameone-maven-plugin"
(
cd "$WORK_DIR"
"${MAVEN_CMD[@]}" -q \
com.codenameone:codenameone-maven-plugin:7.0.204:generate-app-project \
-DgroupId="$GROUP_ID" \
-DartifactId="$ARTIFACT_ID" \
-Dversion=1.0-SNAPSHOT \
-DsourceProject="$SOURCE_PROJECT" \
-Dcn1Version="$CN1_VERSION" \
"${EXTRA_MVN_ARGS[@]}"
)

APP_DIR="$WORK_DIR/$ARTIFACT_ID"
[ -d "$APP_DIR" ] || { bia_log "Failed to create Codename One application project" >&2; exit 1; }
[ -f "$APP_DIR/build.sh" ] && chmod +x "$APP_DIR/build.sh"

SETTINGS_FILE="$APP_DIR/common/codenameone_settings.properties"
if [ ! -f "$SETTINGS_FILE" ]; then
bia_log "codenameone_settings.properties not found at $SETTINGS_FILE" >&2
exit 1
fi

set_property() {
local key="$1" value="$2"
if grep -q "^${key}=" "$SETTINGS_FILE"; then
if sed --version >/dev/null 2>&1; then
sed -i -E "s|^${key}=.*$|${key}=${value}|" "$SETTINGS_FILE"
else
sed -i '' -E "s|^${key}=.*$|${key}=${value}|" "$SETTINGS_FILE"
fi
else
printf '\n%s=%s\n' "$key" "$value" >> "$SETTINGS_FILE"
fi
}

set_property "codename1.packageName" "$PACKAGE_NAME"
set_property "codename1.mainName" "$MAIN_NAME"

# Ensure trailing newline
tail -c1 "$SETTINGS_FILE" | read -r _ || echo >> "$SETTINGS_FILE"

PACKAGE_PATH="${PACKAGE_NAME//.//}"
JAVA_DIR="$APP_DIR/common/src/main/java/${PACKAGE_PATH}"
mkdir -p "$JAVA_DIR"
MAIN_FILE="$JAVA_DIR/${MAIN_NAME}.java"
TEMPLATE="$SCRIPT_DIR/templates/HelloCodenameOne.java.tmpl"
if [ ! -f "$TEMPLATE" ]; then
bia_log "Template not found: $TEMPLATE" >&2
exit 1
fi

sed -e "s|@PACKAGE@|$PACKAGE_NAME|g" \
-e "s|@MAIN_NAME@|$MAIN_NAME|g" \
"$TEMPLATE" > "$MAIN_FILE"

bia_log "Wrote main application class to $MAIN_FILE"

# --- Build iOS project ---
DERIVED_DATA_DIR="${TMPDIR}/codenameone-ios-derived"
rm -rf "$DERIVED_DATA_DIR"
mkdir -p "$DERIVED_DATA_DIR"

bia_log "Building iOS Xcode project using Codename One port"
"${MAVEN_CMD[@]}" -q -f "$APP_DIR/pom.xml" package \
-DskipTests \
-Dcodename1.platform=ios \
-Dcodename1.buildTarget=ios-source \
-Dopen=false \
-Dcodenameone.version="$CN1_VERSION" \
"${EXTRA_MVN_ARGS[@]}"

IOS_TARGET_DIR="$APP_DIR/ios/target"
if [ ! -d "$IOS_TARGET_DIR" ]; then
bia_log "iOS target directory not found at $IOS_TARGET_DIR" >&2
exit 1
fi

PROJECT_DIR=""
for candidate in "$IOS_TARGET_DIR"/*-ios-source; do
if [ -d "$candidate" ]; then
PROJECT_DIR="$candidate"
break
fi
done
if [ -z "$PROJECT_DIR" ]; then
bia_log "Failed to locate generated iOS project under $IOS_TARGET_DIR" >&2
find "$IOS_TARGET_DIR" -type d -print >&2 || true
exit 1
fi

bia_log "Found generated iOS project at $PROJECT_DIR"

if [ -f "$PROJECT_DIR/Podfile" ]; then
bia_log "Installing CocoaPods dependencies"
(
cd "$PROJECT_DIR"
if ! pod install --repo-update; then
bia_log "pod install --repo-update failed; retrying without repo update"
pod install
fi
)
else
bia_log "Podfile not found in generated project; skipping pod install"
fi

WORKSPACE=""
for candidate in "$PROJECT_DIR"/*.xcworkspace; do
if [ -d "$candidate" ]; then
WORKSPACE="$candidate"
break
fi
done
if [ -z "$WORKSPACE" ]; then
bia_log "Failed to locate xcworkspace in $PROJECT_DIR" >&2
ls "$PROJECT_DIR" >&2 || true
exit 1
fi

bia_log "Building workspace $WORKSPACE with scheme $MAIN_NAME"
(
cd "$PROJECT_DIR"
xcodebuild \
-workspace "$WORKSPACE" \
-scheme "$MAIN_NAME" \
-sdk iphonesimulator \
-configuration Debug \
-destination 'generic/platform=iOS Simulator' \
-derivedDataPath "$DERIVED_DATA_DIR" \
CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO \
build
)

PRODUCT_APP=""
while IFS= read -r app_path; do
PRODUCT_APP="$app_path"
break
done < <(find "$DERIVED_DATA_DIR" -type d -name '*.app' -print 2>/dev/null)
if [ -n "$PRODUCT_APP" ]; then
bia_log "Successfully built iOS simulator app at $PRODUCT_APP"
fi

if [ -n "${GITHUB_OUTPUT:-}" ]; then
{
echo "workspace=$WORKSPACE"
[ -n "$PRODUCT_APP" ] && echo "app_bundle=$PRODUCT_APP"
} >> "$GITHUB_OUTPUT"
fi

bia_log "iOS workspace build completed successfully"
51 changes: 45 additions & 6 deletions scripts/setup-workspace.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ DOWNLOAD_DIR="${TMPDIR%/}/codenameone-tools"
ENV_DIR="$DOWNLOAD_DIR/tools"
mkdir -p "$DOWNLOAD_DIR"
mkdir -p "$ENV_DIR"
mkdir -p ../cn1-binaries
CN1_BINARIES="$(cd ../cn1-binaries && pwd -P)"
rm -Rf ../cn1-binaries
CN1_BINARIES_PARENT="$(cd .. && pwd -P)"
CN1_BINARIES="${CN1_BINARIES_PARENT%/}/cn1-binaries"
mkdir -p "$CN1_BINARIES_PARENT"

ENV_FILE="$ENV_DIR/env.sh"

Expand Down Expand Up @@ -182,9 +182,48 @@ log "Maven version:"; "$MAVEN_HOME/bin/mvn" -version

PATH="$JAVA_HOME/bin:$MAVEN_HOME/bin:$PATH"

log "Cloning cn1-binaries"
rm -Rf "$CN1_BINARIES"
git clone https://github.com/codenameone/cn1-binaries "$CN1_BINARIES"
log "Preparing cn1-binaries checkout"
if [ -d "$CN1_BINARIES/.git" ]; then
log "Found existing cn1-binaries repository at $CN1_BINARIES"
if git -C "$CN1_BINARIES" remote get-url origin >/dev/null 2>&1; then
if git -C "$CN1_BINARIES" fetch --depth=1 origin; then
remote_head=$(git -C "$CN1_BINARIES" symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null || true)
if [ -z "$remote_head" ]; then
current_branch=$(git -C "$CN1_BINARIES" rev-parse --abbrev-ref HEAD 2>/dev/null || true)
if [ -n "$current_branch" ] && [ "$current_branch" != "HEAD" ]; then
remote_head="origin/$current_branch"
else
remote_head="origin/master"
fi
fi
if ! git -C "$CN1_BINARIES" rev-parse --verify "$remote_head" >/dev/null 2>&1; then
if git -C "$CN1_BINARIES" rev-parse --verify origin/main >/dev/null 2>&1; then
remote_head="origin/main"
elif git -C "$CN1_BINARIES" rev-parse --verify origin/master >/dev/null 2>&1; then
remote_head="origin/master"
else
log "Unable to determine remote head for cached cn1-binaries; removing checkout"
rm -rf "$CN1_BINARIES"
fi
fi
if [ -d "$CN1_BINARIES/.git" ]; then
log "Updating cn1-binaries to $remote_head"
git -C "$CN1_BINARIES" reset --hard "$remote_head"
fi
else
log "Failed to fetch updates for cached cn1-binaries; removing checkout"
rm -rf "$CN1_BINARIES"
fi
else
log "Cached cn1-binaries checkout missing origin remote; removing"
rm -rf "$CN1_BINARIES"
fi
fi

if [ ! -d "$CN1_BINARIES/.git" ]; then
log "Cloning cn1-binaries"
git clone --depth=1 --filter=blob:none https://github.com/codenameone/cn1-binaries "$CN1_BINARIES"
fi

log "Building Codename One core modules"
"$MAVEN_HOME/bin/mvn" -f maven/pom.xml -DskipTests -Djava.awt.headless=true -Dcn1.binaries="$CN1_BINARIES" -Dcodename1.platform=javase -P local-dev-javase,compile-android install "$@"
Expand Down
Loading