1+ #! /usr/bin/env bash
2+ # Build a sample "Hello Codename One" Android application using the locally-built Codename One Android port
3+ set -euo pipefail
4+
5+ ba_log () { echo " [build-android-app] $1 " ; }
6+
7+ SCRIPT_DIR=" $( cd " $( dirname " ${BASH_SOURCE[0]} " ) " && pwd) "
8+ REPO_ROOT=" $( cd " $SCRIPT_DIR /.." && pwd) "
9+ cd " $REPO_ROOT "
10+
11+ TMPDIR=" ${TMPDIR:-/ tmp} " ; TMPDIR=" ${TMPDIR%/ } "
12+ DOWNLOAD_DIR=" ${TMPDIR%/ } /codenameone-tools"
13+ ENV_DIR=" $DOWNLOAD_DIR /tools"
14+ EXTRA_MVN_ARGS=(" $@ " )
15+
16+ ENV_FILE=" $ENV_DIR /env.sh"
17+ ba_log " Loading workspace environment from $ENV_FILE "
18+ if [ -f " $ENV_FILE " ]; then
19+ ba_log " Workspace environment file metadata"
20+ ls -l " $ENV_FILE " | while IFS= read -r line; do ba_log " $line " ; done
21+ ba_log " Workspace environment file contents"
22+ sed ' s/^/[build-android-app] ENV: /' " $ENV_FILE "
23+ # shellcheck disable=SC1090
24+ source " $ENV_FILE "
25+ ba_log " Loaded environment: JAVA_HOME=${JAVA_HOME:- <unset>} JAVA17_HOME=${JAVA17_HOME:- <unset>} MAVEN_HOME=${MAVEN_HOME:- <unset>} "
26+ else
27+ ba_log " Workspace tools not found. Run scripts/setup-workspace.sh before this script." >&2
28+ exit 1
29+ fi
30+
31+ # --- Tool validations ---
32+ if [ -z " ${JAVA_HOME:- } " ] || [ ! -x " $JAVA_HOME /bin/java" ]; then
33+ ba_log " JAVA_HOME validation failed. Current value: ${JAVA_HOME:- <unset>} " >&2
34+ if [ -n " ${JAVA_HOME:- } " ]; then
35+ ba_log " Contents of JAVA_HOME directory"
36+ if [ -d " $JAVA_HOME " ]; then ls -l " $JAVA_HOME " | while IFS= read -r line; do ba_log " $line " ; done ; else ba_log " JAVA_HOME directory does not exist" ; fi
37+ fi
38+ ba_log " JAVA_HOME is not set correctly. Please run scripts/setup-workspace.sh first." >&2
39+ exit 1
40+ fi
41+ if [ -z " ${JAVA17_HOME:- } " ] || [ ! -x " $JAVA17_HOME /bin/java" ]; then
42+ ba_log " JAVA17_HOME validation failed. Current value: ${JAVA17_HOME:- <unset>} " >&2
43+ if [ -n " ${JAVA17_HOME:- } " ]; then
44+ ba_log " Contents of JAVA17_HOME directory"
45+ if [ -d " $JAVA17_HOME " ]; then ls -l " $JAVA17_HOME " | while IFS= read -r line; do ba_log " $line " ; done ; else ba_log " JAVA17_HOME directory does not exist" ; fi
46+ fi
47+ ba_log " JAVA17_HOME is not set correctly. Please run scripts/setup-workspace.sh first." >&2
48+ exit 1
49+ fi
50+ if [ -z " ${MAVEN_HOME:- } " ] || [ ! -x " $MAVEN_HOME /bin/mvn" ]; then
51+ ba_log " MAVEN_HOME validation failed. Current value: ${MAVEN_HOME:- <unset>} " >&2
52+ if [ -n " ${MAVEN_HOME:- } " ]; then
53+ ba_log " Contents of MAVEN_HOME directory"
54+ if [ -d " $MAVEN_HOME " ]; then ls -l " $MAVEN_HOME " | while IFS= read -r line; do ba_log " $line " ; done ; else ba_log " MAVEN_HOME directory does not exist" ; fi
55+ fi
56+ ba_log " Maven is not available. Please run scripts/setup-workspace.sh first." >&2
57+ exit 1
58+ fi
59+
60+ ba_log " Using JAVA_HOME at $JAVA_HOME "
61+ ba_log " Using JAVA17_HOME at $JAVA17_HOME "
62+ ba_log " Using Maven installation at $MAVEN_HOME "
63+ export PATH=" $JAVA_HOME /bin:$MAVEN_HOME /bin:$PATH "
64+
65+ ANDROID_SDK_ROOT=" ${ANDROID_SDK_ROOT:- ${ANDROID_HOME:- } } "
66+ if [ -z " $ANDROID_SDK_ROOT " ]; then
67+ if [ -d " /usr/local/lib/android/sdk" ]; then ANDROID_SDK_ROOT=" /usr/local/lib/android/sdk"
68+ elif [ -d " $HOME /Android/Sdk" ]; then ANDROID_SDK_ROOT=" $HOME /Android/Sdk" ; fi
69+ fi
70+ if [ -z " $ANDROID_SDK_ROOT " ] || [ ! -d " $ANDROID_SDK_ROOT " ]; then
71+ ba_log " Android SDK not found. Set ANDROID_SDK_ROOT or ANDROID_HOME to a valid installation." >&2
72+ exit 1
73+ fi
74+ export ANDROID_SDK_ROOT ANDROID_HOME=" $ANDROID_SDK_ROOT "
75+ ba_log " Using Android SDK at $ANDROID_SDK_ROOT "
76+
77+ CN1_VERSION=$( awk -F' [<>]' ' /<version>/{print $3; exit}' maven/pom.xml)
78+ ba_log " Detected Codename One version $CN1_VERSION "
79+
80+ WORK_DIR=" $TMPDIR /cn1-hello-android"
81+ rm -rf " $WORK_DIR " ; mkdir -p " $WORK_DIR "
82+
83+ GROUP_ID=" com.codenameone.examples"
84+ ARTIFACT_ID=" hello-codenameone"
85+ MAIN_NAME=" HelloCodenameOne"
86+
87+ SOURCE_PROJECT=" $REPO_ROOT /Samples/SampleProjectTemplate"
88+ if [ ! -d " $SOURCE_PROJECT " ]; then
89+ ba_log " Source project template not found at $SOURCE_PROJECT " >&2
90+ exit 1
91+ fi
92+ ba_log " Using source project template at $SOURCE_PROJECT "
93+
94+ LOCAL_MAVEN_REPO=" ${LOCAL_MAVEN_REPO:- $HOME / .m2/ repository} "
95+ ba_log " Using local Maven repository at $LOCAL_MAVEN_REPO "
96+ mkdir -p " $LOCAL_MAVEN_REPO "
97+ MAVEN_CMD=(
98+ " $MAVEN_HOME /bin/mvn" -B -ntp
99+ -Dmaven.repo.local=" $LOCAL_MAVEN_REPO "
100+ -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn
101+ )
102+
103+ # --- Generate app skeleton ---
104+ ba_log " Generating Codename One application skeleton via codenameone-maven-plugin"
105+ (
106+ cd " $WORK_DIR "
107+ xvfb-run -a " ${MAVEN_CMD[@]} " -q \
108+ com.codenameone:codenameone-maven-plugin:7.0.204:generate-app-project \
109+ -DgroupId=" $GROUP_ID " \
110+ -DartifactId=" $ARTIFACT_ID " \
111+ -Dversion=1.0-SNAPSHOT \
112+ -DsourceProject=" $SOURCE_PROJECT " \
113+ -Dcn1Version=" 7.0.204" \
114+ " ${EXTRA_MVN_ARGS[@]} "
115+ )
116+
117+ APP_DIR=" $WORK_DIR /$ARTIFACT_ID "
118+
119+ # --- Namespace-aware CN1 normalization (xmlstarlet) ---
120+ ROOT_POM=" $APP_DIR /pom.xml"
121+ NS=" mvn=http://maven.apache.org/POM/4.0.0"
122+
123+ if ! command -v xmlstarlet > /dev/null 2>&1 ; then
124+ sudo apt-get update -y && sudo apt-get install -y xmlstarlet
125+ fi
126+
127+ # Helper to run xmlstarlet with Maven namespace
128+ x () { xmlstarlet ed -L -N " $NS " " $@ " ; }
129+ q () { xmlstarlet sel -N " $NS " " $@ " ; }
130+
131+ # 1) Ensure <properties><codenameone.version> exists/updated (root pom)
132+ if [ " $( q -t -v ' count(/mvn:project/mvn:properties)' " $ROOT_POM " 2> /dev/null || echo 0) " = " 0" ]; then
133+ x -s " /mvn:project" -t elem -n properties -v " " " $ROOT_POM "
134+ fi
135+ if [ " $( q -t -v ' count(/mvn:project/mvn:properties/mvn:codenameone.version)' " $ROOT_POM " 2> /dev/null || echo 0) " = " 0" ]; then
136+ x -s " /mvn:project/mvn:properties" -t elem -n codenameone.version -v " $CN1_VERSION " " $ROOT_POM "
137+ else
138+ x -u " /mvn:project/mvn:properties/mvn:codenameone.version" -v " $CN1_VERSION " " $ROOT_POM "
139+ fi
140+
141+ # 2) Parent must be a LITERAL version (no property allowed)
142+ while IFS= read -r -d ' ' P; do
143+ x -u " /mvn:project[mvn:parent/mvn:groupId='com.codenameone' and mvn:parent/mvn:artifactId='codenameone-maven-parent']/mvn:parent/mvn:version" -v " $CN1_VERSION " " $P " || true
144+ done < <( find " $APP_DIR " -type f -name pom.xml -print0)
145+
146+ # 3) Point com.codenameone deps/plugins to ${codenameone.version}
147+ while IFS= read -r -d ' ' P; do
148+ # Dependencies
149+ x -u " /mvn:project//mvn:dependencies/mvn:dependency[starts-with(mvn:groupId,'com.codenameone')]/mvn:version" -v ' ${codenameone.version}' " $P " 2> /dev/null || true
150+ # Plugins (regular)
151+ x -u " /mvn:project//mvn:build/mvn:plugins/mvn:plugin[starts-with(mvn:groupId,'com.codenameone')]/mvn:version" -v ' ${codenameone.version}' " $P " 2> /dev/null || true
152+ # Plugins (pluginManagement)
153+ x -u " /mvn:project//mvn:build/mvn:pluginManagement/mvn:plugins/mvn:plugin[starts-with(mvn:groupId,'com.codenameone')]/mvn:version" -v ' ${codenameone.version}' " $P " 2> /dev/null || true
154+ done < <( find " $APP_DIR " -type f -name pom.xml -print0)
155+
156+ # 4) Ensure common Maven plugins have a version (Maven requires it even if parent not yet resolved)
157+ declare -A PIN=(
158+ [org.apache.maven.plugins:maven-compiler-plugin]=3.11.0
159+ [org.apache.maven.plugins:maven-resources-plugin]=3.3.1
160+ [org.apache.maven.plugins:maven-surefire-plugin]=3.2.5
161+ [org.apache.maven.plugins:maven-failsafe-plugin]=3.2.5
162+ [org.apache.maven.plugins:maven-jar-plugin]=3.3.0
163+ [org.apache.maven.plugins:maven-clean-plugin]=3.3.2
164+ [org.apache.maven.plugins:maven-deploy-plugin]=3.1.2
165+ [org.apache.maven.plugins:maven-install-plugin]=3.1.2
166+ [org.apache.maven.plugins:maven-assembly-plugin]=3.6.0
167+ [org.apache.maven.plugins:maven-site-plugin]=4.0.0-M15
168+ [com.codenameone:codenameone-maven-plugin]=' ${codenameone.version}'
169+ )
170+
171+ add_version_if_missing () {
172+ local pom=" $1 " g=" $2 " a=" $3 " v=" $4 "
173+ # build/plugins
174+ if [ " $( q -t -v " count(/mvn:project/mvn:build/mvn:plugins/mvn:plugin[mvn:groupId='$g ' and mvn:artifactId='$a ']/mvn:version)" " $pom " 2> /dev/null || echo 0) " = " 0" ] &&
175+ [ " $( q -t -v " count(/mvn:project/mvn:build/mvn:plugins/mvn:plugin[mvn:groupId='$g ' and mvn:artifactId='$a '])" " $pom " 2> /dev/null || echo 0) " != " 0" ]; then
176+ x -s " /mvn:project/mvn:build/mvn:plugins/mvn:plugin[mvn:groupId='$g ' and mvn:artifactId='$a ']" -t elem -n version -v " $v " " $pom " || true
177+ fi
178+ # pluginManagement/plugins
179+ if [ " $( q -t -v " count(/mvn:project/mvn:build/mvn:pluginManagement/mvn:plugins/mvn:plugin[mvn:groupId='$g ' and mvn:artifactId='$a ']/mvn:version)" " $pom " 2> /dev/null || echo 0) " = " 0" ] &&
180+ [ " $( q -t -v " count(/mvn:project/mvn:build/mvn:pluginManagement/mvn:plugins/mvn:plugin[mvn:groupId='$g ' and mvn:artifactId='$a '])" " $pom " 2> /dev/null || echo 0) " != " 0" ]; then
181+ x -s " /mvn:project/mvn:build/mvn:pluginManagement/mvn:plugins/mvn:plugin[mvn:groupId='$g ' and mvn:artifactId='$a ']" -t elem -n version -v " $v " " $pom " || true
182+ fi
183+ }
184+
185+ while IFS= read -r -d ' ' P; do
186+ for ga in " ${! PIN[@]} " ; do
187+ add_version_if_missing " $P " " ${ga%%:* } " " ${ga##*: } " " ${PIN[$ga]} "
188+ done
189+ done < <( find " $APP_DIR " -type f -name pom.xml -print0)
190+
191+ # 5) Build with the property set so any lingering refs resolve to the local snapshot
192+ EXTRA_MVN_ARGS+=(" -Dcodenameone.version=${CN1_VERSION} " )
193+
194+ # (Optional) quick non-fatal checks
195+ xmlstarlet sel -N " $NS " -t -v " /mvn:project/mvn:properties/mvn:codenameone.version" -n " $ROOT_POM " || true
196+ xmlstarlet sel -N " $NS " -t -c " /mvn:project/mvn:build/mvn:plugins" -n " $ROOT_POM " | head -n 60 || true
197+
198+
199+
200+ [ -d " $APP_DIR " ] || { ba_log " Failed to create Codename One application project" >&2 ; exit 1; }
201+ [ -f " $APP_DIR /build.sh" ] && chmod +x " $APP_DIR /build.sh"
202+
203+ SETTINGS_FILE=" $APP_DIR /common/codenameone_settings.properties"
204+ [ -f " $SETTINGS_FILE " ] || { ba_log " codenameone_settings.properties not found at $SETTINGS_FILE " >&2 ; exit 1; }
205+
206+ # --- Read settings ---
207+ read_prop () { grep -E " ^$1 =" " $SETTINGS_FILE " | head -n1 | cut -d' =' -f2- | sed ' s/^[[:space:]]*//' ; }
208+
209+ PACKAGE_NAME=" $( read_prop ' codename1.packageName' || true) "
210+ CURRENT_MAIN_NAME=" $( read_prop ' codename1.mainName' || true) "
211+
212+ if [ -z " $PACKAGE_NAME " ]; then
213+ PACKAGE_NAME=" $GROUP_ID "
214+ ba_log " Package name not found in settings. Falling back to groupId $PACKAGE_NAME "
215+ fi
216+ if [ -z " $CURRENT_MAIN_NAME " ]; then
217+ CURRENT_MAIN_NAME=" $MAIN_NAME "
218+ ba_log " Main class name not found in settings. Falling back to target $CURRENT_MAIN_NAME "
219+ fi
220+
221+ # --- Generate Java from external template ---
222+ PACKAGE_PATH=" ${PACKAGE_NAME// .// } "
223+ JAVA_DIR=" $APP_DIR /common/src/main/java/${PACKAGE_PATH} "
224+ mkdir -p " $JAVA_DIR "
225+ MAIN_FILE=" $JAVA_DIR /${MAIN_NAME} .java"
226+
227+ TEMPLATE=" $SCRIPT_DIR /templates/HelloCodenameOne.java.tmpl"
228+ if [ ! -f " $TEMPLATE " ]; then
229+ ba_log " Template not found: $TEMPLATE " >&2
230+ exit 1
231+ fi
232+
233+ sed -e " s|@PACKAGE@|$PACKAGE_NAME |g" \
234+ -e " s|@MAIN_NAME@|$MAIN_NAME |g" \
235+ " $TEMPLATE " > " $MAIN_FILE "
236+
237+ # --- Ensure codename1.mainName is set ---
238+ ba_log " Setting codename1.mainName to $MAIN_NAME "
239+ if grep -q ' ^codename1.mainName=' " $SETTINGS_FILE " ; then
240+ # GNU sed in CI: in-place edit without backup
241+ sed -E -i ' s|^codename1\.mainName=.*$|codename1.mainName=' " $MAIN_NAME " ' |' " $SETTINGS_FILE "
242+ else
243+ printf ' \ncodename1.mainName=%s\n' " $MAIN_NAME " >> " $SETTINGS_FILE "
244+ fi
245+ # Ensure trailing newline
246+ tail -c1 " $SETTINGS_FILE " | read -r _ || echo >> " $SETTINGS_FILE "
247+
248+ # --- Normalize Codename One versions (use Maven Versions Plugin) ---
249+ ba_log " Normalizing Codename One Maven coordinates to $CN1_VERSION "
250+
251+ # --- Build Android gradle project ---
252+ ba_log " Building Android gradle project using Codename One port"
253+ xvfb-run -a " ${MAVEN_CMD[@]} " -q -f " $APP_DIR /pom.xml" package \
254+ -DskipTests \
255+ -Dcodename1.platform=android \
256+ -Dcodename1.buildTarget=android-source \
257+ -Dopen=false \
258+ " ${EXTRA_MVN_ARGS[@]} "
259+
260+ GRADLE_PROJECT_DIR=$( find " $APP_DIR /android/target" -maxdepth 2 -type d -name " *-android-source" | head -n 1 || true)
261+ if [ -z " $GRADLE_PROJECT_DIR " ]; then
262+ ba_log " Failed to locate generated Android project" >&2
263+ ba_log " Contents of $APP_DIR /android/target:" >&2
264+ ls -R " $APP_DIR /android/target" >&2 || ba_log " Unable to list $APP_DIR /android/target" >&2
265+ exit 1
266+ fi
267+
268+ ba_log " Invoking Gradle build in $GRADLE_PROJECT_DIR "
269+ chmod +x " $GRADLE_PROJECT_DIR /gradlew"
270+ ORIGINAL_JAVA_HOME=" $JAVA_HOME "
271+ export JAVA_HOME=" $JAVA17_HOME "
272+ (
273+ cd " $GRADLE_PROJECT_DIR "
274+ if command -v sdkmanager > /dev/null 2>&1 ; then
275+ yes | sdkmanager --licenses > /dev/null 2>&1 || true
276+ elif [ -x " $ANDROID_SDK_ROOT /cmdline-tools/latest/bin/sdkmanager" ]; then
277+ yes | " $ANDROID_SDK_ROOT /cmdline-tools/latest/bin/sdkmanager" --licenses > /dev/null 2>&1 || true
278+ fi
279+ ./gradlew --no-daemon assembleDebug
280+ )
281+ export JAVA_HOME=" $ORIGINAL_JAVA_HOME "
282+
283+ APK_PATH=$( find " $GRADLE_PROJECT_DIR " -path " */outputs/apk/debug/*.apk" | head -n 1 || true)
284+ [ -n " $APK_PATH " ] || { ba_log " Gradle build completed but no APK was found" >&2 ; exit 1; }
285+ ba_log " Successfully built Android APK at $APK_PATH "
0 commit comments