Skip to content

Commit

Permalink
Merge pull request #294 from effekt-lang/feature/compile-option
Browse files Browse the repository at this point in the history
Rework `--compile` and add a seperate `--build` option
  • Loading branch information
b-studios authored Oct 23, 2023
2 parents 523e11b + acda3fa commit ffc7700
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 26 deletions.
1 change: 1 addition & 0 deletions effekt/jvm/src/main/scala/effekt/Driver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ trait Driver extends kiama.util.Compiler[EffektConfig, EffektError] { outer =>
// we are in one of three exclusive modes: LSPServer, Compile, Run
if (config.server()) { compiler.runFrontend(src) }
else if (config.interpret()) { compile() foreach runner.eval }
else if (config.build()) { compile() foreach runner.build }
else if (config.compile()) { compile() }
}
} catch {
Expand Down
13 changes: 9 additions & 4 deletions effekt/jvm/src/main/scala/effekt/EffektConfig.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@ class EffektConfig(args: Seq[String]) extends REPLConfig(args) {

val compile: ScallopOption[Boolean] = toggle(
"compile",
descrYes = "Compile the Effekt program",
descrNo = "Run the effekt program in the interpreter",
descrYes = "Compile the Effekt program to the backend specific representation",
default = Some(false)
)

val build: ScallopOption[Boolean] = toggle(
"build",
descrYes = "Compile the Effekt program and build a backend specific executable",
default = Some(false)
)

Expand Down Expand Up @@ -114,10 +119,10 @@ class EffektConfig(args: Seq[String]) extends REPLConfig(args) {

def requiresCompilation(): Boolean = !server()

def interpret(): Boolean = !server() && !compile()
def interpret(): Boolean = !server() && !compile() && !build()

validateFilesIsDirectory(includePath)

// force some other configs manually to intialize them when compiling with native-image
// force some other configs manually to initialize them when compiling with native-image
server; output; filenames
}
63 changes: 42 additions & 21 deletions effekt/jvm/src/main/scala/effekt/Runner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ package effekt

import effekt.context.Context
import effekt.util.messages.FatalPhaseError
import effekt.util.paths.{ File, file }
import effekt.util.paths.{File, file}
import effekt.util.getOrElseAborting
import kiama.util.IO

/**
* Interface used by [[Driver]] and [[EffektTests]] to run a compiled program.
Expand Down Expand Up @@ -46,14 +47,20 @@ trait Runner[Executable] {
def checkSetup(): Either[String, Unit]

/**
* Runs the executable (e.g. the main file).
* Builds a given executable and returns the resulting path to the executable.
*/
def eval(executable: Executable)(using Context): Unit
def build(executable: Executable)(using Context): String

/**
* Runs the executable (e.g. the main file) by calling the build function.
*/
def eval(executable: Executable)(using Context): Unit =
exec(build(executable))

def canRunExecutable(command: String*): Boolean =
try {
Process(command).run(ProcessIO(out => (), in => (), err => ())).exitValue() == 0
} catch { _ => false }
} catch { case _ => false }

/**
* Helper function to run an executable
Expand Down Expand Up @@ -93,11 +100,19 @@ object JSRunner extends Runner[String] {
if canRunExecutable("node", "--version") then Right(())
else Left("Cannot find nodejs. This is required to use the JavaScript backend.")

def eval(path: String)(using C: Context): Unit =
val out = C.config.outputPath()
val jsFile = (out / path).unixPath
val jsScript = s"require('${jsFile}').main().run()"
exec("node", "--eval", jsScript)
/**
* Creates an executable `.js` file besides the given `.js` file ([[path]])
* and then returns the absolute path of the created executable.
*/
def build(path: String)(using C: Context): String =
val out = C.config.outputPath().getAbsolutePath
val jsFilePath = (out / path).unixPath
// create "executable" using shebang besides the .js file
val jsScriptFilePath = jsFilePath.stripSuffix(s".$extension")
val jsScript = s"require('${jsFilePath}').main().run()"
val shebang = "#!/usr/bin/env node"
IO.createFile(jsScriptFilePath, s"$shebang\n$jsScript", true)
jsScriptFilePath
}

trait ChezRunner extends Runner[String] {
Expand All @@ -110,10 +125,17 @@ trait ChezRunner extends Runner[String] {
if canRunExecutable("scheme", "--help") then Right(())
else Left("Cannot find scheme. This is required to use the ChezScheme backend.")

def eval(path: String)(using C: Context): Unit =
val out = C.config.outputPath()
val chezFile = (out / path).unixPath
exec("scheme", "--script", chezFile)
/**
* Creates an executable bash script besides the given `.ss` file ([[path]])
* and returns the resulting absolute path.
*/
def build(path: String)(using C: Context): String =
val out = C.config.outputPath().getAbsolutePath
val schemeFilePath = (out / path).unixPath
val bashScriptPath = schemeFilePath.stripSuffix(s".$extension")
val bashScript = s"#!/bin/bash\nscheme --script $schemeFilePath"
IO.createFile(bashScriptPath, bashScript, true)
bashScriptPath
}

object ChezMonadicRunner extends ChezRunner {
Expand Down Expand Up @@ -150,7 +172,7 @@ object LLVMRunner extends Runner[String] {
* Requires LLVM and GCC to be installed on the machine.
* Assumes [[path]] has the format "SOMEPATH.ll".
*/
def eval(path: String)(using C: Context): Unit =
override def build(path: String)(using C: Context): String =
val out = C.config.outputPath()
val basePath = (out / path.stripSuffix(".ll")).unixPath
val llPath = basePath + ".ll"
Expand All @@ -168,8 +190,7 @@ object LLVMRunner extends Runner[String] {
val gccMainFile = (C.config.libPath / "main.c").unixPath
val executableFile = basePath
exec(gcc, gccMainFile, "-o", executableFile, objPath)

exec(executableFile)
executableFile
}


Expand All @@ -187,17 +208,17 @@ object MLRunner extends Runner[String] {
else Left("Cannot find mlton. This is required to use the ML backend.")

/**
* Compile the LLVM source file (`<...>.ll`) to an executable
* Compile the MLton source file (`<...>.sml`) to an executable.
*
* Requires LLVM and GCC to be installed on the machine.
* Assumes [[path]] has the format "SOMEPATH.ll".
* Requires the MLton compiler to be installed on the machine.
* Assumes [[path]] has the format "SOMEPATH.sml".
*/
def eval(path: String)(using C: Context): Unit =
override def build(path: String)(using C: Context): String =
val out = C.config.outputPath()
val buildFile = (out / "main.mlb").canonicalPath
val executable = (out / "mlton-main").canonicalPath
exec("mlton",
"-default-type", "int64", // to avoid integer overflows
"-output", executable, buildFile)
exec(executable)
executable
}
2 changes: 1 addition & 1 deletion kiama

0 comments on commit ffc7700

Please sign in to comment.