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

Unify build tool integrations with Metals #942

Merged
merged 23 commits into from
Jul 31, 2019
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
47134dd
Add an additional mechanism to save information about the samenticDB …
tgodzik Jun 25, 2019
9dc0729
Address review feedback:
tgodzik Jul 19, 2019
f8bcfb2
Remove unnecessary import
jvican Jul 24, 2019
184e306
Make mostly stylistic changes to the bsp metals test
jvican Jul 24, 2019
380a028
Make test suite less verbose
jvican Jul 24, 2019
01f760d
Remove displayWarningToUser when version is unsupported
jvican Jul 24, 2019
d1d63bb
Add changes to resolution and transformation logic
jvican Jul 25, 2019
32351b4
Change read and write of workspace settings
jvican Jul 28, 2019
5020ef6
Add `LoadedProject` abstraction
jvican Jul 29, 2019
f1fa503
Add incremental build load mechanism
jvican Jul 30, 2019
7ff9cb3
Remove loaded build abstraction
jvican Jul 30, 2019
0fca102
Remove `changedSettings` from `reattemptConfiguration`
jvican Jul 30, 2019
8acbe60
Throw exceptions when writing workspace settings file
jvican Jul 30, 2019
e3241d1
Add an additional setting for workspace root
tgodzik Jul 25, 2019
194cc16
Rename `workspaceRoot` to `workspaceDir`
jvican Jul 30, 2019
27ef85a
Use real workspace directory to enable semanticdb
jvican Jul 30, 2019
745b154
Remove unnecessary `Project.pprint`
jvican Jul 30, 2019
2972653
Rename misnomer in `pickSettingsForReload` method
jvican Jul 30, 2019
eba47e1
Use workspace dir from project in tests too
jvican Jul 30, 2019
eb93389
Fix error computing `reattemptConfiguration`
jvican Jul 30, 2019
30927d7
Return `ForceReload` when new settings are added
jvican Jul 31, 2019
3f0a295
Use `Coeval` to abstract over logic applying semanticdb
jvican Jul 31, 2019
f45a972
Merge branch 'master' into HEAD
jvican Jul 31, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.idea/
bin/.coursier
bin/.scalafmt*
.vscode/

# Required because these are the proxies for the sourcedeps
.bridge/
Expand Down Expand Up @@ -33,4 +34,4 @@ node_modules/
package-lock.json
.metals/
*.lock
benchmark-bridge/corpus/
benchmark-bridge/corpus/
6 changes: 5 additions & 1 deletion backend/src/main/scala/bloop/BloopComponentsLock.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import java.util.concurrent.Callable

import xsbti.GlobalLock

object BloopComponentsLock extends GlobalLock {
sealed trait ComponentLock extends GlobalLock {
override def apply[T](file: File, callable: Callable[T]): T = synchronized { callable.call() }
}

object BloopComponentsLock extends ComponentLock
tgodzik marked this conversation as resolved.
Show resolved Hide resolved

object SemanticDBCacheLock extends ComponentLock
3 changes: 1 addition & 2 deletions backend/src/main/scala/bloop/CompilerCache.scala
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,7 @@ final class CompilerCache(
Some(Paths.getCacheDirectory("bridge-cache").toFile),
DependencyResolution.getEngine(userResolvers),
bridgeSources,
retrieveDir.toFile,
logger
retrieveDir.toFile
)
}
}
Expand Down
85 changes: 40 additions & 45 deletions backend/src/main/scala/bloop/DependencyResolution.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import bloop.io.AbsolutePath

import sbt.librarymanagement._
import sbt.librarymanagement.ivy._
import coursier.core.Repository
import coursier.error.CoursierError

