-
Notifications
You must be signed in to change notification settings - Fork 340
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
Add basic support for the Pants build tool #935
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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, "") | ||
} | ||
// |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove?
else false | ||
} | ||
} | ||
|
||
object BuildTools { | ||
def all: List[BuildTool] = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need an explicit apply
here?
SbtBuildTool.apply("", userConfig, config), | |
SbtBuildTool("", userConfig, config), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, removed.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not just """{ "pants-targets": "src::" } """
?
workspace: AbsolutePath | ||
): Option[String] = { | ||
new PantsDigest( | ||
() => UserConfiguration(pantsTargets = Option(s"src::")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why s"src::"
is an interpolation ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the detailed review @tgodzik and @marek1840. A lot of great comments 🙏
else false | ||
} | ||
} | ||
|
||
object BuildTools { | ||
def all: List[BuildTool] = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, removed.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I broke up the massive mono-commit into smaller commits and opened a separate PR adding all the non-Pants related changes #1145
We should get that PR merged first before continuing here.
@@ -168,6 +186,14 @@ object UserConfiguration { | |||
errors ++= symbolPrefixes.keys.flatMap { sym => | |||
Symbol.validated(sym).left.toOption | |||
} | |||
val worksheetScreenWidth = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
systemProcess doesn't seem to be used anywhere here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could do this with forAll
instead of a var. yield Digest.digestFile(buildFile, digest)
and then results.forAll(_)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -45,7 +45,8 @@ private[debug] final class DebugProxy( | |||
exitStatus.trySuccess(Restarted) | |||
outputTerminated = true | |||
server.consume(message) | |||
|
|||
case null => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ena
? Looks like a typo
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jvican Thanks! That makes sense, I must have misunderstood previously.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we use the process runner from the bloopInstall function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, since that one launches metals/slowTask
and we call several system processes in this step.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tgodzik Thank you for the review!
s"export" | ||
) ++ args.targets | ||
val shortName = "pants export-classpath export" | ||
SystemProcess.run( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -45,7 +45,8 @@ private[debug] final class DebugProxy( | |||
exitStatus.trySuccess(Restarted) | |||
outputTerminated = true | |||
server.consume(message) | |||
|
|||
case null => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change is unrelated to the PR, let's refactor it separately. I just wanted to fix a NPE
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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-targets
setting to specify what parts of their Pants build should be exported to Metals/Bloop.BUILD
files 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.