Ammonite: how to use another script from an Ivy dependency?

660 Views Asked by At

I have an Ammonite Script that I want to deliver in a JAR.

In another project I want to use this Script - but so far with no success.

I tried according to the documentation (sol_local_build.sc):

import $ivy.`mycompany:myproject_2.12:2.1.0-SNAPSHOT`, local_build

@main
def doit():Unit =
  println(local_build.curl("http://localhost:8080"))

local_build.sc is in the Script I want to use.

This is the exception I get:

sol_local_build.sc:2: '.' expected but eof found.

^
3

There are 3 best solutions below

0
On

For completeness, I could solve my problem as follows:

  • Just create a Scala File in this project.
  • Copy the Script content in an Object.
    package mycompany.myproject
    
    object LocalBuild {
       def curl(..)...
    }
    
  • Add the dependencies to your sbt file (e.g. ammonite.ops)
  • Use it like:
    $ivy.`mycompany:myproject_2.12:2.1.0-SNAPSHOT`, mycompany.myproject.LocalBuild
    
       @main
       def doit():Unit =
          println(LocalBuild.curl("http://localhost:8080"))
    
    
6
On

The script must be compiled on the fly.

Put your script in a standard sbt project

inside a directory, example directory name: "test1"

Put your external script (example name: "script.sc")

// script.sc

println("Hello world!")

into the resource directory ("test1\src\main\resources\script.sc") of the test1 project

Publish the projekt local, i.e. sbt publishLocal

It is published to ".ivy2\local\default\test1_2.12\0.1-SNAPSHOT\ ... " directory.

Now you can use the following ammonite script "test.sc".

It reads the "script.sc" from the jar in the local ivy repository

and writes it to the local directory (must have read/write access) and then executes an external process,

which calls the scala "interpreter" and executes the script.

// test.sc

import $ivy.`default:test1_2.12:0.1-SNAPSHOT`


val scriptCode = scala.util.Try {scala.io.Source.fromResource("script.sc").mkString} getOrElse """Println("Script-file not found!")"""

println("*" * 30)
println(scriptCode)
println("*" * 30)
println()


java.nio.file.Files.write(java.nio.file.Paths.get("script.sc"), scriptCode.getBytes(java.nio.charset.StandardCharsets.UTF_8))

val cmd = Seq("cmd.exe", "/c", "scala", "script.sc")

val output = sys.process.Process(cmd).!! 

println(output)

Executing the script the Ammonite REPL, you get:

******************************
// script.sc

println("Hello world!")
******************************

Hello world!

The script has no error handling and leaves the file in the running directory. You can speed up the execution with the "-savecompiled" compiler switch, i.e

val cmd = Seq("cmd.exe", "/c", "scala", "-savecompiled", "script.sc")

An additional .jar file is created then in the running directory.

1
On

Scala Scripts are not really interpreted, but are compiled "under the hood" as every normal Scala programm. Therefor all code must be reachable during compile time and you cannot call a function inside the other script from the jar-file!

But Ammonite has a buid in multi-stage feature. It compiles one part, executes it and then compiles the next part!

Little improved ammonite-script. It's not error free but runs.

Maybe there is better way to get the script out of the jar. You should ask Li Haoyi!

// test_ammo.sc 
// using ammonite ops

// in subdirectoy /test1

// Ammonite REPL:
// import $exec.test1.test_ammo
// @ Ammonite-multi-stage

import $ivy.`default::test1:0.1-SNAPSHOT`

//import scala.util.Properties
import scala.sys.process.Process

val scriptFileName = "script.sc"

write.over(pwd/"test1"/scriptFileName, read(resource(getClass.getClassLoader)/scriptFileName))

val cmd = Seq("cmd.exe", "/c", "scala", scriptFileName)
val output = Process(cmd).!! 
println(output)

@

import $exec.script // no .sc suffix

ppp() // is a function inside script.sc

script.sc inside resources folder of project published local with "sbt publishLocal":

// script.sc

println("Hello world!")

def ppp() = println("Hello world from ppp!")