diff --git a/.idea/libraries/kotlinx_cli_jvm_0_1_0_dev_5.xml b/.idea/libraries/kotlinx_cli_jvm_0_1_0_dev_5.xml
new file mode 100644
index 000000000..7fee0aedc
--- /dev/null
+++ b/.idea/libraries/kotlinx_cli_jvm_0_1_0_dev_5.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/compiler/build.gradle b/compiler/build.gradle
index 4c5cfd5e8..d68a4cb86 100644
--- a/compiler/build.gradle
+++ b/compiler/build.gradle
@@ -20,6 +20,7 @@ repositories {
mavenLocal()
mavenCentral()
jcenter()
+ maven { url "https://dl.bintray.com/orangy/maven/" }
}
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.junit.jupiter:junit-jupiter-api:5.3.2'
testImplementation 'org.hamcrest:hamcrest-junit:2.0.0.0'
-
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.2'
+ compile 'org.jetbrains.kotlinx:kotlinx-cli-jvm:0.1.0-dev-5'
}
compileKotlin {
diff --git a/compiler/compiler.iml b/compiler/compiler.iml
index 9df271cf3..10103c91c 100644
--- a/compiler/compiler.iml
+++ b/compiler/compiler.iml
@@ -14,5 +14,6 @@
+
\ No newline at end of file
diff --git a/compiler/lib/kotlinx-cli-jvm-0.1.0-dev-5.jar b/compiler/lib/kotlinx-cli-jvm-0.1.0-dev-5.jar
new file mode 100644
index 000000000..2edc1e111
Binary files /dev/null and b/compiler/lib/kotlinx-cli-jvm-0.1.0-dev-5.jar differ
diff --git a/compiler/src/prog8/CompilerMain.kt b/compiler/src/prog8/CompilerMain.kt
index 6a9beef3e..6e3a5535a 100644
--- a/compiler/src/prog8/CompilerMain.kt
+++ b/compiler/src/prog8/CompilerMain.kt
@@ -1,5 +1,6 @@
package prog8
+import kotlinx.cli.*
import prog8.ast.base.AstException
import prog8.compiler.CompilationResult
import prog8.compiler.compileProgram
@@ -14,11 +15,8 @@ import kotlin.system.exitProcess
fun main(args: Array) {
-
printSoftwareHeader("compiler")
- if (args.isEmpty())
- usage()
compileMain(args)
}
@@ -30,46 +28,37 @@ internal fun printSoftwareHeader(what: String) {
private fun compileMain(args: Array) {
- var emulatorToStart = ""
- var moduleFile = ""
- var writeAssembly = true
- var optimize = true
- var launchAstVm = false
- var watchMode = false
- for (arg in args) {
- if(arg=="-emu")
- emulatorToStart = "x64"
- else if(arg=="-emu2")
- emulatorToStart = "x64sc"
- else if(arg=="-noasm")
- writeAssembly = false
- else if(arg=="-noopt")
- 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()
+ val cli = CommandLineInterface("prog8compiler")
+ val startEmulator1 by cli.flagArgument("-emu", "auto-start the 'x64' C-64 emulator after successful compilation")
+ val startEmulator2 by cli.flagArgument("-emu2", "auto-start the 'x64sc' C-64 emulator after successful compilation")
+ val outputDir by cli.flagValueArgument("-out", "directory", "directory for output files instead of current directory", ".")
+ val dontWriteAssembly by cli.flagArgument("-noasm", "don't create assembly code")
+ val dontOptimize by cli.flagArgument("-noopt", "don't perform any optimizations")
+ val launchSimulator by cli.flagArgument("-sim", "launch the prog8 virtual machine/simulator after compilation")
+ val watchMode by cli.flagArgument("-watch", "continuous compilation mode (watches for file changes)")
+ val moduleFiles by cli.positionalArgumentsList("modules", "main module file(s) to compile", minArgs = 1)
+
+ try {
+ cli.parse(args)
+ } catch (e: Exception) {
+ exitProcess(1)
}
- if(watchMode) {
- if(moduleFile.isBlank())
- usage()
+ val outputPath = Paths.get(outputDir)
+ if(!outputPath.toFile().isDirectory) {
+ System.err.println("Output path doesn't exist")
+ exitProcess(1)
+ }
+ if(watchMode && moduleFiles.size<=1) {
val watchservice = FileSystems.getDefault().newWatchService()
while(true) {
- val filepath = Paths.get(moduleFile).normalize()
+ val filepath = Paths.get(moduleFiles.single()).normalize()
println("Continuous watch mode active. Main module: $filepath")
try {
- val compilationResult = compileProgram(filepath, optimize, writeAssembly)
+ val compilationResult = compileProgram(filepath, !dontOptimize, !dontWriteAssembly, outputDir=outputPath)
println("Imported files (now watching:)")
for (importedFile in compilationResult.importedFiles) {
print(" ")
@@ -90,51 +79,37 @@ private fun compileMain(args: Array) {
}
} else {
- if(moduleFile.isBlank())
- usage()
-
- val filepath = Paths.get(moduleFile).normalize()
- val compilationResult: CompilationResult
-
- try {
- compilationResult = compileProgram(filepath, optimize, writeAssembly)
- if(!compilationResult.success)
+ for(filepathRaw in moduleFiles) {
+ val filepath = Paths.get(filepathRaw).normalize()
+ val compilationResult: CompilationResult
+ try {
+ compilationResult = compileProgram(filepath, !dontOptimize, !dontWriteAssembly, outputDir=outputPath)
+ if(!compilationResult.success)
+ exitProcess(1)
+ } catch (x: ParsingFailedError) {
exitProcess(1)
- } catch (x: ParsingFailedError) {
- exitProcess(1)
- } catch (x: AstException) {
- exitProcess(1)
- }
+ } catch (x: AstException) {
+ exitProcess(1)
+ }
- if (launchAstVm) {
- println("\nLaunching AST-based vm...")
- val vm = AstVm(compilationResult.programAst)
- vm.run()
- }
+ if (launchSimulator) {
+ println("\nLaunching AST-based simulator...")
+ val vm = AstVm(compilationResult.programAst)
+ vm.run()
+ }
- if (emulatorToStart.isNotEmpty()) {
- if (compilationResult.programName.isEmpty())
- println("\nCan't start emulator because no program was assembled.")
- else {
- println("\nStarting C-64 emulator $emulatorToStart...")
- val cmdline = listOf(emulatorToStart, "-silent", "-moncommands", "${compilationResult.programName}.vice-mon-list",
- "-autostartprgmode", "1", "-autostart-warp", "-autostart", compilationResult.programName + ".prg")
- val process = ProcessBuilder(cmdline).inheritIO().start()
- process.waitFor()
+ if (startEmulator1 || startEmulator2) {
+ if (compilationResult.programName.isEmpty())
+ println("\nCan't start emulator because no program was assembled.")
+ else {
+ val emulator = if(startEmulator1) "x64" else "x64sc"
+ 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")
+ val process = ProcessBuilder(cmdline).inheritIO().start()
+ 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)
-}
diff --git a/compiler/src/prog8/ast/base/ErrorReporting.kt b/compiler/src/prog8/ast/base/ErrorReporting.kt
index 19b4365ec..df3191f03 100644
--- a/compiler/src/prog8/ast/base/ErrorReporting.kt
+++ b/compiler/src/prog8/ast/base/ErrorReporting.kt
@@ -5,7 +5,7 @@ import prog8.parser.ParsingFailedError
fun printErrors(errors: List, moduleName: String) {
val reportedMessages = mutableSetOf()
- print("\u001b[91m") // bright red
+ System.err.print("\u001b[91m") // bright red
errors.forEach {
val msg = it.toString()
if(msg !in reportedMessages) {
@@ -13,7 +13,7 @@ fun printErrors(errors: List, moduleName: String) {
reportedMessages.add(msg)
}
}
- print("\u001b[0m") // reset color
+ System.err.print("\u001b[0m") // reset color
if(reportedMessages.isNotEmpty())
throw ParsingFailedError("There are ${reportedMessages.size} errors in module '$moduleName'.")
}
diff --git a/compiler/src/prog8/compiler/Main.kt b/compiler/src/prog8/compiler/Main.kt
index 304e34c7c..b2378292e 100644
--- a/compiler/src/prog8/compiler/Main.kt
+++ b/compiler/src/prog8/compiler/Main.kt
@@ -25,7 +25,8 @@ class CompilationResult(val success: Boolean,
fun compileProgram(filepath: Path,
optimize: Boolean,
- writeAssembly: Boolean): CompilationResult {
+ writeAssembly: Boolean,
+ outputDir: Path): CompilationResult {
lateinit var programAst: Program
var programName: String? = null
@@ -102,7 +103,7 @@ fun compileProgram(filepath: Path,
// asm generation directly from the Ast, no need for intermediate code
val zeropage = MachineDefinition.C64Zeropage(compilerOptions)
programAst.anonscopeVarsCleanup()
- val assembly = AsmGen(programAst, compilerOptions, zeropage).compileToAssembly(optimize)
+ val assembly = AsmGen(programAst, compilerOptions, zeropage, outputDir).compileToAssembly(optimize)
assembly.assemble(compilerOptions)
programName = assembly.name
}
diff --git a/compiler/src/prog8/compiler/target/c64/AssemblyProgram.kt b/compiler/src/prog8/compiler/target/c64/AssemblyProgram.kt
index d4de7ffcf..89ccc2b85 100644
--- a/compiler/src/prog8/compiler/target/c64/AssemblyProgram.kt
+++ b/compiler/src/prog8/compiler/target/c64/AssemblyProgram.kt
@@ -2,12 +2,14 @@ package prog8.compiler.target.c64
import prog8.compiler.CompilationOptions
import prog8.compiler.OutputType
-import java.io.File
+import java.nio.file.Path
import kotlin.system.exitProcess
-class AssemblyProgram(val name: String) {
- private val assemblyFile = "$name.asm"
- private val viceMonListFile = "$name.vice-mon-list"
+class AssemblyProgram(val name: String, val outputDir: Path) {
+ private val assemblyFile = outputDir.resolve("$name.asm")
+ 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 {
// 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
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--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) {
OutputType.PRG -> {
command.add("--cbm-prg")
println("\nCreating C-64 prg.")
- "$name.prg"
+ prgFile
}
OutputType.RAW -> {
command.add("--nostart")
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 result = proc.waitFor()
@@ -57,7 +60,7 @@ class AssemblyProgram(val name: String) {
// builds list of breakpoints, appends to monitor list file
val breakpoints = mutableListOf()
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)
if(match!=null)
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(1, "; $num breakpoints have been defined")
breakpoints.add(2, "del")
- File(viceMonListFile).appendText(breakpoints.joinToString("\n")+"\n")
+ viceMonListFile.toFile().appendText(breakpoints.joinToString("\n")+"\n")
}
}
diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt
index a95130c90..76ea1f304 100644
--- a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt
+++ b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt
@@ -14,8 +14,8 @@ import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
import prog8.compiler.target.c64.Petscii
import prog8.functions.BuiltinFunctions
import prog8.functions.FunctionSignature
-import java.io.File
import java.math.RoundingMode
+import java.nio.file.Path
import java.util.*
import kotlin.math.absoluteValue
@@ -25,7 +25,8 @@ internal class AssemblyError(msg: String) : RuntimeException(msg)
internal class AsmGen(val program: Program,
val options: CompilationOptions,
- val zeropage: Zeropage) {
+ val zeropage: Zeropage,
+ val outputDir: Path) {
private val assemblyLines = mutableListOf()
private val globalFloatConsts = mutableMapOf() // 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) }
}
- return AssemblyProgram(program.name)
+ return AssemblyProgram(program.name, outputDir)
}
private fun header() {
diff --git a/compiler/test/UnitTests.kt b/compiler/test/UnitTests.kt
index 6125a0eff..5a14d83b0 100644
--- a/compiler/test/UnitTests.kt
+++ b/compiler/test/UnitTests.kt
@@ -19,7 +19,6 @@ import prog8.vm.RuntimeValueNumeric
import java.io.CharConversionException
import kotlin.test.*
-
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestCompiler {
@Test
@@ -54,7 +53,6 @@ class TestCompiler {
assertFailsWith { 65536L.toHex() }
}
-
@Test
fun testFloatToMflpt5() {
assertThat(Mflpt5.fromNumber(0), equalTo(Mflpt5(0x00, 0x00, 0x00, 0x00, 0x00)))
diff --git a/docs/source/building.rst b/docs/source/building.rst
index d1de6909f..faff28a29 100644
--- a/docs/source/building.rst
+++ b/docs/source/building.rst
@@ -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.
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
------------------------