Skip to content
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

Allow the sbt launcher to be called programmatcially without running into an exit call #7540

Open
sschuberth opened this issue Apr 17, 2024 · 10 comments

Comments

@sschuberth
Copy link

I'd like to call the sbt launcher programmatically from a Kotlin/JVM application in order to analyze sbt projects. For that, I'm using the org.scala-sbt:sbt-launch:1.9.9 artifact from Maven Central, and call xsbt.boot.main() programmatically with arguments I'd usually pass via command line.

This basically does what I want, but afterwards brings down my JVM-based host application, as somewhere as part of xsbt.boot.main() the exit() function seems to be called.

I'm no Scala guy, but it seems to me as if some rather simple refactoring could provide a programmatic entry point that avoid the exit() call (and maybe also takes an array of arguments rather than a CLI-like argument string).

@eed3si9n
Copy link
Member

Probably coming from https://github.com/sbt/launcher/blob/1.x/launcher-implementation/src/main/scala/xsbt/boot/Boot.scala. Would a system property like sbt.boot.exit=false work?

@sschuberth
Copy link
Author

Would a system property like sbt.boot.exit=false work?

For my use-case, yes. Though I believe that introducing conditional code for this could be more complex and introduce pitfalls (like users setting that system property despite using SBT via the CLI) compared to just doing a refactoring that introduces a runLauncher() or similar function (sorry, pseudo-code):

main(String args) {
    arrayOfArgs = parseArgs(args)
    runLauncher(arrayOfArgs)
    exit(0)
}

runLauncher(Array args) {
    // Do the actual work.
}

The Jar's main entry point would still be main(), but programmatic users could just call runlauncher() instead.

@eed3si9n
Copy link
Member

I don't know if the original scope of sbt launcher was to be a generic launcher, but given that almost no one else but sbt uses it I'd suggest looking into Couriser app as a launcher, or drive Coursier programmatically instead.

Here's launcher code for Giter8 for example https://github.com/foundweekends/giter8/blob/develop/launcher/src/main/scala/LauncherMain.scala that resolves artifacts from Maven repo and calls run via reflection.

@sschuberth
Copy link
Author

sschuberth commented Apr 17, 2024

Hmm, I'm starting to get confused about launcher-related artifacts now... what's the difference between

@eed3si9n
Copy link
Member

https://github.com/sbt/launcher hosts launcher-interface and launcher. launcher-interface is the Java interface that exposes an API to the application to be launched, whereas launcher is an über JAR of a generic launcher implementation. This code lives somewhat independently from sbt. sbt-launch is a repackaging of launcher with sbt's configuration like which repositories to read from (it used to be hosted on an Ivy repo for sbt 0.13, for example).

@sschuberth
Copy link
Author

Thanks. And where does Coursier now come into play? IIUC Coursier is "just" a dependency resolver, but not a launcher. So maybe launcher at some point started to use Coursier as the dependency resolver?

Anyway, what I'm looking for is a way to programmatically bootstrap the SBT version configured in a project, just like the launcher does, and to run commands like sbt projects / sbt makePom just like from the command line. All in all, what's your recommendation how to achieve that currently?

@eed3si9n
Copy link
Member

Anyway, what I'm looking for is a way to programmatically bootstrap the SBT version... All in all, what's your recommendation how to achieve that currently?

To some extent that's what CI process like GitHub Actions and Jenkins do basically by creating files and shelling out to an external process. So think that would be probably the easiest regardless of the programming language you're currently on. You can use sbt --client for example to let sbt server run in the background so you don't pay the loading for each command, and shut it down with sbt --client shutdown.

@sschuberth
Copy link
Author

So think that would be probably the easiest regardless of the programming language you're currently on.

Right, but the whole point was to take advantage of SBT being implemented for the JVM, just as my application, and to call it directly via some API to avoid the dependency on an external CLI tool that I would need to bundle with my application...

@eed3si9n
Copy link
Member

In addition to project/build.properties, people can pass in sys props directly from the command line, or use .sbtopts file interpreted in https://github.com/sbt/sbt/blob/1.10.x/sbt. It might be relatively easy to start "some API", but we could be opening up a can of worm that is never compatible with the real sbt CLI and a maintenance headache for both parties.

@sschuberth
Copy link
Author

we could be opening up a can of worm that is never compatible with the real sbt CLI

Agreed, that's why all I was asking for was to be able to basically call the CLI programmatically via Maven artifacts without running into the exit call 😉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants