Add basic support for the Pants build tool#935
Conversation
mzarnowski
left a comment
There was a problem hiding this comment.
Thanks for the contribution! I couldn't resist, so I have looked around.
I have left some questions and suggestions but I suspect @tgodzik (our expert on handling build tools) will probably do another review once this is ready.
metals/src/main/scala/scala/meta/internal/builds/BuildTool.scala
Outdated
Show resolved
Hide resolved
metals/src/main/scala/scala/meta/internal/builds/PantsDigest.scala
Outdated
Show resolved
Hide resolved
metals/src/main/scala/scala/meta/internal/metals/UserConfiguration.scala
Outdated
Show resolved
Hide resolved
metals/src/main/scala/scala/meta/internal/metals/UserConfiguration.scala
Outdated
Show resolved
Hide resolved
metals/src/main/scala/scala/meta/internal/builds/PantsDigest.scala
Outdated
Show resolved
Hide resolved
0705006 to
79171d6
Compare
tgodzik
left a comment
There was a problem hiding this comment.
Reviewed most of the files, only the strictly pants related stuff eludes me a bit, so that was not reviewed that well.
It's mostly some doubts I have and few suggestions on what to improve.
metals/src/main/scala/scala/meta/internal/pantsbuild/BloopPants.scala
Outdated
Show resolved
Hide resolved
metals/src/main/scala/scala/meta/internal/pantsbuild/BloopPants.scala
Outdated
Show resolved
Hide resolved
metals/src/main/scala/scala/meta/internal/pantsbuild/BloopPants.scala
Outdated
Show resolved
Hide resolved
metals/src/main/scala/scala/meta/internal/pantsbuild/BloopPants.scala
Outdated
Show resolved
Hide resolved
metals/src/main/scala/scala/meta/internal/pantsbuild/BloopPants.scala
Outdated
Show resolved
Hide resolved
metals/src/main/scala/scala/meta/internal/pantsbuild/BloopPants.scala
Outdated
Show resolved
Hide resolved
| _ = assertStatus(_.isInstalled) | ||
| } yield assertNoDiff(client.workspaceMessageRequests, "") | ||
| } | ||
| // |
| } | ||
|
|
||
| object BuildTools { | ||
| def all: List[BuildTool] = { |
There was a problem hiding this comment.
This is only used in tests and there is an already existing all method. Could we remove it?
There was a problem hiding this comment.
The other all method doesn't work the same way. In the tests we enumerated the whole list in several different places. I have moved this method into the BuildTools class and converted this into a default method to get a default BuildTools instance, which we can use for testing purposes.
| pants.toFile().setExecutable(true) | ||
| import scala.sys.process._ | ||
| val exit = List(pants.toString(), "generate-pants-ini").! | ||
| require(exit == 0, "failed to generate pants.ini") |
There was a problem hiding this comment.
Can we have a static pants.ini defined with rest of the files. This will make the tests run much slower.
| val userConfig: () => UserConfiguration = () => UserConfiguration() | ||
| val config = MetalsServerConfig.default | ||
| List( | ||
| SbtBuildTool.apply("", userConfig, config), |
There was a problem hiding this comment.
do we need an explicit apply here?
| SbtBuildTool.apply("", userConfig, config), | |
| SbtBuildTool("", userConfig, config), |
metals/src/main/scala/scala/meta/internal/builds/PantsBuildTool.scala
Outdated
Show resolved
Hide resolved
metals/src/main/scala/scala/meta/internal/builds/PantsDigest.scala
Outdated
Show resolved
Hide resolved
metals/src/main/scala/scala/meta/internal/pantsbuild/BloopPants.scala
Outdated
Show resolved
Hide resolved
metals/src/main/scala/scala/meta/internal/pantsbuild/BloopPants.scala
Outdated
Show resolved
Hide resolved
metals/src/main/scala/scala/meta/internal/pantsbuild/Tarjans.scala
Outdated
Show resolved
Hide resolved
|
|
||
| private def preInitialized = { | ||
| val pants_targets_config = | ||
| s""" |
There was a problem hiding this comment.
why not just """{ "pants-targets": "src::" } """ ?
| workspace: AbsolutePath | ||
| ): Option[String] = { | ||
| new PantsDigest( | ||
| () => UserConfiguration(pantsTargets = Option(s"src::")) |
There was a problem hiding this comment.
why s"src::" is an interpolation ?
olafurpg
left a comment
There was a problem hiding this comment.
Thank you for the detailed review @tgodzik and @marek1840. A lot of great comments 🙏
| } | ||
|
|
||
| object BuildTools { | ||
| def all: List[BuildTool] = { |
There was a problem hiding this comment.
The other all method doesn't work the same way. In the tests we enumerated the whole list in several different places. I have moved this method into the BuildTools class and converted this into a default method to get a default BuildTools instance, which we can use for testing purposes.
| val userConfig: () => UserConfiguration = () => UserConfiguration() | ||
| val config = MetalsServerConfig.default | ||
| List( | ||
| SbtBuildTool.apply("", userConfig, config), |
metals/src/main/scala/scala/meta/internal/builds/PantsBuildTool.scala
Outdated
Show resolved
Hide resolved
metals/src/main/scala/scala/meta/internal/pantsbuild/BloopPants.scala
Outdated
Show resolved
Hide resolved
metals/src/main/scala/scala/meta/internal/pantsbuild/BloopPants.scala
Outdated
Show resolved
Hide resolved
metals/src/main/scala/scala/meta/internal/pantsbuild/BloopPants.scala
Outdated
Show resolved
Hide resolved
metals/src/main/scala/scala/meta/internal/pantsbuild/Tarjans.scala
Outdated
Show resolved
Hide resolved
6e2adf8 to
fb029ef
Compare
### Problem We would like to support the Metals VSCode plugin -- see associated PR at scalameta/metals#935. Pants currently does not have any fields which specifically provide the scala version and the compiler jars, so that PR currently iterates over the `libraries` keys, which doesn't provide all the scala compiler jars. ### Solution Add a `scala_platform` key to the `./pants export` output containing the `scala_version` and `compiler_classpath`: ```json { ..., "scala_platform": { "scala_version": "2.12", "compiler_classpath": [ "/path/to/scala-compiler-2.12.8.jar", "/path/to/scala-library-2.12.8.jar", ... ] } ``` ### Result Using the branch at https://github.com/cosmicexplorer/language-server/tree/sohamr/add-pants-build-tool (which is based off of scalameta/metals#935), I have been able to show that metals can extract the `scala_platform` from the json!
1ca17fa to
e970d01
Compare
jvican
left a comment
There was a problem hiding this comment.
This looks really cool. Good job! 💯
metals/src/main/scala/scala/meta/internal/metals/FileWatcher.scala
Outdated
Show resolved
Hide resolved
metals/src/main/scala/scala/meta/internal/metals/MetalsEnrichments.scala
Outdated
Show resolved
Hide resolved
e970d01 to
d85a0b0
Compare
4302e71 to
40b318a
Compare
|
A lot of progress in the latest commits! This PR is not yet ready for review but I think it's getting close to being feature complete
To fix the CI errors, we're blocked by #1030 |
This is Awesome! Thanks a lot, Olaf!!! |
cb01c98 to
b7d6f0a
Compare
ce438d8 to
5b1d179
Compare
5b1d179 to
d2435af
Compare
Previously, the `./pants export` output did not include information whether a target has enabled strict dependencies or not. This information is needed in order to faithfully reproduce the compilation outside of Pants, for example with the IntelliJ compiler or with Bloop see (scalameta/metals#935).
1a61ae3 to
f693969
Compare
|
This PR is still not ready for review. The latest batch of changes makes several improvements
These changes fix several compile errors that I hit on in the wild. |
Previously, the `./pants export` output did not include information whether a target has enabled strict dependencies or not. This information is needed in order to faithfully reproduce the compilation outside of Pants, for example with the IntelliJ compiler or with Bloop see (scalameta/metals#935).
Previously, the `./pants export` output did not include information whether a target has enabled strict dependencies or not. This information is needed in order to faithfully reproduce the compilation outside of Pants, for example with the IntelliJ compiler or with Bloop see (scalameta/metals#935).
ae29e8d to
ee87336
Compare
| errors ++= symbolPrefixes.keys.flatMap { sym => | ||
| Symbol.validated(sym).left.toOption | ||
| } | ||
| val worksheetScreenWidth = |
There was a problem hiding this comment.
These changes got introduces in this PR to enforce consistency in UserConfiguration. Every field should be configurable
| @@ -22,7 +24,16 @@ object DetectionSuite extends BaseSuite { | |||
| } | |||
| def checkSbt(name: String, layout: String, isTrue: Boolean = true): Unit = { | |||
c4d8aeb to
b816d21
Compare
Previously, it was not possible to use Metals with the Pants build tool. This commit implements all the functionality to use Metals with Pants, along with a lot smaller fixes that are necessary to work with Metals in a larger workspace (encountered while developing the Pants integration). This is commit is large because it was difficult to rebase the individual commits on top of the Metals master.
2287c8c to
35b4286
Compare
|
I believe this PR is finally ready for merge! The Pants->Bloop conversion is still not perfect, I expect it to evolve as we iterate on the Pants integration and gain more experience with it. |
tgodzik
left a comment
There was a problem hiding this comment.
Looks good already! I just had a couple of questions really.
| private def pantsTargets(): List[String] = | ||
| userConfig().pantsTargets match { | ||
| case None => Nil | ||
| case Some(target) => target.split(" ").toList |
There was a problem hiding this comment.
Targets will be separate by a space? Will that be consistent with the VS Code representation? It would be nice if it was represented as an array inside the settings.
| BloopPants | ||
| .pantsOwnerOf( | ||
| workspace, | ||
| source.resolveSibling(_.stripSuffix(".sc") + ".scala") |
There was a problem hiding this comment.
Not sure I understand the logic here? Why for .sc files we are looking for the same but with .scala?
There was a problem hiding this comment.
I added a comment
// Convert Scala script name into a `*.scala` filename to find out what
// target it should belong to. Pants doesn't support Scala scripts so
// using the script name unchanged would return no targets.
| def bloopInstall( | ||
| workspace: AbsolutePath, | ||
| languageClient: MetalsLanguageClient, | ||
| systemProcess: List[String] => Future[BloopInstallResult] |
There was a problem hiding this comment.
systemProcess doesn't seem to be used anywhere here
There was a problem hiding this comment.
Renamed
// Not used: we call metals/slowTask directly
_unused: List[String] => Future[BloopInstallResult]
| } { | ||
| isOk &= Digest.digestFile(buildFile, digest) | ||
| } | ||
| isOk |
There was a problem hiding this comment.
We could do this with forAll instead of a var. yield Digest.digestFile(buildFile, digest) and then results.forAll(_)
| outputTerminated = true | ||
| server.consume(message) | ||
|
|
||
| case null => |
There was a problem hiding this comment.
Can we pack it into an Option like:
{ message =>
Option(message) match {
case _ if cancelled.get() =>
// ignore
case Some(OutputNotification()) if outputTerminated =>
// ignore. When restarting, the output keeps getting printed for a short while after the
// output window gets refreshed resulting in stale messages being printed on top, before
// any actual logs from the restarted process
case Some(msg) =>
client.consume(msg)
case None =>
}
}There was a problem hiding this comment.
This change is unrelated to the PR, let's refactor it separately. I just wanted to fix a NPE
| | --[no-]cache (default=false) | ||
| | If enabled, cache the result from `./pants export` | ||
| | --[no-]compile (default=$isCompile) | ||
| | If ena, do not run `./pants compile` |
| val sourceStr = Value.Str(source.toString()) | ||
| if (!sources.contains(sourceStr)) { | ||
| sources += sourceStr | ||
| jsonFile.writeText(ujson.write(json, indent = 4)) |
There was a problem hiding this comment.
I believe Jorge was strongly opposed to anything than bloop modifying the config file while it's working 🤔
There was a problem hiding this comment.
The config file can be modified while it's working, but it must be updated atomically. I don't remember being strongly opposed to this, but I do remember mentioning that tools that don't own these configuration files should never update them, that's something that only the original build tool can do. Let me know if this clarifies your thinking @tgodzik 😄
There was a problem hiding this comment.
@jvican Thanks! That makes sense, I must have misunderstood previously.
There was a problem hiding this comment.
Sounds great! I will follow up with a PR to make our file write operations atomic.
We use _root_.bloop.config.write(json, out) to write most of the JSON files in the Pants export. This step here happens only when the user creates a new file and we need to re-expand the file globs.
| s"export" | ||
| ) ++ args.targets | ||
| val shortName = "pants export-classpath export" | ||
| SystemProcess.run( |
There was a problem hiding this comment.
Shouldn't we use the process runner from the bloopInstall function?
There was a problem hiding this comment.
No, since that one launches metals/slowTask and we call several system processes in this step.
| s"export" | ||
| ) ++ args.targets | ||
| val shortName = "pants export-classpath export" | ||
| SystemProcess.run( |
There was a problem hiding this comment.
No, since that one launches metals/slowTask and we call several system processes in this step.
| BloopPants | ||
| .pantsOwnerOf( | ||
| workspace, | ||
| source.resolveSibling(_.stripSuffix(".sc") + ".scala") |
There was a problem hiding this comment.
I added a comment
// Convert Scala script name into a `*.scala` filename to find out what
// target it should belong to. Pants doesn't support Scala scripts so
// using the script name unchanged would return no targets.
| def bloopInstall( | ||
| workspace: AbsolutePath, | ||
| languageClient: MetalsLanguageClient, | ||
| systemProcess: List[String] => Future[BloopInstallResult] |
There was a problem hiding this comment.
Renamed
// Not used: we call metals/slowTask directly
_unused: List[String] => Future[BloopInstallResult]
| } { | ||
| isOk &= Digest.digestFile(buildFile, digest) | ||
| } | ||
| isOk |
| outputTerminated = true | ||
| server.consume(message) | ||
|
|
||
| case null => |
There was a problem hiding this comment.
This change is unrelated to the PR, let's refactor it separately. I just wanted to fix a NPE
tgodzik
left a comment
There was a problem hiding this comment.
Looks good! Excited to have 5! build tools supported out of the box 😄
|
🎉 |
Following up on scalameta#935 (comment)
Previously, the `./pants export` output did not include information whether a target has enabled strict dependencies or not. This information is needed in order to faithfully reproduce the compilation outside of Pants, for example with the IntelliJ compiler or with Bloop see (scalameta/metals#935).
Previously, Metals didn't work with the Pants build tool https://www.pantsbuild.org/. This PR adds support for Pants that works similarly to the build tool support with sbt, Gradle, Maven and Mill.
pants-targetssetting to specify what parts of their Pants build should be exported to Metals/Bloop.BUILDfiles change, Metals will prompt to the user to "import changes"This PR is still a WIP. There are several issues related to handling large number of individual source files that belong to a Pants target.