Background
I am developing a Play 2.2.3 application; the Play build system uses sbt.
I am using Sonatype Nexus OSS to manage various libraries built and deployed by Maven. At the moment, I am deploying snapshot versions of those libraries. For instance:
<groupId>myGroup</groupId>
<artifactId>myArtifact</artifactId>
<version>0.1-SNAPSHOT</version>
The Play application depends on snapshot versions of those libraries.
Intention
In the Play project, I want to use a specific (thus, "timestamped") snapshot version of a dependency -- say 0.1-20140509.053703-1 -- instead of the latest one as specified by 0.1-SNAPSHOT.
Issue
While the latter works perfectly well, sbt seems to be unable to resolve the specific version. At least sbt constructs the URL pointing to that specific version faultily: The name of the directory that contains the artifact ends with the actual timestamp instead of string SNAPSHOT.
Example
I created a new Play application and added the following dependency in file build.sbt: libraryDependencies += "myGroup" % "myArtifact" % "0.1-20140509.053703-1"
When resolving that dependency, sbt constructs URLs ending with myGroup/myArtifact/0.1-20140509.053703-1/myArtifact-0.1-20140509.053703-1.pom.
Note that the 3rd path component is 0.1-20140509.053703-1. Thus, the directory containing artifact myArtifact incorrectly contains the timestamp as well.
The correct directory name would be 0.1-SNAPSHOT; thus, using SNAPSHOT instead of the actual timestamp. The complete correct URL would thus end with myGroup/myArtifact/0.1-SNAPSHOT/myArtifact-0.1-20140509.053703-1.pom.
Referring to the latest snapshot version instead by using dependency entry libraryDependencies += "myGroup" % "myArtifact" % "0.1-SNAPSHOT" works. The URLs end with 0.1-SNAPSHOT/myArtifact-0.1-SNAPSHOT.pom.
Question
How could I persuade sbt to download a specific snapshot version?
Related
I have created basic Scala Play application with https://www.playframework.com/getting-started play-scala-seed. This project compiles and runs with sbt run. But I have another Scala project that compiles and runs and which I have submitted to my local Ivy repository with command sbt publishLocal. This other project was saved at C:\Users\tomr\.ivy2\local\com.agiintelligence\scala-isabelle_2.13\master-SNAPSHOT as a result of this command.
Then I imported (exactly so - imported, no just opened) my Play project in IntelliJ and I used Project - Open Module Settings - Project Settings - Libraries to add com.agiintelligence jar from my ivy2 location. After such operations IntelliJ editor recognizes com.agiintelligence classes. That is fine.
But when I am trying to run my Play application with sbt run, I experience the error message not found: object com that is exactly when compiling import com.agiintelligence line in my Scala controller file of Play application.
Of course - such error has been reported and resolved with, e.g. object play not found in scala application
But that solution suggests to append build.sbt file. My build.sbt file is pretty bare:
name := """agiintelligence"""
organization := "com.agiintelligence"
version := "1.0-SNAPSHOT"
lazy val root = (project in file(".")).enablePlugins(PlayScala)
scalaVersion := "2.13.5"
libraryDependencies += guice
libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "5.0.0" % Test
// Adds additional packages into Twirl
//TwirlKeys.templateImports += "com.skaraintelligence.controllers._"
// Adds additional packages into conf/routes
// play.sbt.routes.RoutesKeys.routesImport += "com.skaraintelligence.binders._"
My Play application contains (as can bee seen from the IntelliJ project pane) some tens of 'external libraries' (it shows my com.agiintelligence jar as well), but why should I add my own ivy2 library in build.sbt file if no other libraries are listed here? What is different with my library? It is on my computer, in the repository as expected already?
Of course, I can try to add it build.sbt and issue sbt update and see what happens, but I can not understand this logic? Can someone explain it and provide some clue to intelligible solution of my error message?
My Play application contains (as can bee seen from the IntelliJ project pane) some tens of 'external libraries'
Those are probably just transitive dependencies of your Play dependency, that is why sbt downloaded all of them and put them in your classpath so you could use them without you needing to tell it about them; because the pom of Play already did.
It is not that the build tool or the IDE magically added all those dependencies for you because they read your mind and magically understood you wanted them. And that for some reason the magic stopped working for your own library.
Why it is not sufficient to list it Project-Setting--External Libraries in IntelliJ only?
That is sufficient for the IDE to work, but not for the build tool. The build tool is independent of the IDE; it doesn't know about it. sbt just knows about the dependencies you configured in your definition file.
Even more, you should always configure your dependencies on your build tool and then import that in the IDE; rather than the opposite. IDEs are graphical tools, so their state can not be committed, can not be shared, can not keep track of changes, can not be used in CI / CD environments; additionally, different teammates may want to use different IDEs.
I resolved the error message by adding line in build.sbt file
libraryDependencies += "de.unruh" %% "scala-isabelle" % "master-SNAPSHOT"
and by subsequent run of sbt update.
Error is solved, but the main question still stand - why I had to do this? Why there are tens of dependencies that are not listed in build.sbt and why should I list my dependency in build.sbt and why it is not sufficient to list it Project-Setting--External Libraries in IntelliJ only?
OK, comment by #Luis_Miguel_Mejía_Suárez gave the explanation, that comment is the actual and expected answer to my question.
I'm experiencing a kind of impedance mismatch between sbt and bintray-sbt plugin. The plugin is published via bintray-sbt at https://bintray.com/artifact/download/synapse/sbt-plugins/me/synapse/my-sbt-plugin/0.0.1/my-sbt-plugin-0.0.1.pom (publishMavenStyle set to true. If set to false a different directory structure is created but still not the one sbt expects). Test project has
resolvers += Resolver.bintrayRepo("synapse", "sbt-plugins")
addSbtPlugin("me.synapse" % "my-sbt-plugin" % "0.0.1")
in project/plugins.sbt and sbt tries to download https://dl.bintray.com/synapse/sbt-plugins/me/synapse/my-sbt-plugin_2.10_0.13/0.0.1/my-sbt-plugin-0.0.1.pom
What settings should be used in plugin build definition to a) be able to test it from current repository and b) to be able to link it to sbt-plugin-releases repo when the time comes?
UPD: It looks like after the package was linked to sbt-plugin-releases it ended up in proper directory structure.
I am working on an sbt project and I need to pull a jar, available via url, into my project as an unmanaged dependency.
As an example:
I want to retrieve a jar file available at
http://www.sourceGorge.net/fooProject/v4.2/lib/bar.jar. If the jar file is absent from myProject/lib then simply download the source jar, do nothing otherwise.
One possible solution I found on the web suggests the following:
libraryDependencies += "net.sf.proguard" % "proguard-base" % "5.0"
from "file:///tmp/proguard5.0beta2/lib/proguard.jar"
But my question would then be: what am I supposed to substitute for "net.sf.proguard", "program-base", and "5.0" if all I have is a url of the form specified in the above example?
Thank you in advance, and happy holidays if this is your holiday season.
If you're pulling the jar from a url, it doesn't matter what you call it--preferably something informative. sbt uses the organization/artifact/version to resolve dependencies from a repository. If you're providing an exact url, then it doesn't really matter.
For example, I can resolve joda-time like this, and it works just fine:
libraryDependencies += "doesn't" % "matter" % "2.1" from "http://central.maven.org/maven2/joda-time/joda-time/2.1/joda-time-2.1.jar"
It also uses the organization/artifact/version combination to store the jar in a specific directory in your local ivy repository. The above line downloaded the jar to:
~/.ivy2/cache/doesn't/matter/jars/matter-2.1.jar
I'm trying to create a standalone jar file from the elastic4s sources on github. I've used sbt compile as detailed on the github page, but I can't find the jar file.
How do I use sbt to create the jar file so I can import it into my projects as a dependency?
The compile task will only compile the project.
> help compile
Compiles sources.
If you want to create a jar file and use it as a dependency in your project, you have two ways of doing that.
Unmanaged dependency (not recommended)
Unmanaged dependency run +package, which will create a jar file for each supported scala version, which you can use in your projects as an unmanaged dependency. Copy the package-generated jar to lib folder in your project.
The jar files will be located in target/scala-2.11 and target/scala-2.10, depending on the Scala version you want to use it with.
Publish to Local Repository (recommended yet imperfect)
If you want to include your custom-built elastic4s, as a managed dependency, you have to run +publishLocal. This will do the same as above, but additionally it will publish the artifact to your local repository. Assuming you've built it with version := "1.2.1.1-SNAPSHOT", you can include it in your project by just adding:
libraryDependencies += "com.sksamuel.elastic4s" %% "elastic4s" % "1.2.1.1-SNAPSHOT"
What makes the approach imperfect is that once you shared the project on GitHub (or any other project sharing platform), people will have to do publishLocal themselves to be able to build your project. The dependency should therefore go to one of the official binary repositories so when a dependency is needed, it's downloaded from Internet. Consult Publishing.
What is the + character in front of the commands
The + in the commands is for cross-building, if you don't use it the command will be executed only using scalaVersion declared in the build.sbt of the project.
I have artifactory with multiple snapshots versions such as artifact-0.1-20120831.103456-5.
My project depends on specific snapshot version.
If I tell SBT to download 0.1-20120831.103456-5 version instead of 0.1-SNAPSHOT it fails on update task.
// build.sbt
libraryDependencies ++= Seq(
"com.example" % "smith" % "0.1-20120906.110133-36")
// sbt update
[warn] ==== My Repo snapshots: tried
[warn] http://repo.localhost/snapshots/com/example/smith/0.1-20120906.110133-36/commons-0.1-20120906.110133-36.pom
How to make SBT search artifacts in http://repo.localhost/snapshots/com/example/smith/0.1-SNAPSHOT directory but use unique snapshot version?
Addition Apart from the unique version plugin, sbt also has the aether-deploy plugin (see below).
There is the unique version plugin which lets you resolve your artifacts like you want. Quote from the page:
How to point at it
"0.1.0" or "0.1.0-20120602-073010" you can always use the static version number.
"0.1.0-+" selects the latest 0.1.0 snapshot.
"latest.integration" selects the latest revision regardless of its status.
"latest.milestone" selects the latest revision with either Milestone or Release status.
"latest.release" selects the latest with the Release status.
But you also have to publish with this plugin, since the artifacts are published in a different manner regarding the version: In your example, the artifact won't be stored under 0.1-SNAPSHOT directory but under 0.1-20120831.103456-5
Addition
There is also the aether-deploy plugin which uses Aether (Aether offers a standard way of interacting with Maven repositories). Problem is that this plugin currently works for deploying only (as the name of the plugin suggests). Maybe the author has plans to extend it so that it works for resolving as well (sounds like a useful feature to me). If you can't publish with the unique version plugin (e.g. if the snapshots are not owned by you), then you could ask that at the sbt forum.
So I can't offer a solution that works precisely as maven does for your use case, but hopefully it gives some useful info for you and others.
An ugly workaround would be to install the snapshot artefact in your own groupId (say smith.external) NOT as a SNAPSHOT, using install:instal-file
and declare the desired version number instead of declaring the usage of snapshot.
As you don't expect the version to change, you can rely on this, until you get it to work with a provided stable version (and the regular groupId)
Not the best solution but you may try to use conflict managers provided by Ivy (see apache docs). For example, 'latest-revision' is used by default and 'latest-compatible' manager should disallow any dependency conflicts.
It doesn't look easy to set up though, someone on google sbt group posted the following rule:
def addConflictManager(org: String, name: String, conflictManager: String) =
ivyModule <<= (ivyModule, streams) map { (module, s) =>
module.withModule(s.log) { (ivy, desc, _) =>
import _root_.org.apache.ivy.{core, plugins}
import core.module.id.ModuleId
import plugins.matcher.PatternMatcher
desc.addConflictManager(
ModuleId.newInstance(org, name),
ivy.getSettings.getMatcher(PatternMatcher.EXACT),
ivy.getSettings.getConflictManager("latest-compatible"))
module
}
}