1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-06-12 06:29:34 +00:00

Detection of the default include path.

This commit is contained in:
Karol Stasiak 2018-12-24 02:38:28 +01:00
parent 7bf9616fcb
commit 438d8dbe6e
5 changed files with 83 additions and 18 deletions

View File

@ -8,6 +8,8 @@
* Unified the syntax of commandline switches.
* Automatic detection of the standard include path.
* Added aliases.
* Added enumeration types.

View File

@ -28,7 +28,12 @@ no extension for BBC micro program file,
* `-g` Generate also the label file. The label file contains labels with their addresses, with duplicates removed. It can be loaded into the monitor of the Vice emulator for debugging purposes. The file has the same name as the output file and the extension is `.lbl`.
* `-I <dir>;<dir>` The include directories. The current working directory is also an include directory. Those directories are searched for modules and platform definitions.
* `-I <dir>;<dir>` The include directories.
Those directories are searched for modules and platform definitions.
When searching for modules, the directory containing the file currently being compiled is also searched.
When searching for platform definitions, the current working directory is also searched.
If not given, the compiler will try to detect the default include directory.
If given, then the compiler will NOT try to detect the default include directory and you will have to add it to the list yourself.
* `-t <platform>` Target platform. It is loaded from an `.ini` file found in any of the include directories. See also [this document](target-platforms.md).

View File

