Building Kotlin Multiplatform app with Xcode Cloud

946 Views Asked by At

as Apple announced that Xcode Cloud is now available for all developers, I tried to set it up for a Kotlin Multiplatform project. The start was a little bit hard tbh. Currently I am facing following problem during a simple test action:

The operation couldn’t be completed. Unable to locate a Java Runtime.
Please visit http://www.java.com for information on installing Java.
Command PhaseScriptExecution failed with a nonzero exit code

So I tried to install a jdk during the ci_post_clone.sh phase. The output of java -version after the installation on the Xcode Cloud is the following:

openjdk version "17.0.2" 2022-01-18
OpenJDK Runtime Environment (build 17.0.2+8-86)
OpenJDK 64-Bit Server VM (build 17.0.2+8-86, mixed mode, sharing)
Installed java

also ./gradlew -v output:


Showing All Messages
------------------------------------------------------------

Gradle 7.4.2

------------------------------------------------------------
Kotlin:       1.5.31
Groovy:       3.0.9
Ant:          Apache Ant(TM) version 1.10.11 compiled on July 10 2021
JVM:          17.0.2 (Oracle Corporation 17.0.2+8-86)
OS:           Mac OS X 12.4 x86_64

Nevertheless, I still get the same error. Is this maybe a restriction by Apple?

Any ideas?

3

There are 3 best solutions below

1
On

I was able to get this working using an env variable within the Xcode Cloud Workflow, installing a java runtime using brew, and modifying a gradle property.

Within the ci_post_clone.sh, I ran:

#!/bin/sh

brew install openjdk@11

Within the environment variable section in the Xcode Cloud workflow I added:

JAVA_HOME=/usr/local/opt/openjdk@11

Within my gradle.properties file I added:

org.gradle.java.home=/usr/local/opt/openjdk@11

This allowed the application to build successfully within Xcode Cloud.

3
On

Basically we install a JDK through a tar ball into the project folder using ci_post_clone.sh. We choose the project folder because we have control over this directory, contrary to for example installing java through brew (where you would have to symlink the java install into a system directory, which isn't allowed (https://developer.apple.com/documentation/xcode/making-dependencies-available-to-xcode-cloud)

Note You can use custom build scripts to perform a variety of tasks, but you can’t obtain administrator privileges by using sudo.

This is the script we use:

#!/bin/sh

root_dir=$CI_WORKSPACE_PATH
repo_dir=$CI_PRIMARY_REPOSITORY_PATH
jdk_dir="${CI_DERIVED_DATA_PATH}/JDK"

gradle_dir="${repo_dir}/Common"
cache_dir="${CI_DERIVED_DATA_PATH}/.gradle"

jdk_version="20.0.1"

# Check if we stored gradle caches in DerivedData.
recover_cache_files() {
    
    echo "\nRecover cache files"

    if [ ! -d $cache_dir ]; then
        echo " - No valid caches found, skipping"
        return 0
    fi

    echo " - Copying gradle cache to ${gradle_dir}"
    rm -rf "${gradle_dir}/.gradle"
    cp -r $cache_dir $gradle_dir

    return 0
}

# Install the JDK
install_jdk_if_needed() {

    echo "\nInstall JDK if needed"

    if [[ $(uname -m) == "arm64" ]]; then
        echo " - Detected M1"
        arch_type="macos-aarch64"
    else
        echo " - Detected Intel"
        arch_type="macos-x64"
    fi

    # Location of version / arch detection file.
    detect_loc="${jdk_dir}/.${jdk_version}.${arch_type}"

    if [ -f $detect_loc ]; then
        echo " - Found a valid JDK installation, skipping install"
        return 0
    fi

    echo " - No valid JDK installation found, installing..."

    tar_name="jdk-${jdk_version}_${arch_type}_bin.tar.gz"

    # Download and un-tar JDK to our defined location.
    curl -OL "https://download.oracle.com/java/20/archive/${tar_name}"
    tar xzf $tar_name -C $root_dir

    # Move the JDK to our desired location.
    rm -rf $jdk_dir
    mkdir -p $jdk_dir
    mv "${root_dir}/jdk-${jdk_version}.jdk/Contents/Home" $jdk_dir

    # Some cleanup.
    rm -r "${root_dir}/jdk-${jdk_version}.jdk"
    rm $tar_name

    # Add the detection file for subsequent builds.
    touch $detect_loc

    echo " - Set JAVA_HOME in Xcode Cloud to ${jdk_dir}/Home"

    return 0
}

recover_cache_files
install_jdk_if_needed

After running the script for the first time, the logs will print the last echo in the script to indicate where the Java SDK is located. This value should be set as JAVA_HOME in the "Environment Variables" inside the "Edit" section of the workflow.

Environment Variables

After that, the build will work with the Java SDK and build the artifacts.

1
On

I use a very simple script relying on SDK!Man for this

#!/usr/bin/env bash
brew install cocoapods
curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"
sdk install java 19.0.1-tem

# cd into actual project root
cd ../../../
./gradlew app:ios:podinstall

Note that if you want to use java in an XCode build, which you probabbly do you need to set the following environment variable

JAVA_HOME=/Users/local/.sdkman/candidates/java/current