object DependencyResolution {
private final val BloopResolvers =
Expand All @@ -15,72 +17,65 @@ object DependencyResolution {
IvyDependencyResolution(configuration)
}

import java.io.File
import coursier.util.{Gather, Task}
import coursier.cache.{Cache, ArtifactError}
import coursier.{
Dependency,
Fetch,
MavenRepository,
Module,
Repository,
Resolution,
LocalRepositories,
ResolutionProcess
/**
* Resolve the specified module and get all the files. By default, the local ivy
* repository and Maven Central are included in resolution. This resolution throws
* in case there is an error.
*
* @param organization The module's organization.
* @param module The module's name.
* @param version The module's version.
* @param logger A logger that receives messages about resolution.
* @param additionalRepositories Additional repositories to include in resolition.
* @return All the resolved files.
*/
def resolve(
organization: String,
module: String,
version: String,
logger: Logger,
additionalRepos: Seq[Repository] = Nil
)(implicit ec: scala.concurrent.ExecutionContext): Array[AbsolutePath] = {
resolveWithErrors(organization, module, version, logger, additionalRepos) match {
case Right(paths) => paths
case Left(error) => throw error
}
}

/**
* Resolve the specified module and get all the files. By default, the local ivy
* repository and Maven Central are included in resolution.
* repository and Maven Central are included in resolution. This resolution is
* pure and returns either some errors or some resolved jars.
*
* @param organization The module's organization.
* @param module The module's name.
* @param version The module's version.
* @param logger A logger that receives messages about resolution.
* @param additionalRepositories Additional repositories to include in resolition.
* @return All the files that compose the module and that could be found.
* @return Either a coursier error or all the resolved files.
*/
def resolve(
def resolveWithErrors(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated this logic because Alex recommended me to update to the latest coursier API.

organization: String,
module: String,
version: String,
logger: Logger,
additionalRepositories: Seq[Repository] = Nil
)(implicit ec: scala.concurrent.ExecutionContext): Array[AbsolutePath] = {
logger.debug(s"Resolving $organization:$module:$version")(DebugFilter.Compilation)
)(implicit ec: scala.concurrent.ExecutionContext): Either[CoursierError, Array[AbsolutePath]] = {
import coursier._
logger.debug(s"Resolving $organization:$module:$version")(DebugFilter.All)
val org = coursier.Organization(organization)
val moduleName = coursier.ModuleName(module)
val dependency = Dependency(Module(org, moduleName), version)
val start = Resolution(List(dependency))
val repositories = {
val baseRepositories = Seq(
LocalRepositories.ivy2Local,
MavenRepository("https://repo1.maven.org/maven2"),
MavenRepository("https://dl.bintray.com/scalacenter/releases")
)
baseRepositories ++ additionalRepositories
}
val fetch = ResolutionProcess.fetch(repositories, Cache.default.fetch)
val resolution = start.process.run(fetch).unsafeRun()
val localArtifacts: Seq[(Boolean, Either[ArtifactError, File])] = {
Gather[Task]
.gather(resolution.artifacts().map { artifact =>
Cache.default.file(artifact).run.map(artifact.optional -> _)
})
.unsafeRun()(ec)
var fetch = Fetch()
.addDependencies(dependency)
.addRepositories(Repositories.bintray("scalacenter", "releases"))
for (repository <- additionalRepositories) {
fetch.addRepositories(repository)
}

val fileErrors = localArtifacts.collect {
case (isOptional, Left(error)) if !isOptional || !error.notFound => error
}
if (fileErrors.isEmpty) {
localArtifacts.collect { case (_, Right(f)) => AbsolutePath(f.toPath) }.toArray
} else {
val moduleInfo = s"$organization:$module:$version"
val prettyFileErrors = fileErrors.map(_.describe).mkString(System.lineSeparator)
sys.error(
s"Resolution of module $moduleInfo failed with:${System.lineSeparator}${prettyFileErrors}"
)
try Right(fetch.run().map(f => AbsolutePath(f.toPath)).toArray)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe:

Try(fetch.run().map(f => AbsolutePath(f.toPath)).toArray).toEither

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try catches everything, I now that coursier can only throw CoursierError so the try-catch only catches that

catch {
case error: CoursierError => Left(error)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,8 @@ import xsbti.ArtifactInfo.SbtOrganization
class ZincComponentManager(
globalLock: GlobalLock,
provider: ComponentProvider,
secondaryCacheDir: Option[File],
log0: Logger,
secondaryCacheDir: Option[File]
) {
val log = new FullLogger(log0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to see this leaving 👍


/** Get all of the files for component 'id', throwing an exception if no files exist for the component. */
def files(id: String)(ifMissing: IfMissing): Iterable[File] = {
Expand Down
5 changes: 2 additions & 3 deletions backend/src/main/scala/sbt/internal/inc/ZincLmUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,11 @@ object ZincLmUtil {
secondaryCacheDir: Option[File],
dependencyResolution: DependencyResolution,
compilerBridgeSource: ModuleID,
scalaJarsTarget: File,
log: Logger
scalaJarsTarget: File
): AnalyzingCompiler = {
val compilerBridgeProvider = ZincComponentCompiler.interfaceProvider(
compilerBridgeSource,
new ZincComponentManager(globalLock, componentProvider, secondaryCacheDir, log),
new ZincComponentManager(globalLock, componentProvider, secondaryCacheDir),
dependencyResolution,
scalaJarsTarget
)
Expand Down
7 changes: 5 additions & 2 deletions config/src/main/scala/bloop/config/Config.scala
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ object Config {
case class Project(
name: String,
directory: Path,
workspaceDir: Option[Path],
sources: List[Path],
dependencies: List[String],
classpath: List[Path],
Expand All @@ -232,7 +233,7 @@ object Config {

object Project {
// FORMAT: OFF
private[bloop] val empty: Project = Project("", emptyPath, List(), List(), List(), emptyPath, emptyPath, None, None, None, None, None, None, None)
private[bloop] val empty: Project = Project("", emptyPath, None, List(), List(), List(), emptyPath, emptyPath, None, None, None, None, None, None, None)
// FORMAT: ON

def analysisFileName(projectName: String) = s"$projectName-analysis.bin"
Expand Down Expand Up @@ -271,6 +272,7 @@ object Config {
val project = Project(
"dummy-project",
workingDirectory,
Some(workingDirectory),
List(sourceFile),
List("dummy-2"),
List(scalaLibraryJar),
Expand All @@ -286,7 +288,8 @@ object Config {
List(),
Some(outAnalysisFile),
Some(CompileSetup.empty)
)),
)
),
Some(Java(List("-version"))),
Some(Sbt("1.1.0", Nil)),
Some(Test(List(), TestOptions(Nil, Nil))),
Expand Down
17 changes: 9 additions & 8 deletions frontend/src/it/scala/bloop/CommunityBuild.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import scala.concurrent.Await
import scala.concurrent.duration.Duration
import bloop.cli.{Commands, ExitStatus}
import bloop.config.Config
import bloop.data.{Origin, Project}
import bloop.data.{Origin, Project, LoadedProject}
import bloop.engine.{
Action,
Build,
Expand Down Expand Up @@ -111,19 +111,20 @@ abstract class CommunityBuild(val buildpressHomeDir: AbsolutePath) {
val logger = BloopLogger.default("community-build-logger")
val initialState = loadStateForBuild(buildBaseDir.resolve(".bloop"), logger)
val blacklistedProjects = readBlacklistFile(buildBaseDir.resolve("blacklist.buildpress"))
val allProjectsInBuild =
initialState.build.projects.filterNot(p => blacklistedProjects.contains(p.name))
val allProjectsInBuild = initialState.build.loadedProjects
.filterNot(lp => blacklistedProjects.contains(lp.project.name))

val rootProjectName = "bloop-test-root"
val dummyExistingBaseDir = buildBaseDir.resolve("project")
val dummyClassesDir = dummyExistingBaseDir.resolve("target")
val origin = Origin(buildBaseDir, FileTime.fromMillis(0), scala.util.Random.nextInt())
val origin = Origin(buildBaseDir, FileTime.fromMillis(0), 0L, scala.util.Random.nextInt())
val analysisOut = dummyClassesDir.resolve(Config.Project.analysisFileName(rootProjectName))
val rootProject = Project(
name = rootProjectName,
baseDirectory = dummyExistingBaseDir,
dependencies = allProjectsInBuild.map(_.name),
scalaInstance = allProjectsInBuild.head.scalaInstance,
workspaceDirectory = Some(buildBaseDir),
dependencies = allProjectsInBuild.map(_.project.name),
scalaInstance = allProjectsInBuild.head.project.scalaInstance,
rawClasspath = Nil,
resources = Nil,
compileSetup = Config.CompileSetup.empty,
Expand All @@ -141,8 +142,8 @@ abstract class CommunityBuild(val buildpressHomeDir: AbsolutePath) {
origin = origin
)

val newProjects = rootProject :: allProjectsInBuild
val state = initialState.copy(build = initialState.build.copy(projects = newProjects))
val newLoaded = LoadedProject.RawProject(rootProject) :: allProjectsInBuild
val state = initialState.copy(build = initialState.build.copy(loadedProjects = newLoaded))
val allReachable = Dag.dfs(state.build.getDagFor(rootProject))
val reachable = allReachable.filter(_ != rootProject)
val cleanAction = Run(Commands.Clean(reachable.map(_.name)), Exit(ExitStatus.Ok))
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/main/scala/bloop/Bloop.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ object Bloop extends CaseApp[CliOptions] {
)
logger.warn("Please refer to our documentation for more information.")
val client = ClientInfo.CliClientInfo("bloop-single-app", () => true)
val projects = BuildLoader.loadSynchronously(configDirectory, logger)
val build = Build(configDirectory, projects)
val loadedProjects = BuildLoader.loadSynchronously(configDirectory, logger)
val build = Build(configDirectory, loadedProjects)
val state = State(build, client, NoPool, options.common, logger)
run(state, options)
}
Expand Down Expand Up @@ -71,7 +71,7 @@ object Bloop extends CaseApp[CliOptions] {
run(waitForState(action, Interpreter.execute(action, Task.now(state))), options)

case Array("clean") =>
val allProjects = state.build.projects.map(_.name)
val allProjects = state.build.loadedProjects.map(_.project.name)
val action = Run(Commands.Clean(allProjects), Exit(ExitStatus.Ok))
run(waitForState(action, Interpreter.execute(action, Task.now(state))), options)

Expand Down
14 changes: 11 additions & 3 deletions frontend/src/main/scala/bloop/bsp/BloopBspDefinitions.scala
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
package bloop.bsp

import io.circe._
import io.circe.derivation._
import ch.epfl.scala.bsp.Uri

object BloopBspDefinitions {
final case class BloopExtraBuildParams(
clientClassesRootDir: Option[Uri]
clientClassesRootDir: Option[Uri],
semanticdbVersion: Option[String],
supportedScalaVersions: List[String]
)

object BloopExtraBuildParams {
val empty = BloopExtraBuildParams(
clientClassesRootDir = None,
semanticdbVersion = None,
supportedScalaVersions = Nil
)

import io.circe.{RootEncoder, Decoder}
import io.circe.derivation._
val encoder: RootEncoder[BloopExtraBuildParams] = deriveEncoder
val decoder: Decoder[BloopExtraBuildParams] = deriveDecoder
}
Expand Down
Loading