Skip to content

Commit 9c98028

Browse files
Make package --python work
1 parent ee5e528 commit 9c98028

File tree

4 files changed

+89
-6
lines changed

4 files changed

+89
-6
lines changed

build.sc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,12 @@ object dummy extends Module {
180180
Deps.scalafmtCli
181181
)
182182
}
183+
object pythonInterface extends JavaModule with Bloop.Module {
184+
def skipBloop = true
185+
def ivyDeps = Agg(
186+
Deps.pythonInterface
187+
)
188+
}
183189
}
184190

185191
trait BuildMacros extends ScalaCliSbtModule
@@ -662,6 +668,9 @@ trait Cli extends SbtModule with ProtoBuildModule with CliLaunchers
662668
|
663669
|/** Build-time constants. Generated by mill. */
664670
|object Constants {
671+
| def pythonInterfaceOrg = "${Deps.pythonInterface.dep.module.organization.value}"
672+
| def pythonInterfaceName = "${Deps.pythonInterface.dep.module.name.value}"
673+
| def pythonInterfaceVersion = "${Deps.pythonInterface.dep.version}"
665674
| def launcherTypeResourcePath = "${launcherTypeResourcePath.toString}"
666675
| def defaultFilesResourcePath = "$defaultFilesResourcePath"
667676
| def maxAmmoniteScala3Version = "${Scala.maxAmmoniteScala3Version}"

modules/cli/src/main/scala/scala/cli/commands/package0/Package.scala

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package scala.cli.commands.package0
33
import ai.kien.python.Python
44
import caseapp.*
55
import coursier.launcher.*
6+
import dependency.*
67
import packager.config.*
78
import packager.deb.DebianPackage
89
import packager.docker.DockerPackage
@@ -36,7 +37,7 @@ import scala.cli.commands.util.BuildCommandHelpers
3637
import scala.cli.commands.{CommandUtils, ScalaCommand, WatchUtil}
3738
import scala.cli.config.{ConfigDb, Keys}
3839
import scala.cli.errors.ScalaJsLinkingError
39-
import scala.cli.internal.{CachedBinary, ProcUtil, ScalaJsLinker}
40+
import scala.cli.internal.{CachedBinary, Constants, ProcUtil, ScalaJsLinker}
4041
import scala.cli.packaging.{Library, NativeImage}
4142
import scala.util.Properties
4243

@@ -300,7 +301,7 @@ object Package extends ScalaCommand[PackageOptions] with BuildCommandHelpers {
300301

301302
val outputPath = packageType match {
302303
case PackageType.Bootstrap =>
303-
value(bootstrap(build, destPath, value(mainClass), () => alreadyExistsCheck()))
304+
value(bootstrap(build, destPath, value(mainClass), () => alreadyExistsCheck(), logger))
304305
destPath
305306
case PackageType.LibraryJar =>
306307
val content = Library.libraryJar(build)
@@ -391,7 +392,15 @@ object Package extends ScalaCommand[PackageOptions] with BuildCommandHelpers {
391392

392393
case nativePackagerType: PackageType.NativePackagerType =>
393394
val bootstrapPath = os.temp.dir(prefix = "scala-packager") / "app"
394-
value(bootstrap(build, bootstrapPath, value(mainClass), () => alreadyExistsCheck()))
395+
value {
396+
bootstrap(
397+
build,
398+
bootstrapPath,
399+
value(mainClass),
400+
() => alreadyExistsCheck(),
401+
logger
402+
)
403+
}
395404
val sharedSettings = SharedSettings(
396405
sourceAppPath = bootstrapPath,
397406
version = packageOptions.packageVersion,
@@ -627,7 +636,7 @@ object Package extends ScalaCommand[PackageOptions] with BuildCommandHelpers {
627636

628637
val appPath = os.temp.dir(prefix = "scala-cli-docker") / "app"
629638
build.options.platform.value match {
630-
case Platform.JVM => value(bootstrap(build, appPath, mainClass, () => Right(())))
639+
case Platform.JVM => value(bootstrap(build, appPath, mainClass, () => Right(()), logger))
631640
case Platform.JS => buildJs(build, appPath, mainClass, logger)
632641
case Platform.Native =>
633642
val dest = value(buildNative(build, mainClass, logger))
@@ -669,7 +678,8 @@ object Package extends ScalaCommand[PackageOptions] with BuildCommandHelpers {
669678
build: Build.Successful,
670679
destPath: os.Path,
671680
mainClass: String,
672-
alreadyExistsCheck: () => Either[BuildException, Unit]
681+
alreadyExistsCheck: () => Either[BuildException, Unit],
682+
logger: Logger
673683
): Either[BuildException, Unit] = either {
674684
val byteCodeZipEntries = os.walk(build.output)
675685
.filter(os.isFile(_))
@@ -708,10 +718,41 @@ object Package extends ScalaCommand[PackageOptions] with BuildCommandHelpers {
708718
.withOsKind(Properties.isWin)
709719
.callsItself(Properties.isWin)
710720
.withJavaOpts(build.options.javaOptions.javaOpts.toSeq.map(_.value.value))
711-
val params = Parameters.Bootstrap(Seq(loaderContent), mainClass)
721+
val baseParams = Parameters.Bootstrap(Seq(loaderContent), mainClass)
712722
.withDeterministic(true)
713723
.withPreamble(preamble)
714724

725+
val params =
726+
if (build.options.notForBloopOptions.doSetupPython.getOrElse(false)) {
727+
val res = value {
728+
Artifacts.fetch(
729+
Positioned.none(Seq(
730+
dep"${Constants.pythonInterfaceOrg}:${Constants.pythonInterfaceName}:${Constants.pythonInterfaceVersion}"
731+
)),
732+
Nil,
733+
None,
734+
logger,
735+
build.options.finalCache,
736+
None,
737+
Some(_)
738+
)
739+
}
740+
val entries = res.artifacts.map {
741+
case (a, f) =>
742+
val path = os.Path(f)
743+
if (build.options.notForBloopOptions.packageOptions.isStandalone)
744+
ClassPathEntry.Resource(path.last, os.mtime(path), os.read.bytes(path))
745+
else
746+
ClassPathEntry.Url(a.url)
747+
}
748+
val pythonContent = Seq(
749+
ClassLoaderContent(entries)
750+
)
751+
baseParams.addExtraContent("python", pythonContent).withPython(true)
752+
}
753+
else
754+
baseParams
755+
715756
value(alreadyExistsCheck())
716757
BootstrapGenerator.generate(params, destPath.toNIO)
717758
ProcUtil.maybeUpdatePreamble(destPath)

modules/integration/src/test/scala/scala/cli/integration/PackageTestDefinitions.scala

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,4 +894,36 @@ abstract class PackageTestDefinitions(val scalaVersionOpt: Option[String])
894894
)
895895
}
896896
}
897+
898+
test("scalapy") {
899+
900+
def maybeScalapyPrefix =
901+
if (actualScalaVersion.startsWith("2.13.")) ""
902+
else "import me.shadaj.scalapy.py" + System.lineSeparator()
903+
904+
val inputs = TestInputs(
905+
os.rel / "Hello.scala" ->
906+
s"""$maybeScalapyPrefix
907+
|object Hello {
908+
| def main(args: Array[String]): Unit = {
909+
| py.Dynamic.global.print("Hello from Python", flush = true)
910+
| }
911+
|}
912+
|""".stripMargin
913+
)
914+
915+
val dest =
916+
if (Properties.isWin) "hello.bat"
917+
else "hello"
918+
919+
inputs.fromRoot { root =>
920+
os.proc(TestUtil.cli, "package", "--python", ".", "-o", dest, extraOptions)
921+
.call(cwd = root, stdin = os.Inherit, stdout = os.Inherit)
922+
923+
val launcher = root / dest
924+
val res = os.proc(launcher).call(cwd = root)
925+
val output = res.out.trim()
926+
expect(output == "Hello from Python")
927+
}
928+
}
897929
}

project/deps.sc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ object Deps {
139139
def organizeImports = ivy"com.github.liancheng::organize-imports:0.6.0"
140140
def osLib = ivy"com.lihaoyi::os-lib:0.8.1"
141141
def pprint = ivy"com.lihaoyi::pprint:0.8.0"
142+
def pythonInterface = ivy"io.github.alexarchambault.python:interface:0.1.0"
142143
def pythonNativeLibs = ivy"ai.kien::python-native-libs:0.2.4"
143144
def scala3Compiler(sv: String) = ivy"org.scala-lang:scala3-compiler_3:$sv"
144145
def scalaAsync = ivy"org.scala-lang.modules::scala-async:1.0.1".exclude("*" -> "*")

0 commit comments

Comments
 (0)