@ -18,7 +18,7 @@ void main(){
Compile it using the following commandline:
```
java -jar millfork.jar hello_world.mfk -o hello_world -t c64 -I path_to_millfork\include
java -jar millfork.jar hello_world.mfk -o hello_world -t c64
```
Run the output executable (here using the VICE emulator):
@ -27,15 +27,13 @@ Run the output executable (here using the VICE emulator):
x64 hello_world.prg
```
## Basic commandline usage
## Basic command-line usage
The following options are crucial when compiling your sources:
The following options are obligatory when compiling your sources:
* `-o FILENAME` specifies the base name for your output file, an appropriate file extension will be appended (`prg` for Commodore, `xex` for Atari computers, `a2` for Apple, `asm` for assembly output, `lbl` for label file, `inf` for BBC file metadata, `dsk` for PC-88, `tap` for ZX Spectrum, `nes` for Famicom, `bin` for Atari 2600)
* `-I DIR;DIR;DIR;...` specifies the paths to directories with modules to include.
* `-t PLATFORM` specifies the target platform (`c64` is the default). Each platform is defined in an `.ini` file in the include directory. For the list of supported platforms, see [Supported platforms](target-platforms.md)
* `-t PLATFORM` specifies the target platform. Each platform is defined in an `.ini` file in the include directory. For the list of supported platforms, see [Supported platforms](target-platforms.md)
You may be also interested in the following:
@ -57,3 +55,5 @@ You may be also interested in the following:
* `-Wall` enable all warnings
* `--help` list all commandline options
For the comprehensive list of command-line options, see [Command-line options](./command-line.md).

View File

@ -1,5 +1,6 @@
package millfork
import java.io.File
import java.nio.charset.StandardCharsets
import java.nio.file.{Files, Paths}
import java.util.Locale
@ -58,7 +59,7 @@ object Main {
}
val startTime = System.nanoTime()
val (status, c) = parser(errorReporting).parse(Context(errorReporting, Nil), args.toList)
val (status, c0) = parser(errorReporting).parse(Context(errorReporting, Nil), args.toList)
status match {
case CliStatus.Quit => return
case CliStatus.Failed =>
@ -66,18 +67,22 @@ object Main {
case CliStatus.Ok => ()
}
errorReporting.assertNoErrors("Invalid command line")
if (c.inputFileNames.isEmpty) {
errorReporting.verbosity = c0.verbosity.getOrElse(0)
if (c0.inputFileNames.isEmpty) {
errorReporting.fatalQuit("No input files")
}
errorReporting.verbosity = c.verbosity.getOrElse(0)
errorReporting.debug("millfork version " + BuildInfo.version)
errorReporting.trace(s"Copyright (C) $copyrightYears Karol Stasiak")
errorReporting.trace("This program comes with ABSOLUTELY NO WARRANTY.")
errorReporting.trace("This is free software, and you are welcome to redistribute it under certain conditions")
errorReporting.trace("You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/")
val c = fixMissingIncludePath(c0)
if (c.includePath.isEmpty) {
errorReporting.warn("Failed to detect the default include directory, consider using the -I option")
}
val platform = Platform.lookupPlatformFile(c.includePath, c.platform.getOrElse {
val platform = Platform.lookupPlatformFile("." :: c.includePath, c.platform.getOrElse {
errorReporting.info("No platform selected, defaulting to `c64`")
"c64"
})
@ -149,6 +154,55 @@ object Main {
}
}
private def getDefaultIncludePath: Either[String, String] = {
try {
var where = new File(getClass.getProtectionDomain.getCodeSource.getLocation.toURI).getParentFile
if ((where.getName == "scala-2.12" || where.getName == "scala-2.13") && where.getParentFile.getName == "target") {
where = where.getParentFile.getParentFile
}
val dir = new File(where.getAbsolutePath + File.separatorChar + "include")
if (dir.exists()) {
Right(dir.getAbsolutePath)
} else {
Left(s"The ${dir.getAbsolutePath} directory doesn't exist")
}
} catch {
case e: Exception => Left(e.getMessage)
}
}
private def getAllDefaultPlatforms: Seq[String] = {
(getDefaultIncludePath match {
case Left(_) => Seq(
"c64", "c64_scpu", "c64_scpu16", "c64_crt9k", "c64_crt16k", "lunix",
"vic20", "vic20_3k", "vic20_8k", "vic20_a000",
"c16", "plus4", "pet", "c128",
"a8", "bbcmicro", "apple2",
"nes_mmc4", "nes_small", "vcs",
"zxspectrum", "zxspectrum_8080", "pc88", "cpc464",
"cpm", "cpm_z80")
case Right(path) =>
Seq(new File(".").list(), new File(path).list())
.filter(_ ne null)
.flatMap(_.toSeq)
.filter(_.endsWith(".ini"))
.map(_.stripSuffix(".ini"))
}).sorted
}
private def fixMissingIncludePath(c: Context)(implicit log: Logger): Context = {
if (c.includePath.isEmpty) {
getDefaultIncludePath match {
case Left(err) =>
log.debug(s"Failed to find the default include path: $err")
case Right(path) =>
log.debug(s"Automatically detected include path: $path")
return c.copy(includePath = List(path))
}
}
c
}
private def assembleForMos(c: Context, platform: Platform, options: CompilationOptions): AssemblerOutput = {
val optLevel = c.optimizationLevel.getOrElse(0)
val unoptimized = new MosSourceLoadingQueue(
@ -260,12 +314,12 @@ object Main {
parameter("-t", "--target").placeholder("<platform>").action { (p, c) =>
assertNone(c.platform, "Platform already defined")
c.copy(platform = Some(p))
}.description("Target platform, any of: c64, c16, plus4, vic20, vic20_3k, vic20_8k, pet, c128, a8, bbc, apple2, nes_mmc4, nes_small, c64_scpu, c64_scpu16, vcs.")
}.description(s"Target platform, any of:\n${getAllDefaultPlatforms.grouped(10).map(_.mkString(", ")).mkString(",\n")}.")
parameter("-I", "--include-dir").repeatable().placeholder("<dir>;<dir>;...").action { (paths, c) =>
val n = paths.split(";")
c.copy(includePath = c.includePath ++ n)
}.description("Include paths for modules.")
}.description("Include paths for modules. If not given, the default path is used: " + getDefaultIncludePath.fold(identity, identity))
parameter("-r", "--run").placeholder("<program>").action { (p, c) =>
assertNone(c.runFileName, "Run program already defined")
@ -432,7 +486,7 @@ object Main {
assertNone(c.optimizationLevel, "Optimization level already defined")
c.copy(optimizationLevel = Some(i))
}.description("Optimize code even more.")
if (i == 1 || i > 3) f.hidden()
if (i == 1 || i > 4) f.hidden()
}
flag("--inline").action { c =>
c.changeFlag(CompilationFlag.InlineFunctions, true)

View File

@ -6,13 +6,17 @@ package millfork.cli
trait CliOption[T, O <: CliOption[T, O]] {
this: O =>
def toStrings(firstTab: Int): List[String] = {
@inline
def indent(s: String):String = "".padTo(firstTab, ' ') + s
val fl = firstLine
if (_description == "") {
List(fl)
} else if (fl.length < firstTab) {
List(fl.padTo(firstTab, ' ') + _description)
return List(fl)
}
val descriptionLines = _description.split("\n")
if (fl.length < firstTab) {
(fl.padTo(firstTab, ' ') + descriptionLines.head) :: descriptionLines.tail.map(indent).toList
} else {
List(fl, "".padTo(firstTab, ' ') + _description)
fl :: descriptionLines.map(indent).toList
}
}