mirror of
https://github.com/irmen/prog8.git
synced 2024-11-29 17:50:35 +00:00
added output directory command line option
improved cli parser by using kotlinx.cli
This commit is contained in:
parent
2d768c3f28
commit
aa94300bdd
9
.idea/libraries/kotlinx_cli_jvm_0_1_0_dev_5.xml
generated
Normal file
9
.idea/libraries/kotlinx_cli_jvm_0_1_0_dev_5.xml
generated
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<component name="libraryTable">
|
||||||
|
<library name="kotlinx-cli-jvm-0.1.0-dev-5">
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$PROJECT_DIR$/compiler/lib/kotlinx-cli-jvm-0.1.0-dev-5.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</component>
|
@ -20,6 +20,7 @@ repositories {
|
|||||||
mavenLocal()
|
mavenLocal()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
jcenter()
|
jcenter()
|
||||||
|
maven { url "https://dl.bintray.com/orangy/maven/" }
|
||||||
}
|
}
|
||||||
|
|
||||||
def prog8version = rootProject.file('compiler/res/version.txt').text.trim()
|
def prog8version = rootProject.file('compiler/res/version.txt').text.trim()
|
||||||
@ -35,8 +36,8 @@ dependencies {
|
|||||||
testImplementation "org.jetbrains.kotlin:kotlin-test-junit5:$kotlinVersion"
|
testImplementation "org.jetbrains.kotlin:kotlin-test-junit5:$kotlinVersion"
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.2'
|
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.2'
|
||||||
testImplementation 'org.hamcrest:hamcrest-junit:2.0.0.0'
|
testImplementation 'org.hamcrest:hamcrest-junit:2.0.0.0'
|
||||||
|
|
||||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.2'
|
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.2'
|
||||||
|
compile 'org.jetbrains.kotlinx:kotlinx-cli-jvm:0.1.0-dev-5'
|
||||||
}
|
}
|
||||||
|
|
||||||
compileKotlin {
|
compileKotlin {
|
||||||
|
@ -14,5 +14,6 @@
|
|||||||
<orderEntry type="library" name="antlr-runtime-4.7.2" level="project" />
|
<orderEntry type="library" name="antlr-runtime-4.7.2" level="project" />
|
||||||
<orderEntry type="module" module-name="parser" />
|
<orderEntry type="module" module-name="parser" />
|
||||||
<orderEntry type="library" name="unittest-libs" level="project" />
|
<orderEntry type="library" name="unittest-libs" level="project" />
|
||||||
|
<orderEntry type="library" name="kotlinx-cli-jvm-0.1.0-dev-5" level="project" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
BIN
compiler/lib/kotlinx-cli-jvm-0.1.0-dev-5.jar
Normal file
BIN
compiler/lib/kotlinx-cli-jvm-0.1.0-dev-5.jar
Normal file
Binary file not shown.
@ -1,5 +1,6 @@
|
|||||||
package prog8
|
package prog8
|
||||||
|
|
||||||
|
import kotlinx.cli.*
|
||||||
import prog8.ast.base.AstException
|
import prog8.ast.base.AstException
|
||||||
import prog8.compiler.CompilationResult
|
import prog8.compiler.CompilationResult
|
||||||
import prog8.compiler.compileProgram
|
import prog8.compiler.compileProgram
|
||||||
@ -14,11 +15,8 @@ import kotlin.system.exitProcess
|
|||||||
|
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
|
|
||||||
printSoftwareHeader("compiler")
|
printSoftwareHeader("compiler")
|
||||||
|
|
||||||
if (args.isEmpty())
|
|
||||||
usage()
|
|
||||||
compileMain(args)
|
compileMain(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,46 +28,37 @@ internal fun printSoftwareHeader(what: String) {
|
|||||||
|
|
||||||
|
|
||||||
private fun compileMain(args: Array<String>) {
|
private fun compileMain(args: Array<String>) {
|
||||||
var emulatorToStart = ""
|
val cli = CommandLineInterface("prog8compiler")
|
||||||
var moduleFile = ""
|
val startEmulator1 by cli.flagArgument("-emu", "auto-start the 'x64' C-64 emulator after successful compilation")
|
||||||
var writeAssembly = true
|
val startEmulator2 by cli.flagArgument("-emu2", "auto-start the 'x64sc' C-64 emulator after successful compilation")
|
||||||
var optimize = true
|
val outputDir by cli.flagValueArgument("-out", "directory", "directory for output files instead of current directory", ".")
|
||||||
var launchAstVm = false
|
val dontWriteAssembly by cli.flagArgument("-noasm", "don't create assembly code")
|
||||||
var watchMode = false
|
val dontOptimize by cli.flagArgument("-noopt", "don't perform any optimizations")
|
||||||
for (arg in args) {
|
val launchSimulator by cli.flagArgument("-sim", "launch the prog8 virtual machine/simulator after compilation")
|
||||||
if(arg=="-emu")
|
val watchMode by cli.flagArgument("-watch", "continuous compilation mode (watches for file changes)")
|
||||||
emulatorToStart = "x64"
|
val moduleFiles by cli.positionalArgumentsList("modules", "main module file(s) to compile", minArgs = 1)
|
||||||
else if(arg=="-emu2")
|
|
||||||
emulatorToStart = "x64sc"
|
try {
|
||||||
else if(arg=="-noasm")
|
cli.parse(args)
|
||||||
writeAssembly = false
|
} catch (e: Exception) {
|
||||||
else if(arg=="-noopt")
|
exitProcess(1)
|
||||||
optimize = false
|
|
||||||
else if(arg=="-sim") {
|
|
||||||
launchAstVm = true
|
|
||||||
writeAssembly = false
|
|
||||||
emulatorToStart = ""
|
|
||||||
}
|
|
||||||
else if(arg=="-watch")
|
|
||||||
watchMode = true
|
|
||||||
else if(!arg.startsWith("-"))
|
|
||||||
moduleFile = arg
|
|
||||||
else
|
|
||||||
usage()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(watchMode) {
|
val outputPath = Paths.get(outputDir)
|
||||||
if(moduleFile.isBlank())
|
if(!outputPath.toFile().isDirectory) {
|
||||||
usage()
|
System.err.println("Output path doesn't exist")
|
||||||
|
exitProcess(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(watchMode && moduleFiles.size<=1) {
|
||||||
val watchservice = FileSystems.getDefault().newWatchService()
|
val watchservice = FileSystems.getDefault().newWatchService()
|
||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
val filepath = Paths.get(moduleFile).normalize()
|
val filepath = Paths.get(moduleFiles.single()).normalize()
|
||||||
println("Continuous watch mode active. Main module: $filepath")
|
println("Continuous watch mode active. Main module: $filepath")
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val compilationResult = compileProgram(filepath, optimize, writeAssembly)
|
val compilationResult = compileProgram(filepath, !dontOptimize, !dontWriteAssembly, outputDir=outputPath)
|
||||||
println("Imported files (now watching:)")
|
println("Imported files (now watching:)")
|
||||||
for (importedFile in compilationResult.importedFiles) {
|
for (importedFile in compilationResult.importedFiles) {
|
||||||
print(" ")
|
print(" ")
|
||||||
@ -90,14 +79,11 @@ private fun compileMain(args: Array<String>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if(moduleFile.isBlank())
|
for(filepathRaw in moduleFiles) {
|
||||||
usage()
|
val filepath = Paths.get(filepathRaw).normalize()
|
||||||
|
|
||||||
val filepath = Paths.get(moduleFile).normalize()
|
|
||||||
val compilationResult: CompilationResult
|
val compilationResult: CompilationResult
|
||||||
|
|
||||||
try {
|
try {
|
||||||
compilationResult = compileProgram(filepath, optimize, writeAssembly)
|
compilationResult = compileProgram(filepath, !dontOptimize, !dontWriteAssembly, outputDir=outputPath)
|
||||||
if(!compilationResult.success)
|
if(!compilationResult.success)
|
||||||
exitProcess(1)
|
exitProcess(1)
|
||||||
} catch (x: ParsingFailedError) {
|
} catch (x: ParsingFailedError) {
|
||||||
@ -106,35 +92,24 @@ private fun compileMain(args: Array<String>) {
|
|||||||
exitProcess(1)
|
exitProcess(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (launchAstVm) {
|
if (launchSimulator) {
|
||||||
println("\nLaunching AST-based vm...")
|
println("\nLaunching AST-based simulator...")
|
||||||
val vm = AstVm(compilationResult.programAst)
|
val vm = AstVm(compilationResult.programAst)
|
||||||
vm.run()
|
vm.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (emulatorToStart.isNotEmpty()) {
|
if (startEmulator1 || startEmulator2) {
|
||||||
if (compilationResult.programName.isEmpty())
|
if (compilationResult.programName.isEmpty())
|
||||||
println("\nCan't start emulator because no program was assembled.")
|
println("\nCan't start emulator because no program was assembled.")
|
||||||
else {
|
else {
|
||||||
println("\nStarting C-64 emulator $emulatorToStart...")
|
val emulator = if(startEmulator1) "x64" else "x64sc"
|
||||||
val cmdline = listOf(emulatorToStart, "-silent", "-moncommands", "${compilationResult.programName}.vice-mon-list",
|
println("\nStarting C-64 emulator $emulator...")
|
||||||
|
val cmdline = listOf(emulator, "-silent", "-moncommands", "${compilationResult.programName}.vice-mon-list",
|
||||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", compilationResult.programName + ".prg")
|
"-autostartprgmode", "1", "-autostart-warp", "-autostart", compilationResult.programName + ".prg")
|
||||||
val process = ProcessBuilder(cmdline).inheritIO().start()
|
val process = ProcessBuilder(cmdline).inheritIO().start()
|
||||||
process.waitFor()
|
process.waitFor()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun usage() {
|
|
||||||
System.err.println("Missing argument(s):")
|
|
||||||
System.err.println(" [-noasm] don't create assembly code")
|
|
||||||
System.err.println(" [-noopt] don't perform any optimizations")
|
|
||||||
System.err.println(" [-emu] auto-start the 'x64' C-64 emulator after successful compilation")
|
|
||||||
System.err.println(" [-emu2] auto-start the 'x64sc' C-64 emulator after successful compilation")
|
|
||||||
System.err.println(" [-sim] launch the prog8 virtual machine/simulator after compilation")
|
|
||||||
System.err.println(" [-watch] continuous compilation mode (watches for file changes)")
|
|
||||||
System.err.println(" modulefile main module file to compile")
|
|
||||||
exitProcess(1)
|
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import prog8.parser.ParsingFailedError
|
|||||||
|
|
||||||
fun printErrors(errors: List<Any>, moduleName: String) {
|
fun printErrors(errors: List<Any>, moduleName: String) {
|
||||||
val reportedMessages = mutableSetOf<String>()
|
val reportedMessages = mutableSetOf<String>()
|
||||||
print("\u001b[91m") // bright red
|
System.err.print("\u001b[91m") // bright red
|
||||||
errors.forEach {
|
errors.forEach {
|
||||||
val msg = it.toString()
|
val msg = it.toString()
|
||||||
if(msg !in reportedMessages) {
|
if(msg !in reportedMessages) {
|
||||||
@ -13,7 +13,7 @@ fun printErrors(errors: List<Any>, moduleName: String) {
|
|||||||
reportedMessages.add(msg)
|
reportedMessages.add(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
print("\u001b[0m") // reset color
|
System.err.print("\u001b[0m") // reset color
|
||||||
if(reportedMessages.isNotEmpty())
|
if(reportedMessages.isNotEmpty())
|
||||||
throw ParsingFailedError("There are ${reportedMessages.size} errors in module '$moduleName'.")
|
throw ParsingFailedError("There are ${reportedMessages.size} errors in module '$moduleName'.")
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,8 @@ class CompilationResult(val success: Boolean,
|
|||||||
|
|
||||||
fun compileProgram(filepath: Path,
|
fun compileProgram(filepath: Path,
|
||||||
optimize: Boolean,
|
optimize: Boolean,
|
||||||
writeAssembly: Boolean): CompilationResult {
|
writeAssembly: Boolean,
|
||||||
|
outputDir: Path): CompilationResult {
|
||||||
lateinit var programAst: Program
|
lateinit var programAst: Program
|
||||||
var programName: String? = null
|
var programName: String? = null
|
||||||
|
|
||||||
@ -102,7 +103,7 @@ fun compileProgram(filepath: Path,
|
|||||||
// asm generation directly from the Ast, no need for intermediate code
|
// asm generation directly from the Ast, no need for intermediate code
|
||||||
val zeropage = MachineDefinition.C64Zeropage(compilerOptions)
|
val zeropage = MachineDefinition.C64Zeropage(compilerOptions)
|
||||||
programAst.anonscopeVarsCleanup()
|
programAst.anonscopeVarsCleanup()
|
||||||
val assembly = AsmGen(programAst, compilerOptions, zeropage).compileToAssembly(optimize)
|
val assembly = AsmGen(programAst, compilerOptions, zeropage, outputDir).compileToAssembly(optimize)
|
||||||
assembly.assemble(compilerOptions)
|
assembly.assemble(compilerOptions)
|
||||||
programName = assembly.name
|
programName = assembly.name
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,14 @@ package prog8.compiler.target.c64
|
|||||||
|
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.compiler.CompilationOptions
|
||||||
import prog8.compiler.OutputType
|
import prog8.compiler.OutputType
|
||||||
import java.io.File
|
import java.nio.file.Path
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
class AssemblyProgram(val name: String) {
|
class AssemblyProgram(val name: String, val outputDir: Path) {
|
||||||
private val assemblyFile = "$name.asm"
|
private val assemblyFile = outputDir.resolve("$name.asm")
|
||||||
private val viceMonListFile = "$name.vice-mon-list"
|
private val prgFile = outputDir.resolve("$name.prg")
|
||||||
|
private val binFile = outputDir.resolve("$name.bin")
|
||||||
|
private val viceMonListFile = outputDir.resolve("$name.vice-mon-list")
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// 6502 opcodes (including aliases and illegal opcodes), these cannot be used as variable or label names
|
// 6502 opcodes (including aliases and illegal opcodes), these cannot be used as variable or label names
|
||||||
@ -27,21 +29,22 @@ class AssemblyProgram(val name: String) {
|
|||||||
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps
|
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps
|
||||||
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
||||||
"-Wall", "-Wno-strict-bool", "-Wno-shadow", "-Werror", "-Wno-error=long-branch",
|
"-Wall", "-Wno-strict-bool", "-Wno-shadow", "-Werror", "-Wno-error=long-branch",
|
||||||
"--dump-labels", "--vice-labels", "-l", viceMonListFile, "--no-monitor")
|
"--dump-labels", "--vice-labels", "-l", viceMonListFile.toString(), "--no-monitor")
|
||||||
|
|
||||||
val outFile = when(options.output) {
|
val outFile = when(options.output) {
|
||||||
OutputType.PRG -> {
|
OutputType.PRG -> {
|
||||||
command.add("--cbm-prg")
|
command.add("--cbm-prg")
|
||||||
println("\nCreating C-64 prg.")
|
println("\nCreating C-64 prg.")
|
||||||
"$name.prg"
|
prgFile
|
||||||
}
|
}
|
||||||
OutputType.RAW -> {
|
OutputType.RAW -> {
|
||||||
command.add("--nostart")
|
command.add("--nostart")
|
||||||
println("\nCreating raw binary.")
|
println("\nCreating raw binary.")
|
||||||
"$name.bin"
|
binFile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
command.addAll(listOf("--output", outFile, assemblyFile))
|
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
|
||||||
|
println("ASM COMMAND: $command") // TODO
|
||||||
|
|
||||||
val proc = ProcessBuilder(command).inheritIO().start()
|
val proc = ProcessBuilder(command).inheritIO().start()
|
||||||
val result = proc.waitFor()
|
val result = proc.waitFor()
|
||||||
@ -57,7 +60,7 @@ class AssemblyProgram(val name: String) {
|
|||||||
// builds list of breakpoints, appends to monitor list file
|
// builds list of breakpoints, appends to monitor list file
|
||||||
val breakpoints = mutableListOf<String>()
|
val breakpoints = mutableListOf<String>()
|
||||||
val pattern = Regex("""al (\w+) \S+_prog8_breakpoint_\d+.?""") // gather breakpoints by the source label that"s generated for them
|
val pattern = Regex("""al (\w+) \S+_prog8_breakpoint_\d+.?""") // gather breakpoints by the source label that"s generated for them
|
||||||
for(line in File(viceMonListFile).readLines()) {
|
for(line in viceMonListFile.toFile().readLines()) {
|
||||||
val match = pattern.matchEntire(line)
|
val match = pattern.matchEntire(line)
|
||||||
if(match!=null)
|
if(match!=null)
|
||||||
breakpoints.add("break \$" + match.groupValues[1])
|
breakpoints.add("break \$" + match.groupValues[1])
|
||||||
@ -66,6 +69,6 @@ class AssemblyProgram(val name: String) {
|
|||||||
breakpoints.add(0, "; vice monitor breakpoint list now follows")
|
breakpoints.add(0, "; vice monitor breakpoint list now follows")
|
||||||
breakpoints.add(1, "; $num breakpoints have been defined")
|
breakpoints.add(1, "; $num breakpoints have been defined")
|
||||||
breakpoints.add(2, "del")
|
breakpoints.add(2, "del")
|
||||||
File(viceMonListFile).appendText(breakpoints.joinToString("\n")+"\n")
|
viceMonListFile.toFile().appendText(breakpoints.joinToString("\n")+"\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,8 @@ import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
|
|||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.c64.Petscii
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
import prog8.functions.FunctionSignature
|
import prog8.functions.FunctionSignature
|
||||||
import java.io.File
|
|
||||||
import java.math.RoundingMode
|
import java.math.RoundingMode
|
||||||
|
import java.nio.file.Path
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
@ -25,7 +25,8 @@ internal class AssemblyError(msg: String) : RuntimeException(msg)
|
|||||||
|
|
||||||
internal class AsmGen(val program: Program,
|
internal class AsmGen(val program: Program,
|
||||||
val options: CompilationOptions,
|
val options: CompilationOptions,
|
||||||
val zeropage: Zeropage) {
|
val zeropage: Zeropage,
|
||||||
|
val outputDir: Path) {
|
||||||
|
|
||||||
private val assemblyLines = mutableListOf<String>()
|
private val assemblyLines = mutableListOf<String>()
|
||||||
private val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
|
private val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
|
||||||
@ -62,11 +63,12 @@ internal class AsmGen(val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
File("${program.name}.asm").printWriter().use {
|
val outputFile = outputDir.resolve("${program.name}.asm").toFile()
|
||||||
|
outputFile.printWriter().use {
|
||||||
for (line in assemblyLines) { it.println(line) }
|
for (line in assemblyLines) { it.println(line) }
|
||||||
}
|
}
|
||||||
|
|
||||||
return AssemblyProgram(program.name)
|
return AssemblyProgram(program.name, outputDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun header() {
|
private fun header() {
|
||||||
|
@ -19,7 +19,6 @@ import prog8.vm.RuntimeValueNumeric
|
|||||||
import java.io.CharConversionException
|
import java.io.CharConversionException
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
class TestCompiler {
|
class TestCompiler {
|
||||||
@Test
|
@Test
|
||||||
@ -54,7 +53,6 @@ class TestCompiler {
|
|||||||
assertFailsWith<CompilerException> { 65536L.toHex() }
|
assertFailsWith<CompilerException> { 65536L.toHex() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testFloatToMflpt5() {
|
fun testFloatToMflpt5() {
|
||||||
assertThat(Mflpt5.fromNumber(0), equalTo(Mflpt5(0x00, 0x00, 0x00, 0x00, 0x00)))
|
assertThat(Mflpt5.fromNumber(0), equalTo(Mflpt5(0x00, 0x00, 0x00, 0x00, 0x00)))
|
||||||
|
@ -94,6 +94,13 @@ Start the compiler with the ``-watch`` argument to enable this.
|
|||||||
It will compile your program and then instead of exiting, it waits for any changes in the module source files.
|
It will compile your program and then instead of exiting, it waits for any changes in the module source files.
|
||||||
As soon as a change happens, the program gets compiled again.
|
As soon as a change happens, the program gets compiled again.
|
||||||
|
|
||||||
|
Other options
|
||||||
|
^^^^^^^^^^^^^
|
||||||
|
There's an option to specify the output directory if you're not happy with the default (the current working directory).
|
||||||
|
Also it is possible to specify more than one main module to compile:
|
||||||
|
this can be useful to quickly recompile multiple separate programs quickly.
|
||||||
|
(compiling in a batch like this is a lot faster than invoking the compiler again once per main file)
|
||||||
|
|
||||||
|
|
||||||
Module source code files
|
Module source code files
|
||||||
------------------------
|
------------------------
|
||||||
|
Loading…
Reference in New Issue
Block a user