-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Minimal scripting support #11379
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
anatoliykmetyuk
merged 8 commits into
scala:master
from
philwalk:minimal-scripting-support
Feb 15, 2021
Merged
Minimal scripting support #11379
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
c95f4af
changes to dist/bin/scala script:
philwalk 6c0a15e
changes to dist/bin/scala script:
philwalk 693ffc5
more robust script detection in dist/bin/scala
philwalk 20a7533
fix problem in dist/bin/script, simplify target_jar test
philwalk 3555838
resolved requested changes
philwalk 3bce0a0
corrected save_compiled test
philwalk 4604261
replaced sys.error with assertion w/internal error message
philwalk be1072f
+ added more robust detection of relative classpath entries
philwalk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,108 @@ | ||
package dotty.tools.scripting | ||
|
||
import java.io.File | ||
import java.nio.file.Path | ||
import dotty.tools.dotc.config.Properties.isWin | ||
|
||
/** Main entry point to the Scripting execution engine */ | ||
object Main: | ||
/** All arguments before -script <target_script> are compiler arguments. | ||
All arguments afterwards are script arguments.*/ | ||
def distinguishArgs(args: Array[String]): (Array[String], File, Array[String]) = | ||
val (compilerArgs, rest) = args.splitAt(args.indexOf("-script")) | ||
private def distinguishArgs(args: Array[String]): (Array[String], File, Array[String], Boolean, Boolean) = | ||
val (leftArgs, rest) = args.splitAt(args.indexOf("-script")) | ||
assert(rest.size >= 2,s"internal error: rest == Array(${rest.mkString(",")})") | ||
|
||
val file = File(rest(1)) | ||
val scriptArgs = rest.drop(2) | ||
(compilerArgs, file, scriptArgs) | ||
var saveJar = false | ||
var invokeFlag = true // by default, script main method is invoked | ||
val compilerArgs = leftArgs.filter { | ||
case "-save" | "-savecompiled" => | ||
saveJar = true | ||
false | ||
case "-compile-only" => | ||
invokeFlag = false // no call to script main method | ||
false | ||
case _ => | ||
true | ||
} | ||
(compilerArgs, file, scriptArgs, saveJar, invokeFlag) | ||
end distinguishArgs | ||
|
||
def main(args: Array[String]): Unit = | ||
val (compilerArgs, scriptFile, scriptArgs) = distinguishArgs(args) | ||
try ScriptingDriver(compilerArgs, scriptFile, scriptArgs).compileAndRun() | ||
val (compilerArgs, scriptFile, scriptArgs, saveJar, invokeFlag) = distinguishArgs(args) | ||
val driver = ScriptingDriver(compilerArgs, scriptFile, scriptArgs) | ||
try driver.compileAndRun { (outDir:Path, classpath:String, mainClass: String) => | ||
if saveJar then | ||
// write a standalone jar to the script parent directory | ||
writeJarfile(outDir, scriptFile, scriptArgs, classpath, mainClass) | ||
invokeFlag | ||
} | ||
catch | ||
case ScriptingException(msg) => | ||
println(s"Error: $msg") | ||
sys.exit(1) | ||
|
||
case e: java.lang.reflect.InvocationTargetException => | ||
throw e.getCause | ||
|
||
private def writeJarfile(outDir: Path, scriptFile: File, scriptArgs:Array[String], | ||
classpath:String, mainClassName: String): Unit = | ||
|
||
val javaClasspath = sys.props("java.class.path") | ||
val runtimeClasspath = s"${classpath}$pathsep$javaClasspath" | ||
|
||
val jarTargetDir: Path = Option(scriptFile.toPath.getParent) match { | ||
case None => sys.error(s"no parent directory for script file [$scriptFile]") | ||
case Some(parent) => parent | ||
} | ||
|
||
def scriptBasename = scriptFile.getName.takeWhile(_!='.') | ||
val jarPath = s"$jarTargetDir/$scriptBasename.jar" | ||
|
||
val cpPaths = runtimeClasspath.split(pathsep).map(_.absPath) | ||
|
||
import java.util.jar.Attributes.Name | ||
val cpString:String = cpPaths.distinct.mkString(" ") | ||
val manifestAttributes:Seq[(Name, String)] = Seq( | ||
(Name.MANIFEST_VERSION, "1.0"), | ||
(Name.MAIN_CLASS, mainClassName), | ||
(Name.CLASS_PATH, cpString), | ||
) | ||
import dotty.tools.io.{Jar, Directory} | ||
val jar = new Jar(jarPath) | ||
val writer = jar.jarWriter(manifestAttributes:_*) | ||
try | ||
writer.writeAllFrom(Directory(outDir)) | ||
finally | ||
writer.close() | ||
end writeJarfile | ||
|
||
def pathsep = sys.props("path.separator") | ||
|
||
|
||
extension(file: File){ | ||
def norm: String = file.toString.norm | ||
} | ||
|
||
extension(path: String) { | ||
// Normalize path separator, convert relative path to absolute | ||
def norm: String = | ||
path.replace('\\', '/') match { | ||
case s if s.secondChar == ":" => s.drop(2) | ||
case s if s.startsWith("./") => s.drop(2) | ||
case s => s | ||
} | ||
|
||
// convert to absolute path relative to cwd. | ||
def absPath: String = norm match | ||
case str if str.isAbsolute => norm | ||
case _ => s"/${sys.props("user.dir").norm}/$norm" | ||
|
||
def absFile: File = File(path.absPath) | ||
|
||
// Treat norm paths with a leading '/' as absolute. | ||
// Windows java.io.File#isAbsolute treats them as relative. | ||
def isAbsolute = path.norm.startsWith("/") || (isWin && path.secondChar == ":") | ||
def secondChar: String = path.take(2).drop(1).mkString("") | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
#!/usr/bin/env scala | ||
# comment | ||
STUFF=nada | ||
!# | ||
// everything above this point should be ignored by the compiler | ||
def main(args: Array[String]): Unit = | ||
args.zipWithIndex.foreach { case (arg,i) => printf("arg %d: [%s]\n",i,arg) } | ||
System.err.printf("mainClassFromStack: %s\n",mainFromStack) | ||
assert(mainFromStack.contains("hashBang"),s"fromStack[$mainFromStack]") | ||
|
||
lazy val mainFromStack:String = { | ||
val result = new java.io.StringWriter() | ||
new RuntimeException("stack").printStackTrace(new java.io.PrintWriter(result)) | ||
val stack = result.toString.split("[\r\n]+").toList | ||
if verbose then for( s <- stack ){ System.err.printf("[%s]\n",s) } | ||
stack.filter { str => str.contains(".main(") }.map { | ||
// derive main class name from stack when main object is NOT declared in source | ||
_.replaceAll("[.].*",""). | ||
replaceAll("\\s+at\\s+","") | ||
}.distinct.take(1).mkString("") | ||
} | ||
|
||
lazy val verbose = Option(System.getenv("DOTC_VERBOSE")) match | ||
case None => false | ||
case _ => true |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#!/usr/bin/env scala | ||
export STUFF=nada | ||
#lots of other stuff that isn't valid scala | ||
!# | ||
// everything above this point should be ignored by the compiler | ||
object Zoo { | ||
def main(args: Array[String]): Unit = | ||
args.zipWithIndex.foreach { case (arg,i) => printf("arg %d: [%s]\n",i,arg) } | ||
printf("mainClassFromStack: %s\n",mainClassFromStack) | ||
assert(mainClassFromStack == "Zoo",s"fromStack[$mainClassFromStack]") | ||
|
||
lazy val mainClassFromStack:String = { | ||
val result = new java.io.StringWriter() | ||
new RuntimeException("stack").printStackTrace(new java.io.PrintWriter(result)) | ||
val stack = result.toString.split("[\r\n]+").toList | ||
if verbose then for( s <- stack ){ System.err.printf("[%s]\n",s) } | ||
val shortStack = stack.filter { str => str.contains(".main(") && ! str.contains("$") }.map { | ||
// derive main class name from stack when main object is declared in source | ||
_.replaceAll("[.].*",""). | ||
replaceAll("\\s+at\\s+","") | ||
} | ||
// for( s <- shortStack ){ System.err.printf("[%s]\n",s) } | ||
shortStack.take(1).mkString("|") | ||
} | ||
|
||
lazy val verbose = Option(System.getenv("DOTC_VERBOSE")) match | ||
case None => false | ||
case _ => true | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import java.nio.file.Paths | ||
|
||
object ScriptParent { | ||
def main(args: Array[String]): Unit = { | ||
args.zipWithIndex.foreach { case (arg,i) => printf("arg %d: [%s]\n",i,arg) } | ||
val scriptName = Option(sys.props("script.path")) match { | ||
case None => | ||
printf("no script.path property\n") | ||
case Some(script) => | ||
val p = Paths.get(script).toAbsolutePath.toFile.getParent | ||
printf("parentDir: [%s]\n",p) | ||
} | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#!/usr/bin/env scala | ||
|
||
def main(args: Array[String]): Unit = | ||
args.zipWithIndex.foreach { case (arg,i) => printf("arg %d: [%s]\n",i,arg) } | ||
val path = Option(sys.props("script.path")) match { | ||
case None => printf("no script.path property is defined\n") | ||
case Some(path) => | ||
printf("script.path: %s\n",path) | ||
assert(path.endsWith("scriptPath.sc"),s"actual path [$path]") | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
#!/usr/bin/env scala | ||
|
||
import java.io.File | ||
|
||
// create an empty file | ||
def main(args: Array[String]): Unit = | ||
val file = File("touchedFile.out") | ||
file.createNewFile(); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.