diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index 7f5cc8694..8a8eb79ea 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -1,7 +1,7 @@
-
+
-
+
\ No newline at end of file
diff --git a/codeAst/src/prog8/code/ast/AstBase.kt b/codeAst/src/prog8/code/ast/AstBase.kt
index 1ef0b4058..82af97b5c 100644
--- a/codeAst/src/prog8/code/ast/AstBase.kt
+++ b/codeAst/src/prog8/code/ast/AstBase.kt
@@ -1,9 +1,7 @@
package prog8.code.ast
-import prog8.code.core.IMemSizer
-import prog8.code.core.IStringEncoding
-import prog8.code.core.Position
-import java.util.*
+import prog8.code.core.*
+import java.nio.file.Path
// New (work-in-progress) simplified AST for the code generator.
@@ -41,11 +39,11 @@ class PtNodeGroup: PtNode(Position.DUMMY) {
abstract class PtNamedNode(val name: String, position: Position): PtNode(position) {
val scopedName: List by lazy {
- if(this is PtModule)
- emptyList()
+ var namedParent: PtNode = this.parent
+ if(namedParent is PtProgram)
+ listOf(name)
else {
- var namedParent: PtNode = this.parent
- while(namedParent !is PtNamedNode)
+ while (namedParent !is PtNamedNode)
namedParent = namedParent.parent
namedParent.scopedName + name
}
@@ -53,8 +51,22 @@ abstract class PtNamedNode(val name: String, position: Position): PtNode(positio
}
+class ProgramOptions(
+ val output: OutputType,
+ val launcher: CbmPrgLauncherType,
+ val zeropage: ZeropageType,
+ val zpReserved: Collection,
+ val loadAddress: UInt?,
+ val floatsEnabled: Boolean,
+ val noSysInit: Boolean,
+ val dontReinitGlobals: Boolean,
+ val optimize: Boolean
+)
+
+
class PtProgram(
val name: String,
+ val options: ProgramOptions,
val memsizer: IMemSizer,
val encoding: IStringEncoding
) : PtNode(Position.DUMMY) {
@@ -63,29 +75,17 @@ class PtProgram(
print("'$name'")
}
- fun allModuleDirectives(): Sequence =
- children.asSequence().flatMap { it.children }.filterIsInstance().distinct()
+// fun allModuleDirectives(): Sequence =
+// children.asSequence().flatMap { it.children }.filterIsInstance().distinct()
fun allBlocks(): Sequence =
- children.asSequence().flatMap { it.children }.filterIsInstance()
+ children.asSequence().filterIsInstance()
fun entrypoint(): PtSub? =
allBlocks().firstOrNull { it.name == "main" }?.children?.firstOrNull { it is PtSub && it.name == "start" } as PtSub?
}
-class PtModule(
- name: String,
- val loadAddress: UInt?,
- val library: Boolean,
- position: Position
-) : PtNamedNode(name, position) {
- override fun printProperties() {
- print("$name addr=$loadAddress library=$library")
- }
-}
-
-
class PtBlock(name: String,
val address: UInt?,
val library: Boolean,
@@ -97,51 +97,6 @@ class PtBlock(name: String,
}
-class PtDirective(var name: String, position: Position) : PtNode(position) {
- val args: List
- get() = children.map { it as PtDirectiveArg }
-
- override fun printProperties() {
- print(name)
- }
-
- override fun hashCode(): Int {
- return Objects.hash(name, args)
- }
-
- override fun equals(other: Any?): Boolean {
- if(other !is PtDirective)
- return false
- if(other===this)
- return true
- return(name==other.name && args.zip(other.args).all { it.first==it.second })
- }
-}
-
-
-class PtDirectiveArg(val str: String?,
- val name: String?,
- val int: UInt?,
- position: Position
-): PtNode(position) {
- override fun printProperties() {
- print("str=$str name=$name int=$int")
- }
-
- override fun hashCode(): Int {
- return Objects.hash(str, name, int)
- }
-
- override fun equals(other: Any?): Boolean {
- if(other !is PtDirectiveArg)
- return false
- if(other===this)
- return true
- return str==other.str || name==other.name || int==other.int
- }
-}
-
-
class PtInlineAssembly(val assembly: String, position: Position) : PtNode(position) {
override fun printProperties() {}
}
@@ -153,3 +108,19 @@ class PtLabel(name: String, position: Position) : PtNamedNode(name, position) {
}
}
+
+class PtBreakpoint(position: Position): PtNode(position) {
+ override fun printProperties() {}
+}
+
+
+class PtInlineBinary(val file: Path, val offset: UInt?, val length: UInt?, position: Position) : PtNode(position) {
+ override fun printProperties() {
+ print("filename=$file offset=$offset length=$length")
+ }
+}
+
+
+class PtNop(position: Position): PtNode(position) {
+ override fun printProperties() {}
+}
\ No newline at end of file
diff --git a/codeAst/src/prog8/code/ast/AstStatements.kt b/codeAst/src/prog8/code/ast/AstStatements.kt
index 3058285a0..a92af9ea5 100644
--- a/codeAst/src/prog8/code/ast/AstStatements.kt
+++ b/codeAst/src/prog8/code/ast/AstStatements.kt
@@ -1,7 +1,6 @@
package prog8.code.ast
import prog8.code.core.*
-import javax.xml.crypto.Data
class PtAsmSub(
@@ -157,7 +156,7 @@ class PtReturn(position: Position) : PtNode(position) {
}
-class PtVariable(name: String, val type: DataType, position: Position) : PtNamedNode(name, position) {
+class PtVariable(name: String, val type: DataType, var value: PtExpression?, position: Position) : PtNamedNode(name, position) {
override fun printProperties() {
print("$type $name")
}
diff --git a/codeGenExperimental/src/prog8/codegen/experimental/AsmGen.kt b/codeGenExperimental/src/prog8/codegen/experimental/AsmGen.kt
index dfa5f0989..e8c7ebd02 100644
--- a/codeGenExperimental/src/prog8/codegen/experimental/AsmGen.kt
+++ b/codeGenExperimental/src/prog8/codegen/experimental/AsmGen.kt
@@ -5,6 +5,7 @@ import prog8.code.ast.*
import prog8.code.core.*
import javax.xml.stream.XMLOutputFactory
import kotlin.io.path.Path
+import kotlin.io.path.absolutePathString
import kotlin.io.path.div
/*
@@ -36,7 +37,7 @@ class AsmGen(internal val program: PtProgram,
xml.elt("program")
xml.attr("name", program.name)
xml.startChildren()
- program.allModuleDirectives().forEach { writeNode(it) }
+ write(program.options)
program.children.forEach { writeNode(it) }
xml.endElt()
xml.endDoc()
@@ -45,11 +46,32 @@ class AsmGen(internal val program: PtProgram,
return AssemblyProgram("dummy")
}
+ private fun write(options: ProgramOptions) {
+ xml.elt("options")
+ xml.attr("output", options.output.name)
+ xml.attr("launcher", options.launcher.name)
+ xml.attr("zeropage", options.zeropage.name)
+ if(options.loadAddress!=null)
+ xml.attr("loadaddress", options.loadAddress.toString())
+ xml.attr("floatsenabled", options.floatsEnabled.toString())
+ xml.attr("nosysinit", options.noSysInit.toString())
+ xml.attr("dontreinitglobals", options.dontReinitGlobals.toString())
+ xml.attr("optimize", options.optimize.toString())
+ if(options.zpReserved.isNotEmpty()) {
+ xml.startChildren()
+ options.zpReserved.forEach {
+ xml.elt("zpreserved")
+ xml.attr("from", it.first.toString())
+ xml.attr("to", it.last.toString())
+ xml.endElt()
+ }
+ }
+ xml.endElt()
+ }
+
private fun writeNode(it: PtNode) {
when(it) {
- is PtModule -> write(it)
is PtBlock -> write(it)
- is PtDirective -> write(it)
is PtSub -> write(it)
is PtVariable -> write(it)
is PtAssignment -> write(it)
@@ -68,6 +90,7 @@ class AsmGen(internal val program: PtProgram,
is PtIdentifier -> write(it)
is PtIfElse -> write(it)
is PtInlineAssembly -> write(it)
+ is PtInlineBinary -> write(it)
is PtJump -> write(it)
is PtMemoryByte -> write(it)
is PtMemMapped -> write(it)
@@ -83,11 +106,20 @@ class AsmGen(internal val program: PtProgram,
is PtWhen -> write(it)
is PtWhenChoice -> write(it)
is PtLabel -> write(it)
+ is PtNop -> {}
+ is PtBreakpoint -> write(it)
is PtNodeGroup -> it.children.forEach { writeNode(it) }
else -> TODO("$it")
}
}
+ private fun write(breakPt: PtBreakpoint) {
+ xml.elt("breakpoint")
+ xml.startChildren()
+ xml.pos(breakPt.position)
+ xml.endElt()
+ }
+
private fun write(pipe: PtPipe) {
xml.elt("pipe")
xml.attr("type", pipe.type.name)
@@ -116,13 +148,8 @@ class AsmGen(internal val program: PtProgram,
xml.endElt()
}
- private fun write(string: PtString) {
- xml.elt("string")
- xml.attr("encoding", string.encoding.name)
- xml.startChildren()
- xml.text(string.value)
- xml.endElt()
- }
+ private fun write(string: PtString) =
+ xml.writeTextNode("string", listOf(Pair("encoding", string.encoding.name)), string.value)
private fun write(rept: PtRepeatLoop) {
xml.elt("repeat")
@@ -255,9 +282,19 @@ class AsmGen(internal val program: PtProgram,
xml.elt("assembly")
xml.startChildren()
xml.pos(inlineAsm.position)
- xml.elt("code")
- xml.text(inlineAsm.assembly)
+ xml.writeTextNode("code", emptyList(), inlineAsm.assembly)
xml.endElt()
+ }
+
+ private fun write(inlineBinary: PtInlineBinary) {
+ xml.elt("binary")
+ xml.attr("filename", inlineBinary.file.absolutePathString())
+ if(inlineBinary.offset!=null)
+ xml.attr("offset", inlineBinary.offset!!.toString())
+ if(inlineBinary.length!=null)
+ xml.attr("length", inlineBinary.length!!.toString())
+ xml.startChildren()
+ xml.pos(inlineBinary.position)
xml.endElt()
}
@@ -391,18 +428,6 @@ class AsmGen(internal val program: PtProgram,
xml.endElt()
}
- private fun write(module: PtModule) {
- xml.elt("module")
- xml.attr("name", module.name)
- xml.attr("library", module.library.toString())
- if(module.loadAddress!=null)
- xml.attr("loadaddress", module.loadAddress.toString())
- xml.startChildren()
- xml.pos(module.position)
- module.children.asSequence().filter { it !is PtDirective }.forEach { write(it as PtBlock) }
- xml.endElt()
- }
-
private fun write(block: PtBlock) {
xml.elt("block")
xml.attr("name", block.name)
@@ -559,26 +584,9 @@ class AsmGen(internal val program: PtProgram,
xml.elt("var")
xml.attr("name", variable.name)
xml.attr("type", variable.type.name)
- xml.endElt()
- }
-
- private fun write(directive: PtDirective) {
- if(directive.name=="%import")
- return
- xml.elt("directive")
- xml.attr("name", directive.name.substring(1))
- if(directive.args.isNotEmpty()) {
+ if(variable.value!=null) {
xml.startChildren()
- directive.args.forEach {
- xml.elt("arg")
- if(it.name!=null)
- xml.attr("name", it.name!!)
- if(it.str!=null)
- xml.attr("string", it.str!!)
- if(it.int!=null)
- xml.attr("number", it.int!!.toString())
- xml.endElt()
- }
+ writeNode(variable.value!!)
}
xml.endElt()
}
diff --git a/codeGenExperimental/src/prog8/codegen/experimental/IndentingXmlWriter.kt b/codeGenExperimental/src/prog8/codegen/experimental/IndentingXmlWriter.kt
index 9125939b2..2406d7fd2 100644
--- a/codeGenExperimental/src/prog8/codegen/experimental/IndentingXmlWriter.kt
+++ b/codeGenExperimental/src/prog8/codegen/experimental/IndentingXmlWriter.kt
@@ -13,13 +13,12 @@ class IndentingXmlWriter(val xml: XMLStreamWriter): XMLStreamWriter by xml {
fun elt(name: String) = writeStartElement(name)
fun attr(name: String, value: String) = writeAttribute(name, value)
fun attrs(attributes: List>) = attributes.forEach { writeAttribute(it.first, it.second) }
- fun text(text: String) = writeCData(text)
fun startChildren() {
xml.writeCharacters("\n")
content.pop()
content.push(true)
}
- fun endElt() = this.writeEndElement()
+ fun endElt(writeIndent: Boolean=true) = writeEndElement(writeIndent)
fun pos(pos: Position) {
elt("src")
attr("pos", pos.toString())
@@ -63,18 +62,29 @@ class IndentingXmlWriter(val xml: XMLStreamWriter): XMLStreamWriter by xml {
content.push(false)
}
- override fun writeEndElement() {
+ fun writeEndElement(writeIndents: Boolean) {
indent--
- if(content.pop())
+ if(content.pop() && writeIndents)
xml.writeCharacters(" ".repeat(indent))
xml.writeEndElement()
xml.writeCharacters("\n")
}
+ override fun writeEndElement() = writeEndElement(true)
+
override fun writeStartElement(name: String, ns: String, p2: String) {
xml.writeCharacters(" ".repeat(indent))
xml.writeStartElement(name, ns, p2)
indent++
content.push(false)
}
+
+ fun writeTextNode(name: String, attrs: List>, text: String) {
+ xml.writeCharacters(" ".repeat(indent))
+ xml.writeStartElement(name)
+ attrs.forEach { (name, value) -> xml.writeAttribute(name, value) }
+ xml.writeCData(text)
+ xml.writeEndElement()
+ xml.writeCharacters("\n")
+ }
}
diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt
index 4ce778e4f..e8831ef7a 100644
--- a/compiler/src/prog8/compiler/Compiler.kt
+++ b/compiler/src/prog8/compiler/Compiler.kt
@@ -402,7 +402,7 @@ internal fun asmGeneratorFor(program: Program,
if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02)) {
// TODO for now, only use the new Intermediary Ast for this experimental codegen:
- val intermediateAst = IntermediateAstMaker(program).transform()
+ val intermediateAst = IntermediateAstMaker(program, options).transform()
return prog8.codegen.experimental.AsmGen(intermediateAst, errors, symbolTable, options)
}
} else {
diff --git a/compiler/src/prog8/compiler/IntermediateAstMaker.kt b/compiler/src/prog8/compiler/IntermediateAstMaker.kt
index 871d74e11..4e5567eb8 100644
--- a/compiler/src/prog8/compiler/IntermediateAstMaker.kt
+++ b/compiler/src/prog8/compiler/IntermediateAstMaker.kt
@@ -1,43 +1,43 @@
package prog8.compiler
-import prog8.ast.Module
import prog8.ast.Program
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.code.ast.*
+import prog8.code.core.CompilationOptions
import prog8.code.core.DataType
+import kotlin.io.path.Path
-class IntermediateAstMaker(val program: Program) {
+class IntermediateAstMaker(val program: Program, val comp: CompilationOptions) {
fun transform(): PtProgram {
+ val options = ProgramOptions(
+ comp.output,
+ comp.launcher,
+ comp.zeropage,
+ comp.zpReserved,
+ program.definedLoadAddress,
+ comp.floats,
+ comp.noSysInit,
+ comp.dontReinitGlobals,
+ comp.optimize
+ )
+
val ptProgram = PtProgram(
program.name,
+ options,
program.memsizer,
program.encoding
)
- for (module in program.modules) {
- ptProgram.add(transform(module))
- }
+ // note: modules are not represented any longer in this Ast. All blocks have been moved into the top scope.
+ for (block in program.allBlocks)
+ ptProgram.add(transform(block))
return ptProgram
}
- private fun transform(srcModule: Module): PtModule {
- val module = PtModule(
- srcModule.name,
- srcModule.loadAddress,
- srcModule.isLibrary,
- srcModule.position
- )
-
- for (statement in srcModule.statements)
- module.add(transformStatement(statement))
-
- return module
- }
-
private fun transformStatement(statement: Statement): PtNode {
return when (statement) {
is AnonymousScope -> throw FatalAstException("AnonymousScopes should have been flattened")
@@ -158,17 +158,20 @@ class IntermediateAstMaker(val program: Program) {
return branch
}
- private fun transform(directive: Directive): PtDirective {
- val dir = PtDirective(directive.directive, directive.position)
- for (arg in directive.args) {
- dir.add(transform(arg))
+ private fun transform(directive: Directive): PtNode {
+ return when(directive.directive) {
+ "%breakpoint" -> PtBreakpoint(directive.position)
+ "%asmbinary" -> {
+ val offset: UInt? = if(directive.args.size>=2) directive.args[1].int!! else null
+ val length: UInt? = if(directive.args.size>=3) directive.args[2].int!! else null
+ val sourcePath = Path(directive.definingModule.source.origin)
+ val includedPath = sourcePath.resolveSibling(directive.args[0].str!!)
+ PtInlineBinary(includedPath, offset, length, directive.position)
+ }
+ else -> PtNop(directive.position)
}
- return dir
}
- private fun transform(arg: DirectiveArg): PtDirectiveArg =
- PtDirectiveArg(arg.str, arg.name, arg.int, arg.position)
-
private fun transform(srcFor: ForLoop): PtForLoop {
val forloop = PtForLoop(srcFor.position)
forloop.add(transform(srcFor.loopVar))
@@ -307,7 +310,10 @@ class IntermediateAstMaker(val program: Program) {
private fun transform(srcVar: VarDecl): PtNode {
return when(srcVar.type) {
- VarDeclType.VAR -> PtVariable(srcVar.name, srcVar.datatype, srcVar.position)
+ VarDeclType.VAR -> {
+ val value = if(srcVar.value!=null) transformExpression(srcVar.value!!) else null
+ PtVariable(srcVar.name, srcVar.datatype, value, srcVar.position)
+ }
VarDeclType.CONST -> PtConstant(srcVar.name, srcVar.datatype, (srcVar.value as NumericLiteral).number, srcVar.position)
VarDeclType.MEMORY -> PtMemMapped(srcVar.name, srcVar.datatype, (srcVar.value as NumericLiteral).number.toUInt(), srcVar.position)
}
diff --git a/compiler/test/ast/TestIntermediateAst.kt b/compiler/test/ast/TestIntermediateAst.kt
index b3ae24ddd..5824763b0 100644
--- a/compiler/test/ast/TestIntermediateAst.kt
+++ b/compiler/test/ast/TestIntermediateAst.kt
@@ -4,7 +4,12 @@ import io.kotest.assertions.fail
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.ints.shouldBeGreaterThan
import io.kotest.matchers.shouldBe
+import io.kotest.matchers.shouldNotBe
import prog8.code.ast.PtVariable
+import prog8.code.core.CbmPrgLauncherType
+import prog8.code.core.CompilationOptions
+import prog8.code.core.OutputType
+import prog8.code.core.ZeropageType
import prog8.code.target.C64Target
import prog8.compiler.IntermediateAstMaker
import prog8tests.helpers.compileText
@@ -25,8 +30,18 @@ class TestIntermediateAst: FunSpec({
}
"""
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
- val ast = IntermediateAstMaker(result.program).transform()
+ val options = CompilationOptions(
+ OutputType.PRG,
+ CbmPrgLauncherType.BASIC,
+ ZeropageType.BASICSAFE,
+ emptyList(),
+ floats = true,
+ noSysInit = false,
+ compTarget = C64Target()
+ )
+ val ast = IntermediateAstMaker(result.program, options).transform()
ast.name shouldBe result.program.name
+ ast.allBlocks().any() shouldBe true
val entry = ast.entrypoint() ?: fail("no main.start() found")
entry.name shouldBe "start"
entry.scopedName shouldBe listOf("main", "start")
diff --git a/examples/test.p8 b/examples/test.p8
index 6a515e4e0..4fdf46c8d 100644
--- a/examples/test.p8
+++ b/examples/test.p8
@@ -1,10 +1,15 @@
%import textio
%import test_stack
%zeropage basicsafe
+%zpreserved 50,80
+%zpreserved 150,155
+%address $4000
; Note: this program is compatible with C64 and CX16.
+%option align_word
main {
+ %option align_word
ubyte[256] sieve
ubyte candidate_prime = 2 ; is increased in the loop
@@ -12,6 +17,10 @@ main {
sub start() {
sys.memset(sieve, 256, false) ; clear the sieve, to reset starting situation on subsequent runs
+ %breakpoint
+ %asmbinary "LICENSE", 10 ,1
+
+
; calculate primes
txt.print("prime numbers up to 255:\n\n")
ubyte amount=0
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index e708b1c02..41d9927a4 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 0f80bbf51..41dfb8790 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index 4f906e0c8..1b6c78733 100755
--- a/gradlew
+++ b/gradlew
@@ -1,7 +1,7 @@
-#!/usr/bin/env sh
+#!/bin/sh
#
-# Copyright 2015 the original author or authors.
+# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,67 +17,101 @@
#
##############################################################################
-##
-## Gradle start up script for UN*X
-##
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
##############################################################################
# Attempt to set APP_HOME
+
# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
+APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
+MAX_FD=maximum
warn () {
echo "$*"
-}
+} >&2
die () {
echo
echo "$*"
echo
exit 1
-}
+} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
- NONSTOP* )
- nonstop=true
- ;;
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
+ JAVACMD=$JAVA_HOME/jre/sh/java
else
- JAVACMD="$JAVA_HOME/bin/java"
+ JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
- JAVACMD="java"
+ JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@@ -106,80 +140,95 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
-fi
-
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
-
-# For Cygwin or MSYS, switch paths to Windows format before running java
-if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
- JAVACMD=`cygpath --unix "$JAVACMD"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
- # Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
- fi
- i=`expr $i + 1`
- done
- case $i in
- 0) set -- ;;
- 1) set -- "$args0" ;;
- 2) set -- "$args0" "$args1" ;;
- 3) set -- "$args0" "$args1" "$args2" ;;
- 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
-# Escape application args
-save () {
- for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
- echo " "
-}
-APP_ARGS=`save "$@"`
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
exec "$JAVACMD" "$@"