diff --git a/.idea/openapi-plugin.iml b/.idea/openapi-plugin.iml
index 3ef99cd..28bf0d2 100644
--- a/.idea/openapi-plugin.iml
+++ b/.idea/openapi-plugin.iml
@@ -10,7 +10,7 @@
-
+
@@ -24,7 +24,7 @@
-
+
@@ -33,11 +33,11 @@
-
+
-
+
diff --git a/breaking_versions.txt b/breaking_versions.txt
index ea33d7d..1d93f16 100644
--- a/breaking_versions.txt
+++ b/breaking_versions.txt
@@ -1,2 +1,3 @@
## Next line means plugin 2.4.0.RC11 is not compatible with Jeka 0.9.0.RELEASE and above
-## 2.4.0.RC11 : 0.9.0.RELEASE (remove this comment and leading '##' to be effective)
\ No newline at end of file
+## 2.4.0.RC11 : 0.9.0.RELEASE (remove this comment and leading '##' to be effective)
+0.11.0-0 : 0.10.49
\ No newline at end of file
diff --git a/jeka b/jeka
new file mode 100755
index 0000000..3d5e96b
--- /dev/null
+++ b/jeka
@@ -0,0 +1,1071 @@
+#!/bin/bash
+#
+# Copyright 2014-2024 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Script for launching JeKa tool.
+#
+# Authors: Jerome Angibaud, Patrick Santana
+#
+# Rules for selecting a JDK :
+# - if JEKA_JDK_HOME env var is specified, select it
+# - if a jeka.java.version property is specified
+# - if a jeka.jdk.[version] property is specified, select the specified path.
+# - else, look in cache or download the proper JDK
+# - else
+# - if JAVA_HOME env var is specified, select it
+# - else, look in cache and download default version (temurin 21)
+#
+# Rules for reading a property (said "my.prop") :
+# - if a command line argument contains "-Dmy.prop=xxx", returns 'xxx'
+# - if an OS environment variable 'my.prop' exists, returns this value
+# - if property is defined in $BASE_DIR/jeka.properties, returns this value
+# - look recursively in $BASE_DIR/../jeka.properties. Stop at first folder ancestor not having a jeka.properties file
+# - look in JEKA_USER_HOME/global.properties
+#
+
+set -e
+#####################
+# Global vars
+#####################
+
+declare CMD_LINE_ARGS=("$@")
+declare -a INTERPOLATED_ARGS
+
+declare JEKA_VERSION_PROP_NAME="jeka.java.version"
+declare JEKA_JAR_NAME="dev.jeka.jeka-core.jar"
+
+declare JEKA_USER_HOME
+declare GLOBAL_PROP_FILE
+declare BASE_DIR # To find BASE_DIR/jeka/local.properties, BASE_DIR/jeka/def, ...
+declare CURRENT_SCRIPT_DIR
+declare -i PROGRAM_OPTION_INDEX
+CURRENT_SCRIPT_DIR="$( cd "$(dirname "$0")" ; pwd -P )"
+
+# Global variables are preferred over passing all arguments
+# in method call, cause they are too many.
+declare DEFAULT_JAVA_VERSION="21"
+declare JDK_DOWNLOAD_DISTRIB="temurin"
+declare JDK_DOWNLOAD_LIBC_TYPE="glibc" # default for linux, overridden for other os
+declare JDK_DOWNLOAD_FILE_TYPE="tar.gz" # overridden for *WIN os
+declare JDK_DOWNLOAD_OS
+declare JDK_DOWNLOAD_ARCH
+
+declare IS_VERBOSE # we should not log anything when on the green path, except if '-lsu' arg is present
+declare IS_QUIET # we should log only error msg
+
+declare LOG_DEBUG # DEBUG
+declare DRY_RUN # Debugging purpose
+declare DEFAULT_BASE_DIR="."
+
+
+#######################################
+# Prints passed arguments on the stderr
+# Globals:
+# none
+# Arguments:
+# $*
+#######################################
+msg() {
+ echo "$*" 1>&2
+}
+
+#######################################
+# Gets the sub-string part ending before '#' of a specified string. ('Hello#World' should returns 'Hello')
+# Globals:
+# none
+# Arguments:
+# $1 : the string to extract substring from
+# Outputs:
+# Write extracted sub-string to stdout
+#######################################
+substring_before_hash() {
+ # Extract the substring before '#' using cut
+ result=$(echo "$1" | cut -d'#' -f1)
+
+ # Echo the resulting substring
+ echo "$result"
+}
+
+#######################################
+# Gets the sub-string part starting after '#' of a specified string. ('Hello#World' should returns 'World')
+# Globals:
+# none
+# Arguments:
+# $1 : the string to extract substring from
+# Outputs:
+# Write extracted sub-string to stdout
+#######################################
+substring_after_hash() {
+ # Extract the substring after '#' using parameter expansion
+ result=${1#*#}
+
+ # If the input string did not have a '#', return empty. Otherwise, return the result
+ if [ "$result" == "$1" ]; then
+ echo ""
+ else
+ echo "$result"
+ fi
+}
+
+array_contains() {
+ local search=$1; shift
+ local array=("$@")
+
+ for element in "${array[@]}"; do
+ if [[ $element == $search ]]; then
+ echo "true"
+ return 0 # Element found, return success
+ fi
+ done
+ echo "false"
+}
+
+
+#######################################
+# Gets the value of a property, declared as '-Dprop.name=prop.value' in an array.
+# Globals:
+# CMD_LINE_ARGS (read)
+# Arguments:
+# $1 : the property name
+# Outputs:
+# Write property value to stdout
+#######################################
+get_system_prop_from_args() {
+ local prop_name=$1
+ local prefix="-D$prop_name="
+ for arg in "${CMD_LINE_ARGS[@]}"
+ do
+ if [[ "$arg" == "$prefix"* ]] ; then
+ echo "${arg#$prefix}"
+ fi
+ done
+}
+
+#######################################
+# Download specified zip/tgz file and unpack it to the specified dir
+# Globals:
+# none
+# Arguments:
+# $1 : url to download file
+# $2 : the target directory path to unzip/unpack content
+# $3 : optional file type to unpack ('zip' or 'tar.gz'). Default is 'zip'.
+#######################################
+download_and_unpack() {
+ local url=$1
+ local dir=$2
+ local file_type=$3 # 'zip' or 'tar.gz'
+ local temp_file
+ temp_file=$(mktemp)
+ rm "$temp_file"
+
+ ## download
+ if [ -x "$(command -v curl)" ]; then
+ local silent_flag="s"
+ if [ "$IS_VERBOSE" != "" ]; then
+ silent_flag=""
+ fi
+ curl -"$silent_flag"Lf --fail --show-error -o "$temp_file" "$url"
+ if [ $? -ne 0 ]; then
+ msg "Curl request failed $url"
+ msg "Returned code: $?"
+ exit 1
+ fi
+ elif [ -x "$(command -v wget)" ]; then
+ wget -q -O "$temp_file" "$url"
+ else
+ msg "Error: curl or wget not found, please make sure one of them is installed"
+ exit 1
+ fi
+
+ ## unpack
+ mkdir -p "$dir"
+ if [ "$file_type" == "tar.gz" ]; then
+ gzip -cd "$temp_file" | tar xf - -C "$dir"
+ else
+ unzip -qq -o "$temp_file" -d "$dir"
+ fi
+ rm "$temp_file"
+}
+
+#######################################
+# Computes if we should print something on console when downloading files
+# Globals:
+# CMD_LINE_ARGS (read)
+# IS_VERBOSE (write)
+# Arguments:
+# None
+#######################################
+compute_VERBOSE_QUIET() {
+ for arg in "${CMD_LINE_ARGS[@]}"
+ do
+ if [ "$arg" == "--verbose" ] || [ "$arg" == "-v" ] || [ "$arg" == "-d" ] || [ "$arg" == "--debug" ]; then
+ IS_VERBOSE="true"
+ elif [ "$arg" == "--quiet" ] || [ "$arg" == "-q" ]; then
+ IS_QUIET="true"
+ fi
+ done
+}
+
+#######################################
+# Prints passed arguments on the standard stream, only if LOG_DEBUG is non-empty
+# Globals:
+# LOG_DEBUG (read)
+# Arguments:
+# $*
+#######################################
+debug() {
+ if [ -n "$LOG_DEBUG" ] || [ -n "$IS_VERBOSE" ]; then
+ echo "$*" 1>&2
+ fi
+}
+
+#######################################
+# Prints passed arguments on the standard stream, only if IS_QUIET is empty
+# Globals:
+# IS_QUIET (read)
+# Arguments:
+# $*
+#######################################
+info() {
+ if [ -z "$IS_QUIET" ]; then
+ echo "$*" 1>&2
+ fi
+}
+
+#######################################
+# Gets the Jeka directory for the user. This is where are located global.properties and cache dirs.
+# Globals:
+# JEKA_USER_HOME (read)
+# Arguments:
+# none
+# Outputs:
+# Write location to stdout
+#######################################
+get_jeka_user_home() {
+ if [ -z "$JEKA_USER_HOME" ]; then
+ echo "$HOME/.jeka"
+ else
+ echo "$JEKA_USER_HOME"
+ fi
+}
+
+#######################################
+# Gets the effective cache dir for Jeka user
+# Globals:
+# JEKA_CACHE_DIR (read)
+# JEKA_USER_HOME (read)
+# Arguments:
+# none
+# Outputs:
+# Write location to stdout
+#######################################
+get_cache_dir() {
+ if [ -z "$JEKA_CACHE_DIR" ]; then
+ echo "$JEKA_USER_HOME/cache"
+ else
+ echo "$JEKA_CACHE_DIR"
+ fi
+}
+
+#######################################
+# Gets the dir for caching projects cloned from git
+# Globals:
+# JEKA_CACHE_DIR (read)
+# JEKA_USER_HOME (read)
+# Arguments:
+# none
+# Outputs:
+# Write location to stdout
+#######################################
+get_git_cache_dir() {
+ echo "$(get_cache_dir)/git"
+}
+
+
+
+#######################################
+# Gets the value of a property declared within a property file
+# Globals:
+# none
+# Arguments:
+# $1 : the path of the property file
+# $2 : the property name
+# Outputs:
+# Write property value to stdout
+#######################################
+get_prop_value_from_file() {
+ local file=$1
+ local key=$2
+ if [ ! -f "$file" ]; then
+ return
+ fi
+ local value
+ value=$(grep "^${key}=" "${file}")
+ local -i key_length
+ key_length=${#key}
+ ((key_length++))
+ echo "${value:key_length}"
+}
+
+#######################################
+# Gets the translation of a property name (as my.prop) to an env var name (as MY_PROP)
+# Globals:
+# none
+# Arguments:
+# $1 : the property name
+# Outputs:
+# Write env var name to stdout
+#######################################
+get_env_var_name() {
+ local prop_name="$1"
+ local result=${prop_name^}
+ result=${result//./_}
+ result=${result//-/_}
+ echo "$result"
+}
+
+#######################################
+# Resolves and returns the value of a property by looking in command line args, env var and jeka.properties files
+# Globals:
+# CMD_LINE_ARGS (read)
+# Arguments:
+# $1 : the base directory from where looking for jeka.properties file
+# $2 : the property name
+# Outputs:
+# Write env var name to stdout
+#######################################
+get_prop_value_from_base_dir() {
+ local base_dir=$1
+ local prop_name=$2
+
+ # First look in command line args
+ local cmd_args_value
+ cmd_args_value="$(get_system_prop_from_args "$prop_name")"
+ if [ "$cmd_args_value" != "" ]; then
+ echo "$cmd_args_value"
+ return 0
+ fi
+
+ # Then look in env variables
+ local env_value
+ env_value=$(printenv "$prop_name")
+ if [ "$env_value" != "" ]; then
+ echo "$env_value"
+ return 0
+ fi
+
+ local value
+ value=$(get_prop_value_from_file "$base_dir/jeka.properties" "$prop_name")
+ if [ -z "$value" ]; then
+ local parent_dir="$base_dir/.."
+ local parent_jeka_props="$parent_dir/jeka.properties"
+ if [ -f "$parent_jeka_props" ]; then
+ get_prop_value_from_base_dir "$parent_dir" "$prop_name"
+ else
+ get_prop_value_from_file "$GLOBAL_PROP_FILE" "$prop_name"
+ fi
+ return 0
+ fi
+ echo "$value"
+}
+
+#######################################
+# Returns the JAVA version to use according properties
+# Globals:
+# CMD_LINE_ARGS (read)
+# JEKA_VERSION_PROP_NAME (read)
+# Arguments:
+# $1 : the base directory from where looking for jeka.properties file
+# Outputs:
+# Write JAVA version to stdout
+#######################################
+get_java_version_from_props() {
+ local base_dir="$1"
+ local version
+ version=$(get_prop_value_from_base_dir "$base_dir" "$JEKA_VERSION_PROP_NAME")
+ local trimmed_version
+ trimmed_version="${version// /}" # remove spaces
+ echo "$trimmed_version"
+}
+
+#######################################
+# Returns the JAVA version to use according properties
+# Globals:
+# INTERPOLATED_ARGS (write)
+# Arguments:
+# $@ : an array representing the original args
+# Outputs:
+# result in INTERPOLATED_ARGS global args
+#######################################
+compute_INTERPOLATED_ARGS() {
+ INTERPOLATED_ARGS=()
+ for arg in "$@"; do
+ if [[ $arg == ::* ]]; then # if arg starts with '::'
+ local token=${arg:2}
+ local prop_name="jeka.cmd.$token"
+ local value
+ value=$(get_prop_value_from_base_dir "$DEFAULT_BASE_DIR" "$prop_name")
+ if [ "$value" != "" ]; then
+
+ # Value may content 0 or many elements
+ # shellcheck disable=SC2206
+ local substitute=($value)
+ INTERPOLATED_ARGS+=("${substitute[@]}")
+ else
+ INTERPOLATED_ARGS+=("$arg")
+ fi
+ else
+ INTERPOLATED_ARGS+=("$arg")
+ fi
+ done
+}
+
+#######################################
+# Find the index of the remote option in an an array
+# Globals:
+# INTERPOLATED_ARGS (write)
+# Arguments:
+# $@ : an array representing the original args
+# Outputs:
+# result in INTERPOLATED_ARGS global args
+#######################################
+find_remote_arg_index() {
+ index=0
+ for arg in "$@"; do
+ is_remote_arg "$arg"
+ if [ "$result" == "true" ]; then
+ echo "$index"
+ return 0;
+ fi
+ ((index++))
+ done
+ echo "-1";
+}
+
+#######################################
+# Determines if the passed argument is a remote option (as -r)
+# Arguments:
+# $1 : a string representing one argument
+# Outputs:
+# result in 'result' global args
+#######################################
+is_remote_arg() {
+ local arg="$1"
+ if [[ "$arg" != -* ]]; then
+ result="false"
+ return 0
+ fi
+ local option=${arg:1}
+
+ # check if option contains letter 'r' and optionally 'u' or 'p'
+ if [[ "$option" == *r* ]] && [[ $option =~ ^(r?u?p?|r?u?p?)$ ]] && [[ ${#option} -le 3 ]]; then
+ result="true"
+ else
+ result="false"
+ fi
+}
+
+# call `get_jdk_home_from_props base_dir JAVA_VERSION`
+get_jdk_home_from_props() {
+ local base_dir=$1
+ local jdk_version=$2
+ local prop_name="jeka.jdk.$jdk_version"
+ get_prop_value_from_base_dir "$base_dir" "$prop_name"
+}
+
+is_git_url() {
+ if [[ $1 =~ ^(https://|ssh://|git://|git@).* ]]; then
+ echo "true"
+ else
+ echo "false"
+ fi
+}
+
+#######################################
+# Returns the dir caching the specified git repo url
+# Arguments:
+# $1 : a string representing the git repo url
+# Outputs:
+# The dir location
+#######################################
+get_folder_name_from_git_url() {
+ local url=$1
+ local trimmed_url=$url
+ local protocols=("https://" "ssh://" "git://" "git@")
+ for protocol in "${protocols[@]}"; do
+ trimmed_url="${trimmed_url#$protocol}"
+ done
+ local folder_name="${trimmed_url//\//_}" # replace '/' by '_'
+ echo "$folder_name"
+}
+
+assert_dir_exits() {
+ if [ ! -d "$1" ]; then
+ msg "Directory $1 does not exist"
+ exit 1
+ fi
+}
+
+#######################################
+# Computes the base directory according the value of '-r' option.
+# If the -r refers to a git repo url, then returns the dir where the repo is cloned.
+# Global Vars:
+# BASE_DIR (write)
+# Arguments:
+# $1 : a string representing a git repo url or a directory path
+# $2 : true/false for updating the cloned repo
+# Outputs:
+# result in BASE_DIR global args
+#######################################
+compute_base_dir_from_resolved_remote_arg() {
+
+ local remote_path="$1" # file-system path or git url
+ local should_clean="$2"
+ local is_git_remote
+ is_git_remote=$(is_git_url "$remote_path")
+
+ # the remote reference a file-system path
+ if [ "false" == "$is_git_remote" ]; then
+
+ if [[ "$remote_path" == /* ]]; then # absolute path
+ result="$remote_path"
+ assert_dir_exits "$result"
+ else # relative path
+ result="$(pwd)/$remote_arg"
+ assert_dir_exits "$result"
+ result=$(cd "$result" && pwd) # normalize pass
+ fi
+ BASE_DIR="$result"
+ return 0
+ fi
+
+ ## Remote reference a git repo
+ local git_url
+ git_url=$(substring_before_hash "$remote_path")
+ local git_tag
+ git_tag=$(substring_after_hash "$remote_path")
+ local branch_args=""
+ if [ "$git_tag" != "" ]; then
+ branch_args="--branch $git_tag"
+ fi
+ local cache_dir_name
+ cache_dir_name=$(get_folder_name_from_git_url "$remote_path")
+ result=$(get_git_cache_dir)/"$cache_dir_name"
+ if [ "$should_clean" == "true" ]; then
+ rm -rf "$result"
+ fi
+ if [ ! -d "$result" ]; then
+ local quiet_flag="--quiet"
+ if [ "$IS_VERBOSE" != "" ]; then
+ quiet_flag=""
+ fi
+ info "Cloning $git_url into $result ..."
+ # $quiet_flag and $branch_args are not doubled-quoted on purpose (they may contains 0 or 2 args)
+ # shellcheck disable=SC2086
+ git clone $quiet_flag -c advice.detachedHead=false --depth 1 $branch_args "$git_url" "$result"
+ else
+ debug "Cache directory $result already exists. Won't clone or update."
+ fi
+ BASE_DIR=$result
+}
+
+#######################################
+# Computes the base directory according presence or not of -r option
+# Arguments:
+# $@ : array representing the interpolated command line
+# Outputs:
+# result in BASE_DIR global args
+#######################################
+compute_BASE_DIR() {
+ local -a array=( "$@" )
+ local result
+ local index
+ index=$(find_remote_arg_index "${array[@]}")
+ if [ "$index" == "-1" ]; then
+ BASE_DIR=$(pwd)
+ return 0
+ fi
+ local option=${array[(($index))]}
+ local next_index=$((index + 1))
+ local remote_arg=${array[$next_index]}
+ local need_update="false"
+
+ # check if cmdline contains -u or --update options, prior the -parameters
+ local -a prior_program_option_array
+ if [[ $PROGRAM_OPTION_INDEX == -1 ]]; then
+ prior_program_option_array=("${INTERPOLATED_ARGS[@]}")
+ else
+ prior_program_option_array=("${INTERPOLATED_ARGS[@]:0:PROGRAM_OPTION_INDEX}")
+ fi
+ local contains_u
+ contains_u=$(array_contains "-u" "${prior_program_option_array[@]}")
+ local contains_update
+ contains_update=$(array_contains "--update" "${prior_program_option_array[@]}")
+
+ if [[ "$option" == *u* ]] || [[ $contains_u == "true" ]] || [[ $contains_update == "true" ]]; then
+ need_update="true"
+ fi
+ compute_base_dir_from_resolved_remote_arg "$remote_arg" "$need_update"
+}
+
+#######################################
+# Computes the location of JeKa distribution directory according the JeKa version
+# used for the current BASE DIR. This may implies to dowload the distribution.
+# Global Vars:
+# JEKA_DIST_DIR (write)
+# Arguments:
+# $1 : the base directory
+# Outputs:
+# result in JEKA_DIST_DIR global args
+#######################################
+compute_JEKA_DIST_DIR() {
+ if [ "$JEKA_DIST_DIR" != "" ]; then
+ return 0
+ fi
+
+ local base_dir=$1
+ local explicit_distrib_dir
+ explicit_distrib_dir=$(get_prop_value_from_base_dir "$base_dir" "jeka.distrib.location")
+ if [ -n "$explicit_distrib_dir" ]; then
+ JEKA_DIST_DIR="$explicit_distrib_dir"
+ else
+ local jeka_version=
+ jeka_version=$(get_prop_value_from_base_dir "$base_dir" "jeka.version")
+ if [ -z "$jeka_version" ]; then
+ JEKA_DIST_DIR="$CURRENT_SCRIPT_DIR" # if no version and distrib location specified, use the current script dir
+ else
+ local distrib_cache_dir
+ distrib_cache_dir=$(get_cache_dir)/distributions/$jeka_version
+ if [ -d "$distrib_cache_dir" ]; then
+ JEKA_DIST_DIR="$distrib_cache_dir"
+
+ else
+ # select download repo
+ local jeka_repo
+ if [[ "$jeka_version" == *"-SNAPSHOT" ]]; then
+ jeka_repo="https://oss.sonatype.org/content/repositories/snapshots"
+ else
+ jeka_repo="https://repo.maven.apache.org/maven2"
+ fi
+ local distrib_repo
+ distrib_repo=$(get_prop_value_from_base_dir "$base_dir" "jeka.distrib.repo")
+ [ -n "$distrib_repo" ] && jeka_repo=$distrib_repo
+
+ local url=$jeka_repo/dev/jeka/jeka-core/$jeka_version/jeka-core-$jeka_version-distrib.zip
+ info "Download Jeka distrib from $url in $distrib_cache_dir"
+ download_and_unpack "$url" "$distrib_cache_dir"
+ JEKA_DIST_DIR=$distrib_cache_dir
+ fi
+ fi
+ fi
+}
+
+## Execute Jeka. Call `exec_jeka $base_dir`.
+## Returns value in JEKA_CLASSPATH
+compute_JEKA_CLASSPATH() {
+ if [ "$JEKA_CLASSPATH" != "" ]; then
+ return 0
+ fi
+ local dist_dir=$1
+ local bin_dir="$dist_dir"
+
+ # If no distrib dir is specified (no jeka.version specified), we look first
+ # for jeka-core.jar presence in the same dir of the current script
+ if [ -z "$dist_dir" ]; then ## No jeka.version is specified, should find the local one
+ if [ -f "$CURRENT_SCRIPT_DIR/$JEKA_JAR_NAME" ]; then
+ bin_dir="$CURRENT_SCRIPT_DIR"
+ fi
+ if [ -z "$dist_dir" ]; then
+ msg "No JeKa distribution found from script location $CURRENT_SCRIPT_DIR."
+ msg "You probably forgot to mention a 'jeka.version' or 'jeka.distrib.location' property in jeka.properties file."
+ exit 1
+ fi
+ fi
+
+ local boot_dir_args
+
+ ## Reference to remote found
+ if [ "$REMOTE_BASE_DIR" != "" ]; then
+ if [ -d "$REMOTE_BASE_DIR/jeka-boot" ]; then
+ boot_dir_args="$REMOTE_BASE_DIR/jeka-boot/*:"
+ fi
+
+ ## No remote script, launch on current dir
+ else
+ if [ -d "./jeka-boot" ]; then
+ boot_dir_args="./jeka-boot/*:"
+ fi
+ fi
+ local jar_file="$dist_dir/bin/$JEKA_JAR_NAME"
+ if [ ! -f "$jar_file" ]; then
+ jar_file="$dist_dir/$JEKA_JAR_NAME"
+ fi
+ if [ ! -f "$jar_file" ]; then
+ msg "Cannot find JeKa jar file $jar_file."
+ msg "Are you sure the JeKa distribution you use is properly packaged ?"
+ exit 1
+ fi
+ JEKA_CLASSPATH="$boot_dir_args$jar_file"
+}
+
+# call `get_or_download_jdk $JAVA_VERSION`. The result is set to DOWNLOAD_JDK_DIR var.
+get_or_download_jdk() {
+ local JAVA_VERSION="$1"
+ local specified_distrib
+ specified_distrib=$(get_prop_value_from_base_dir "$BASE_DIR" "jeka.java.distrib")
+ if [ -n "$specified_distrib" ]; then
+ JDK_DOWNLOAD_DISTRIB="$specified_distrib"
+ fi
+ local jdk_cache_dir
+ jdk_cache_dir="$(get_cache_dir)/jdks/$JDK_DOWNLOAD_DISTRIB-$JAVA_VERSION"
+ if [ ! -d "$jdk_cache_dir" ]; then
+ if [ -z "$JDK_DOWNLOAD_OS" ]; then
+ msg "Unable to download JDK, unsupported Operating System: $(uname -s)"
+ msg "You may workaround this problem by specifying a 'jeka.jdk.$JAVA_VERSION' env var or property in ~/jeka/global.properties file."
+ exit 1
+ fi
+ if [ -z "$JDK_DOWNLOAD_ARCH" ]; then
+ msg "Unable to download JDK, unsupported Architecture: $(uname -m)"
+ msg "You may workaround this problem by specifying a 'jeka.jdk.$JAVA_VERSION' env var or property in ~/jeka/global.properties file."
+ exit 1
+ fi
+ local download_url="https://api.foojay.io/disco/v3.0/directuris?distro=$JDK_DOWNLOAD_DISTRIB&javafx_bundled=false&libc_type=$JDK_DOWNLOAD_LIBC_TYPE&archive_type=$JDK_DOWNLOAD_FILE_TYPE&operating_system=$JDK_DOWNLOAD_OS&package_type=jdk&version=$JAVA_VERSION&architecture=$JDK_DOWNLOAD_ARCH&latest=available"
+ info "Downloading JDK $JDK_DOWNLOAD_DISTRIB $JAVA_VERSION to $jdk_cache_dir. It may take a while..."
+ download_and_unpack "$download_url" "$jdk_cache_dir" "$JDK_DOWNLOAD_FILE_TYPE"
+ if [ "tar.gz" == "$JDK_DOWNLOAD_FILE_TYPE" ]; then
+ pushd "$jdk_cache_dir" > /dev/null 2>&1
+ local nested_dir
+ nested_dir=$(find "." -mindepth 1 -maxdepth 1 -type d | head -n 1 | cut -c 3-)
+ popd > /dev/null 2>&1
+ temp_dir=$(mktemp -d)
+ if [ "$JDK_DOWNLOAD_OS" = "mac" ]; then
+ nested_dir+="/Contents/Home"
+ fi
+ mv "$jdk_cache_dir"/"$nested_dir"/* "$temp_dir"
+ mv "$temp_dir"/* "$jdk_cache_dir"
+ fi
+ fi
+ DOWNLOAD_JDK_DIR=$jdk_cache_dir
+}
+
+#######################################
+# Computes Java command according version, distrib, os and arch, implying optional
+# JDK download
+# Arguments:
+# Global vars:
+# JDK_DOWNLOAD_OS
+# JDK_DOWNLOAD_LIBC_TYPE
+# JDK_DOWNLOAD_ARCH
+# JAVA_VERSION
+# JAVA_HOME
+# DEFAULT_JAVA_VERSION (read)
+# JEKA_JDK_HOME
+# IS_VERBOSE (read)
+# BASE_DIR (read)
+# JAVA_CMD
+# Outputs:
+# result in BASE_DIR global args
+#######################################
+compute_JAVA_CMD() {
+ if [ "$JAVA_CMD" != "" ]; then
+ return 0;
+ fi
+
+ # OS specific support. $var _must_ be set to either true or false.
+ case "$(uname -s)" in
+ Linux*)
+ JDK_DOWNLOAD_OS="linux"
+ if [ -f /etc/alpine-release ]; then
+ JDK_DOWNLOAD_OS=alpine-linux
+ fi
+ ;;
+ Darwin*)
+ JDK_DOWNLOAD_OS="mac"
+ JDK_DOWNLOAD_LIBC_TYPE="libc"; # necessary to download proper JDK
+ ;;
+ esac
+
+ case "$(uname -m)" in
+ i?86)
+ JDK_DOWNLOAD_ARCH="x32";;
+ x86_64|amd64)
+ JDK_DOWNLOAD_ARCH="x64";;
+ aarch64)
+ JDK_DOWNLOAD_ARCH="aarch64";;
+ armv7l)
+ JDK_DOWNLOAD_ARCH="arm";;
+ ppc64le)
+ JDK_DOWNLOAD_ARCH="ppc64le";;
+ s390x)
+ JDK_DOWNLOAD_ARCH="s390x";;
+ arm64)
+ JDK_DOWNLOAD_ARCH="arm64"
+ ;;
+ *)
+ JDK_DOWNLOAD_ARCH=""
+ ;;
+ esac
+
+ # Determines JAVA_HOME
+ JAVA_VERSION=$(get_java_version_from_props "$BASE_DIR")
+
+ if [ -n "$JEKA_JDK_HOME" ]; then # We can enforce usage of a specific JDK by setting JEKA_JDK_HOME env var
+ JAVA_HOME="$JEKA_JDK_HOME"
+
+ elif [ -n "$JAVA_VERSION" ] || [ -z "$JAVA_HOME" ]; then # if a Java version is specified in then use one of the JeKa managed JDK
+ if [ -z "$JAVA_VERSION" ]; then
+ JAVA_VERSION=$"$DEFAULT_JAVA_VERSION"
+ if [ -n "$IS_VERBOSE" ]; then
+ info "No JAVA_HOME defined and no jeka.java.version defined. Use Java $DEFAULT_JAVA_VERSION."
+ fi
+ fi
+ jdkPath=$(get_jdk_home_from_props "$BASE_DIR" "$JAVA_VERSION")
+ debug "JDK HOME $JAVA_VERSION from env or props : $jdkPath "
+ if [ -z "$jdkPath" ]; then
+ get_or_download_jdk "$JAVA_VERSION"
+ JAVA_HOME="$DOWNLOAD_JDK_DIR"
+ fi
+ fi
+
+ # Determines JAVA_CMD to use according JAVA_HOME
+ if [ -z "$JAVA_CMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVA_CMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVA_CMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVA_CMD="$(which java)"
+ fi
+ fi
+
+ if [ ! -x "$JAVA_CMD" ] ; then
+ msg "Error: JAVA_HOME is not defined correctly (valued to $JAVA_HOME )."
+ msg " We cannot execute $JAVA_CMD" >&2
+ msg " You can specify which JDK to use by setting JEKA_JDK environment variable."
+ exit 1
+ fi
+
+}
+
+#######################################
+# Execute java program if requested.
+# Program execution is requested when '-p' is present in cmd line args.
+# The args following '-p' are to be passed to program args.
+# First, Jeka try to find an executable bin or jar, in 'jeka-output'
+# If there is none, jeka launches build and retry.
+#
+# Global Vars:
+# BASE_DIR (read)
+# JEKA_DIST_DIR (write)
+# RETRY_AFTER_BUILD (read/write)
+# INTERPOLATED_ARGS (read)
+# PROGRAM_OPTION_INDEX (read)
+# Outputs:
+# None. Exit anyway if program launch has been requested
+#######################################
+execute_program_if_requested() {
+ if [[ $PROGRAM_OPTION_INDEX == -1 ]]; then
+ return 0
+ fi
+ local -i from_arg_index
+ from_arg_index=$((PROGRAM_OPTION_INDEX+1))
+ local program_args=("${INTERPOLATED_ARGS[@]:$from_arg_index}")
+ execute_and_exit_native_if_present "${program_args[@]}"
+ execute_and_exit_java_if_present "${program_args[@]}"
+
+ if [[ "$RETRY_AFTER_BUILD" == "true" ]]; then
+ msg "Cannot find a native or jar executable in $BASE_DIR/jeka-output"
+ exit 1
+ fi
+
+ ## If we are here, this means that no native or jar has been found -> build
+ debug "Launch a build to generate executable"
+ compute_JAVA_CMD
+ compute_JEKA_DIST_DIR "$BASE_DIR"
+ compute_JEKA_CLASSPATH "$JEKA_DIST_DIR"
+ local -a heading_args=("${INTERPOLATED_ARGS[@]::$PROGRAM_OPTION_INDEX}")
+
+ local build_cmd
+ build_cmd=$(get_prop_value_from_base_dir "$BASE_DIR" "jeka.program.build")
+ if [[ -z "$build_cmd" ]]; then
+ if [ -d "$BASE_DIR/src" ]; then
+ build_cmd="project: pack -Djeka.skip.tests=true --stderr"
+ else
+ build_cmd="base: pack -Djeka.skip.tests=true --stderr"
+ fi
+ fi
+
+ # shellcheck disable=SC2206
+ local -a build_args=($build_cmd)
+ # shellcheck disable=SC2145
+ info "Launch build with command : ${heading_args[@]} ${build_args[@]}"
+ "$JAVA_CMD" "${JEKA_OPTS[@]}" "-Djeka.current.basedir=$BASE_DIR" -cp "$JEKA_CLASSPATH" "dev.jeka.core.tool.Main" "${heading_args[@]}" "${build_args[@]}"
+ RETRY_AFTER_BUILD="true"
+ execute_program_if_requested
+
+}
+
+#######################################
+# Gets the index of '-p' or '--program' in the given array
+# Returns -1, if no such element found
+# Globals:
+# none
+# Arguments:
+# $1 : the arrays where we are searching element in
+# $2 : the array providing elements to search
+# Outputs:
+# Write to stdout
+#######################################
+compute_PROGRAM_OPTION_INDEX() {
+ local -i index=0
+ local -a items=("$@")
+ for item in "${items[@]}"; do
+ if [ "$item" == "-p" ] || [ "$item" == "--program" ]; then
+ PROGRAM_OPTION_INDEX="$index"
+ return 0
+ fi
+ ((index += 1))
+
+ done
+ PROGRAM_OPTION_INDEX=-1
+}
+
+######################################
+# Execute and exit native program if present
+#
+# Global Vars:
+# BASE_DIR (read)
+# Arguments:
+# $1 : args array to pass to native program
+# Outputs:
+# None. Exit anyway if program found
+#######################################
+execute_and_exit_native_if_present() {
+ local exe_file;
+ for file in "$BASE_DIR"/jeka-output/*; do
+ if [ -f "$file" ] && [ -x "$file" ] && [[ "$file" != *".jar" ]]; then
+ exe_file="$file"
+ break
+ fi
+ done
+ if [ "$exe_file" == "" ]; then
+ debug "No native exe file found in $BASE_DIR/jeka-output/*"
+ return 0
+ fi
+ exec "$exe_file" "$@"
+ exit $?
+}
+
+######################################
+# Execute and exit java program if present
+#
+# Global Vars:
+# BASE_DIR (read)
+# Arguments:
+# $1 : args array to pass to native program
+# Outputs:
+# None. Exit anyway if program found
+#######################################
+execute_and_exit_java_if_present() {
+ local jar_file;
+ for file in "$BASE_DIR"/jeka-output/*; do
+ if [ -f "$file" ] && [[ "$file" == *".jar" ]]; then
+ jar_file="$file"
+ break
+ fi
+ done
+ if [ "$jar_file" == "" ]; then
+ debug "No Jar file found in $BASE_DIR/jeka-output/*"
+ return 0
+ fi
+ compute_JAVA_CMD
+ local -a sysProp_params
+ filter_in_sysProp "$@"
+ sysProp_params=("${returned[@]}")
+ local -a regular_params
+ filter_out_sysProp "$@"
+ regular_params=("${returned[@]}")
+ exec "$JAVA_CMD" "${sysProp_params[@]}" -jar "$jar_file" "${regular_params[@]}"
+ exit $?
+}
+
+######################################
+# Filter array keeping only items that match '-Dxxx=yyy' (sys prop)
+#
+# Global Vars:
+# returned (write)
+# Arguments:
+# $@ : arrays to filter
+# Outputs:
+# The filtered array is written to in the 'returned' global var
+#######################################
+filter_in_sysProp() {
+ local arr=("$@")
+ returned=()
+ for i in "${arr[@]}"; do
+ if [[ $i == -D* ]] && [[ $i == *"="* ]]; then
+ returned+=("$i")
+ fi
+ done
+}
+
+######################################
+# Filter array removing items that match '-Dxxx=yyy' (sys prop)
+#
+# Global Vars:
+# returned (write)
+# Arguments:
+# $@ : arrays to filter
+# Outputs:
+# The filtered array is written to in the 'returned' global var
+#######################################
+filter_out_sysProp() {
+ local arr=("$@")
+ returned=()
+ for i in "${arr[@]}"; do
+ if [[ $i != -D* ]] || [[ $i != *"="* ]]; then
+ returned+=("$i")
+ fi
+ done
+}
+
+##############################################################
+# Script starts here
+##############################################################
+
+compute_VERBOSE_QUIET
+JEKA_USER_HOME=$(get_jeka_user_home)
+GLOBAL_PROP_FILE="$JEKA_USER_HOME/global.properties"
+
+compute_INTERPOLATED_ARGS "${CMD_LINE_ARGS[@]}"
+compute_PROGRAM_OPTION_INDEX "${INTERPOLATED_ARGS[@]}"
+compute_BASE_DIR "${INTERPOLATED_ARGS[@]}"
+
+execute_program_if_requested
+
+compute_JAVA_CMD
+
+## When debugging we don't want to execute Jeka
+if [ -z "$DRY_RUN" ]; then
+ compute_JEKA_DIST_DIR "$BASE_DIR"
+ compute_JEKA_CLASSPATH "$JEKA_DIST_DIR"
+
+ exec "$JAVA_CMD" "${JEKA_OPTS[@]}" "-Djeka.current.basedir=$BASE_DIR" -cp "$JEKA_CLASSPATH" "dev.jeka.core.tool.Main" "$@"
+fi
diff --git a/jeka.properties b/jeka.properties
index 261393a..d0b366e 100644
--- a/jeka.properties
+++ b/jeka.properties
@@ -1,5 +1,4 @@
-#jeka.version=0.11.0-alpha.3
+jeka.version=0.11.0-alpha.5
jeka.java.version=8
-jeka.java.distrib=corretto
-jeka.cmd.build=base: pack
\ No newline at end of file
+
diff --git a/jeka.ps1 b/jeka.ps1
new file mode 100644
index 0000000..7b07ec1
--- /dev/null
+++ b/jeka.ps1
@@ -0,0 +1,619 @@
+#Requires -Version 5
+
+#
+# Copyright 2014-2024 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Author : Jérôme Angibaud
+#
+# Execute JeKa by Downloading requiered potentialy missing dependencies (JDK, JeKa version)
+# The passed arguments are interpolated then passed to JeKA engine.
+#
+
+function MessageInfo {
+ param([string]$msg)
+ if ($global:QuietFlag -ne $true) {
+ [Console]::Error.WriteLine($msg)
+ }
+
+}
+
+function MessageVerbose {
+ param([string]$msg)
+
+ if (($VerbosePreference -eq "Continue") -and ($global:QuietFlag -ne $true)) {
+ [Console]::Error.WriteLine($msg)
+ }
+
+}
+
+function Exit-Error {
+ param([string]$msg)
+
+ Write-Host $msg -ForegroundColor Red
+ exit 1
+}
+
+function Get-JekaUserHome {
+ if ([string]::IsNullOrEmpty($env:JEKA_USER_HOME)) {
+ return "$env:USERPROFILE\.jeka"
+ }
+ else {
+ return $env:JEKA_USER_HOME
+ }
+}
+
+function Get-CacheDir([string]$jekaUserHome) {
+ if ([string]::IsNullOrEmpty($env:JEKA_CACHE_DIR)) {
+ return $jekaUserHome + "\cache"
+ }
+ else {
+ return $env:JEKA_CACHE_DIR
+ }
+}
+
+class BaseDirResolver {
+ [string]$url
+ [string]$cacheDir
+ [bool]$updateFlag
+
+ BaseDirResolver([string]$url, [string]$cacheDir, [bool]$updateFlag) {
+ $this.url = $url
+ $this.cacheDir = $cacheDir
+ $this.updateFlag = $updateFlag
+ }
+
+ [string] GetPath() {
+ if ($this.url -eq '') {
+ return $PWD.Path
+ }
+ if ($this.IsGitRemote() -eq $false) {
+ return $this.url
+ }
+ $path = $this.cacheDir + "\git\" + $this.FolderName()
+ if ([System.IO.Directory]::Exists($path)) {
+ if ($this.updateFlag) {
+ Remove-Item -Path $path -Recurse -Force
+ $this.GitClone($path)
+ }
+ } else {
+ $this.GitClone($path)
+ }
+ return $path
+ }
+
+ hidden [void] GitClone([string]$path) {
+ $branchArgs = $this.SubstringAfterHash()
+ if ($branchArgs -ne '') {
+ $branchArgs = "--branch " + $branchArgs
+ }
+ $repoUrl = $this.SubstringBeforeHash()
+ MessageInfo "Git clone $repoUrl"
+ $gitCmd = "git clone --quiet -c advice.detachedHead=false --depth 1 $branchArgs $repoUrl $path"
+ Invoke-Expression -Command $gitCmd
+ }
+
+ hidden [bool] IsGitRemote() {
+ $gitUrlRegex = '(https?://*)|^(ssh://*)|^(git://*)|^(git@[^:]+:*)$'
+ $myUrl = $this.url
+ if ($myUrl -match $gitUrlRegex) {
+ return $true;
+ } else {
+ return $false;
+ }
+ }
+
+ hidden [string] SubstringBeforeHash() {
+ return $this.url.Split('#')[0]
+ }
+
+ hidden [string] SubstringAfterHash() {
+ return $this.url.Split('#')[1]
+ }
+
+ hidden [string] FolderName() {
+ $protocols = @('https://', 'ssh://', 'git://', 'git@')
+ $trimmedUrl = $this.url
+ foreach ($protocol in $protocols) {
+ $trimmedUrl = $trimmedUrl -replace [regex]::Escape($protocol), ''
+ }
+ # replace '/' by '_'
+ $folderName = $trimmedUrl -replace '/', '_'
+ return $folderName
+ }
+
+}
+
+class CmdLineArgs {
+ [Array]$args
+
+ CmdLineArgs([Array]$arguments) {
+ $this.args = $arguments
+ }
+
+ [string] GetSystemProperty([string]$propName) {
+ $prefix = "-D$propName="
+ foreach ($arg in $this.args) {
+ if ($arg -eq $null) {
+ continue
+ }
+ if ( $arg.StartsWith($prefix) ) {
+ return $arg.Replace($prefix, "")
+ }
+ }
+ return $null
+ }
+
+ [int] GetIndexOfFirstOf([Array]$candidates) {
+ foreach ($arg in $candidates) {
+ $index = $this.args.IndexOf($arg)
+ if ($index -ne -1) {
+ return $index
+ }
+ }
+ return -1
+ }
+
+ [string] GetRemoteBaseDirArg() {
+ $remoteArgs= @("-r", "--remote", "-ru", "-ur")
+ $remoteIndex= $this.GetIndexOfFirstOf($remoteArgs)
+ if ($remoteIndex -eq -1) {
+ return $null
+ }
+ return $this.args[$remoteIndex + 1]
+ }
+
+ [array] GetProgramArgs() {
+ $index = $this.GetProgramArgIndex() + 1
+ return $this.args[$index..$this.args.Length]
+ }
+
+ [array] GetPriorProgramArgs() {
+ $index = $this.GetProgramArgIndex() - 1
+ if ($index -lt 0) {
+ return @()
+ }
+ return $this.args[0..$index]
+ }
+
+ [bool] IsUpdateFlagPresent() {
+ $remoteArgs= @("-ru", "-ur", "-u", "--remote-update")
+ $remoteIndex= $this.GetIndexOfFirstOf($remoteArgs)
+ return ($remoteIndex -ne -1)
+ }
+
+ [bool] IsQuietFlagPresent() {
+ $remoteArgs= @("-q", "--quiet")
+ $remoteIndex= $this.GetIndexOfFirstOf($remoteArgs)
+ return ($remoteIndex -ne -1)
+ }
+
+ [bool] IsVerboseFlagPresent() {
+ $remoteArgs= @("-v", "--verbose", "--debug")
+ $remoteIndex= $this.GetIndexOfFirstOf($remoteArgs)
+ return ($remoteIndex -ne -1)
+ }
+
+ [bool] IsProgramFlagPresent() {
+ $remoteIndex= $this.GetProgramArgIndex()
+ return ($remoteIndex -ne -1)
+ }
+
+ [array] FilterOutSysProp() {
+ $result = @()
+ foreach ($item in $this.args) {
+ if (!($item.StartsWith("-D") -AND ($item.Contains("="))) ) {
+ $result += $item
+ }
+ }
+ return $result
+ }
+
+ [array] FilterInSysProp() {
+ $result = @()
+ foreach ($item in $this.args) {
+ if ($item.StartsWith("-D") -AND ($item.Contains("=")) ) {
+ $result += $item
+ }
+ }
+ return $result
+ }
+
+ hidden [Int16] GetProgramArgIndex() {
+ $remoteArgs= @("-p", "--program")
+ return $this.GetIndexOfFirstOf($remoteArgs)
+ }
+
+}
+
+class Props {
+ [CmdLineArgs]$cmdLineArgs
+ [string]$baseDir
+ [string]$globalPropFile
+
+ Props([CmdLineArgs]$cmdLineArgs, [string]$baseDir, [string]$globalPropFile ) {
+ $this.cmdLineArgs = $cmdLineArgs
+ $this.baseDir = $baseDir
+ $this.globalPropFile = $globalPropFile
+ }
+
+ [string] GetValue([string]$propName) {
+ $cmdArgsValue = $this.cmdLineArgs.GetSystemProperty($propName)
+ if ($cmdArgsValue -ne '') {
+ return $cmdArgsValue
+ }
+ $envValue = [Environment]::GetEnvironmentVariable($propName)
+ if ($envValue) {
+ return $envValue
+ }
+
+ $jekaPropertyFilePath = $this.baseDir + '\jeka.properties'
+
+ $value = [Props]::GetValueFromFile($jekaPropertyFilePath, $propName)
+ if ('' -eq $value) {
+ $parentDir = $this.baseDir + '\..'
+ $parentJekaPropsFile = $parentDir + '\jeka.properties'
+ if (Test-Path $parentJekaPropsFile) {
+ $value = [Props]::GetValueFromFile($parentJekaPropsFile, $propName)
+ } else {
+ $value = [Props]::GetValueFromFile($this.globalPropFile, $propName)
+ }
+ }
+ return $value
+ }
+
+ [string] GetValueOrDefault([string]$propName, [string]$defaultValue) {
+ $value = $this.GetValue($propName)
+ if ($value -eq '') {
+ return $defaultValue
+ } else {
+ return $value
+ }
+ }
+
+ [CmdLineArgs] InterpolatedCmdLine() {
+ $result = @()
+ foreach ($arg in $this.cmdLineArgs.args) {
+ if ($arg -like "::*") {
+ $propKey= ( "jeka.cmd." + $arg.Substring(2) )
+ $propValue= $this.GetValue($propKey)
+ if ('' -ne $propValue) {
+ $valueArray= [Props]::ParseCommandLine($propValue)
+ $result += $valueArray
+ } else {
+ $result += $arg
+ }
+ } else {
+ $result += $arg
+ }
+ }
+ return [CmdLineArgs]::new($result)
+ }
+
+ static [array] ParseCommandLine([string]$cmdLine) {
+ $pattern = '(""[^""]*""|[^ ]*)'
+ $regex = New-Object Text.RegularExpressions.Regex $pattern
+ return $regex.Matches($cmdLine) | ForEach-Object { $_.Value.Trim('"') } | Where-Object { $_ -ne "" }
+ }
+
+ static [string] GetValueFromFile([string]$filePath, [string]$propertyName) {
+ if (! [System.IO.File]::Exists($filePath)) {
+ return $null
+ }
+ $data = [System.IO.File]::ReadAllLines($filePath)
+ foreach ($line in $data) {
+ if ($line -match "^$propertyName=") {
+ $keyLength = $propertyName.Length + 1
+ $value = $line.Substring($keyLength)
+ return $value
+ }
+ }
+ return $null
+ }
+
+}
+
+class Jdks {
+ [Props]$Props
+ [string]$CacheDir
+
+ Jdks([Props]$props, [string]$cacheDir) {
+ $this.Props = $props
+ $this.CacheDir = $cacheDir
+ }
+
+ [string] GetJavaCmd() {
+ $javaHome = $this.JavaHome()
+ $javaExe = $this.JavaExe($javaHome)
+ return $javaExe
+ }
+
+ hidden Install([string]$distrib, [string]$version, [string]$targetDir) {
+ MessageInfo "Downloading JDK $distrib $version. It may take a while..."
+ $jdkurl="https://api.foojay.io/disco/v3.0/directuris?distro=$distrib&javafx_bundled=false&libc_type=c_std_lib&archive_type=zip&operating_system=windows&package_type=jdk&version=$version&architecture=x64&latest=available"
+ $zipExtractor = [ZipExtractor]::new($jdkurl, $targetDir)
+ $zipExtractor.ExtractRootContent()
+ }
+
+ hidden [string] CachedJdkDir([string]$version, [string]$distrib) {
+ return $this.CacheDir + "\jdks\" + $distrib + "-" + $version
+ }
+
+ hidden [string] JavaExe([string]$javaHome) {
+ return $javaHome + "\bin\java.exe"
+ }
+
+ hidden [string] JavaHome() {
+ if ($Env:JEKA_JAVA_HOME -ne $null) {
+ return $Env:JEKA_JAVA_HOME
+ }
+ $version = ($this.Props.GetValueOrDefault("jeka.java.version", "21"))
+ $distib = ($this.Props.GetValueOrDefault("jeka.java.distrib", "temurin"))
+ $cachedJdkDir = $this.CachedJdkDir($version, $distib)
+ $javaExeFile = $this.JavaExe($cachedJdkDir)
+ if (! [System.IO.File]::Exists($javaExeFile)) {
+ $this.Install($distib, $version, $cachedJdkDir)
+ }
+ return $cachedJdkDir
+ }
+
+}
+
+class JekaDistrib {
+ [Props]$props
+ [String]$cacheDir
+
+ JekaDistrib([Props]$props, [string]$cacheDir) {
+ $this.props = $props
+ $this.cacheDir = $cacheDir
+ }
+
+ [string] GetBinDir() {
+ $specificLocation = $this.props.GetValue("jeka.distrib.location")
+ if ($specificLocation -ne '') {
+ return $specificLocation
+ }
+ $jekaVersion = $this.props.GetValue("jeka.version")
+
+ # If version not specified, use jeka jar present in running distrib
+ if ($jekaVersion -eq '') {
+ $dir = $PSScriptRoot
+ $jarFile = $dir + "\dev.jeka.jeka-core.jar"
+ if (! [System.IO.File]::Exists($jarFile)) {
+ Write-Host "No Jeka jar file found at $jarFile" -ForegroundColor Red
+ Write-Host "This is due that neither jeka.distrib.location nor jeka.version are specified in properties, " -ForegroundColor Red
+ Write-Host "and you are probably invoking local 'jeka.ps1'" -ForegroundColor Red
+ Write-Host "Specify one the mentionned above properties or invoke 'jeka' if JeKa is installed on host machine." -ForegroundColor Red
+ exit 1
+ }
+ return $dir
+ }
+
+ $distDir = $this.cacheDir + "\distributions\" + $jekaVersion
+ if ( [System.IO.Directory]::Exists($distDir)) {
+ return "$distDir\bin"
+ }
+
+ $distRepo = $this.props.GetValueOrDefault("jeka.distrib.repo", "https://repo.maven.apache.org/maven2")
+ $url = "$distRepo/dev/jeka/jeka-core/$jekaVersion/jeka-core-$jekaVersion-distrib.zip"
+ $zipExtractor = [ZipExtractor]::new($url, $distDir)
+ $zipExtractor.Extract()
+ return "$distDir\bin"
+ }
+
+ [string] GetJar() {
+ return $this.GetBinDir() + "\dev.jeka.jeka-core.jar"
+ }
+
+}
+
+class ZipExtractor {
+ [string]$url
+ [string]$dir
+
+ ZipExtractor([string]$url, [string]$dir) {
+ $this.url = $url
+ $this.dir = $dir
+ }
+
+ ExtractRootContent() {
+ $zipFile = $this.Download()
+ $tempDir = [System.IO.Path]::GetTempFileName()
+ Remove-Item -Path $tempDir
+ Expand-Archive -Path $zipFile -DestinationPath $tempDir -Force
+ $subDirs = Get-ChildItem -Path $tempDir -Directory
+ $root = $tempDir + "\" + $subDirs[0]
+ Move-Item -Path $root -Destination $this.dir -Force
+ Remove-Item -Path $zipFile
+ Remove-Item -Path $tempDir -Recurse
+ }
+
+ Extract() {
+ $zipFile = $this.Download()
+ Expand-Archive -Path $zipFile -DestinationPath $this.dir -Force
+ Remove-Item -Path $zipFile
+ }
+
+ hidden [string] Download() {
+ $downloadFile = [System.IO.Path]::GetTempFileName() + ".zip"
+ $webClient = New-Object System.Net.WebClient
+ $webClient.DownloadFile($this.url, $downloadFile)
+ $webClient.Dispose()
+ return $downloadFile
+ }
+}
+
+class ProgramExecutor {
+ [string]$folder
+ [array]$cmdLineArgs
+
+ ProgramExecutor([string]$folder, [array]$cmdLineArgs) {
+ $this.folder = $folder
+ $this.cmdLineArgs = $cmdLineArgs
+ }
+
+ Exec([string]$javaCmd, [string]$progFile) {
+ if ($progFile.EndsWith('.exe')) {
+ & "$progFile" $this.cmdLineArgs
+ } else {
+ & "$javaCmd" -jar "$progFile" $this.cmdLineArgs
+ }
+ }
+
+ [string] FindProg() {
+ $dir = $this.folder
+ $exist = [System.IO.Directory]::Exists("$dir")
+ if (-not (Test-Path $dir)) {
+ return $null
+ }
+ $exeFile = $this.findFile(".exe")
+ if ($exeFile -ne '') {
+ return $exeFile
+ }
+ return $this.findFile(".jar")
+ }
+
+ hidden [string] findFile([string]$extension) {
+ $files = Get-ChildItem -Path $this.folder -Filter "*$extension"
+ if ($files) {
+ $firstFile = $files | Select-Object -First 1
+ return $firstFile.FullName
+ }
+ return $null
+ }
+}
+
+function ExecJekaEngine {
+ param(
+ [string]$baseDir,
+ [string]$cacheDir,
+ [Props]$props,
+ [string]$javaCmd,
+ [array]$cmdLineArgs,
+ [Parameter(Mandatory=$false)][bool]$stderr = $false
+ )
+
+ $jekaDistrib = [JekaDistrib]::new($props, $cacheDir)
+ $jekaJar = $jekaDistrib.GetJar()
+ $classpath = "$baseDir\jeka-boot\*;$jekaJar"
+ $jekaOpts = $Env:JEKA_OPTS
+ $baseDirProp = "-Djeka.current.basedir=$baseDir"
+ & "$javaCmd" $jekaOpts "$baseDirProp" -cp "$classpath" "dev.jeka.core.tool.Main" $cmdLineArgs
+}
+
+function ExecProg {
+ param(
+ [string]$javaCmd,
+ [string]$progFile,
+ [array]$cmdLineArgs
+ )
+
+ $argLine = $cmdLineArgs -join ' '
+ if ($progFile.EndsWith('.exe')) {
+ Write-Verbose "Run native program $progFile with args $argLine"
+ & "$progFile" $cmdLineArgs
+ } else {
+ $cmdLineArgs = [CmdLineArgs]::new($cmdLineArgs)
+ $sypPropArgs = $cmdLineArgs.FilterInSysProp()
+ $sanitizedProgArgs = $cmdLineArgs.FilterOutSysProp()
+ Write-Verbose "Run Java program $progFile with args $argLine"
+ & "$javaCmd" -jar "$sypPropArgs" "$progFile" $sanitizedProgArgs
+ }
+}
+
+function Main {
+ param(
+ [array]$arguments
+ )
+
+ #$argLine = $arguments -join '|'
+ #MessageInfo "Raw arguments |$argLine|"
+ $jekaUserHome = Get-JekaUserHome
+ $cacheDir = Get-CacheDir($jekaUserHome)
+ $globalPropFile = $jekaUserHome + "\global.properties"
+
+ # Get interpolated cmdLine, while ignoring Base dir
+ $rawCmdLineArgs = [CmdLineArgs]::new($arguments)
+ if ($rawCmdLineArgs.IsQuietFlagPresent()) {
+ $global:QuietFlag = $true
+ }
+ $rawProps = [Props]::new($rawCmdLineArgs, $PWD.Path, $globalPropFile)
+ $cmdLineArgs = $rawProps.InterpolatedCmdLine()
+
+ # Resolve basedir and interpolate cmdLine according props declared in base dir
+ $remoteArg = $cmdLineArgs.GetRemoteBaseDirArg()
+ $updateArg = $cmdLineArgs.IsUpdateFlagPresent()
+ $baseDirResolver = [BaseDirResolver]::new($remoteArg, $cacheDir, $updateArg)
+ $baseDir = $baseDirResolver.GetPath()
+ $props = [Props]::new($cmdLineArgs, $baseDir, $globalPropFile)
+ $cmdLineArgs = $props.InterpolatedCmdLine()
+ $joinedArgs = $cmdLineArgs.args -join " "
+ if ($cmdLineArgs.IsVerboseFlagPresent()) {
+ $VerbosePreference = 'Continue'
+ }
+ MessageVerbose "Interpolated cmd line : $joinedArgs"
+
+ # Compute Java command
+ $jdks = [Jdks]::new($props, $cacheDir)
+ $javaCmd = $jdks.GetJavaCmd()
+
+ # -p option present : try to execute program directly, bypassing jeka engine
+ if ($cmdLineArgs.IsProgramFlagPresent()) {
+ $progArgs = $cmdLineArgs.GetProgramArgs() # arguments metionned after '-p'
+ $progDir = $baseDir + "\jeka-output"
+ $prog = [ProgramExecutor]::new($progDir, $progArgs)
+ $progFile = $prog.FindProg()
+ if ($progFile -ne '') {
+ ExecProg -javaCmd $javaCmd -progFile $progFile -cmdLineArgs $progArgs
+ exit $LASTEXITCODE
+ }
+
+ # No executable or Jar found : launch a build
+ $buildCmd = $props.GetValue("jeka.program.build")
+ MessageInfo "jeka.program.build=$buildCmd"
+ if (!$buildCmd) {
+ $srcDir = $baseDir + "\src"
+ if ([System.IO.Directory]::Exists($srcDir)) {
+ $buildCmd = "project: pack -Djeka.skip.tests=true --stderr"
+ } else {
+ $buildCmd = "base: pack -Djeka.skip.tests=true --stderr"
+ }
+ }
+ $buildArgs = [Props]::ParseCommandLine($buildCmd)
+ $leadingArgs = $cmdLineArgs.GetPriorProgramArgs()
+ $effectiveArgs = $leadingArgs + $buildArgs
+ MessageInfo "Building with command : $effectiveArgs"
+ ExecJekaEngine -baseDir $baseDir -cacheDir $cacheDir -props $props -javaCmd $javaCmd -cmdLineArgs $effectiveArgs
+ if ($LASTEXITCODE -ne 0) {
+ Exit-Error "Build exited with error code $LASTEXITCODE. Cannot execute program"
+ }
+ $progFile = $prog.FindProg()
+ if ($progFile -eq '') {
+ Exit-Error "No program found to be executed in $progDir"
+ }
+ ExecProg -javaCmd $javaCmd -progFile $progFile -cmdLineArgs $progArgs
+ exit $LASTEXITCODE
+
+ # Execute Jeke engine
+ } else {
+ ExecJekaEngine -javaCmd $javaCmd -baseDir $baseDir -cacheDir $cacheDir -props $props -cmdLineArgs $cmdLineArgs.args
+ exit $LASTEXITCODE
+ }
+
+}
+
+$ErrorActionPreference = "Stop"
+Main -arguments $args
\ No newline at end of file