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

Clean all projects by default #655

Merged
merged 3 commits into from
Sep 23, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion frontend/src/main/scala/bloop/cli/Commands.scala
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ object Commands {

case class Clean(
@ExtraName("p")
@HelpMessage("The projects to clean.")
@HelpMessage("The projects to clean (you can specify multiple). By default, all.")
project: List[String] = Nil,
@ExtraName("propagate")
@HelpMessage("Run clean for the project's dependencies. By default, false.")
Expand Down
12 changes: 8 additions & 4 deletions frontend/src/main/scala/bloop/engine/Interpreter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -293,10 +293,14 @@ object Interpreter {
}

private def clean(cmd: Commands.Clean, state: State): Task[State] = {
val (projects, missing) = lookupProjects(cmd.project, state)
if (missing.isEmpty)
Tasks.clean(state, projects, cmd.includeDependencies).map(_.mergeStatus(ExitStatus.Ok))
else Task.now(reportMissing(missing, state))
if (cmd.project.isEmpty)
Tasks.clean(state, state.build.projects, cmd.includeDependencies).map(_.mergeStatus(ExitStatus.Ok))
else {
val (projects, missing) = lookupProjects(cmd.project, state)
if (missing.isEmpty)
Tasks.clean(state, projects, cmd.includeDependencies).map(_.mergeStatus(ExitStatus.Ok))
else Task.now(reportMissing(missing, state))
}
}

private def linkWithScalaJs(
Expand Down
17 changes: 9 additions & 8 deletions frontend/src/main/scala/bloop/engine/caches/ResultsCache.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,10 @@ import scala.concurrent.duration.Duration
*
* @param all The map of projects to latest compilation results.
* @param successful The map of all projects to latest successful compilation results.
* @param logger A logger.
*/
final class ResultsCache private (
all: Map[Project, Compiler.Result],
successful: Map[Project, PreviousResult],
logger: Logger
successful: Map[Project, PreviousResult]
) {

/** Returns the last succesful result if present, empty otherwise. */
Expand All @@ -58,17 +56,17 @@ final class ResultsCache private (
def cleanSuccessful(projects: List[Project]): ResultsCache = {
// Remove all the successful results from the cache.
val newSuccessful = successful.filterKeys(p => !projects.contains(p))
new ResultsCache(all, newSuccessful, logger)
new ResultsCache(all, newSuccessful)
}

def addResult(project: Project, result: Compiler.Result): ResultsCache = {
val newAll = all + (project -> result)
result match {
case s: Compiler.Result.Success =>
new ResultsCache(newAll, successful + (project -> s.previous), logger)
new ResultsCache(newAll, successful + (project -> s.previous))
case Compiler.Result.Empty =>
new ResultsCache(newAll, successful + (project -> ResultsCache.EmptyResult), logger)
case r => new ResultsCache(newAll, successful, logger)
new ResultsCache(newAll, successful + (project -> ResultsCache.EmptyResult))
case r => new ResultsCache(newAll, successful)
}
}

Expand All @@ -90,6 +88,9 @@ object ResultsCache {
private[ResultsCache] final val EmptyResult: PreviousResult =
PreviousResult.of(Optional.empty[CompileAnalysis], Optional.empty[MiniSetup])

private[bloop] val forTests: ResultsCache =
new ResultsCache(Map.empty, Map.empty)

def load(build: Build, cwd: AbsolutePath, logger: Logger): ResultsCache = {
val handle = loadAsync(build, cwd, logger).runAsync(ExecutionContext.ioScheduler)
Await.result(handle, Duration.Inf)
Expand Down Expand Up @@ -126,7 +127,7 @@ object ResultsCache {

val all = build.projects.map(p => fetchPreviousResult(p).map(r => p -> r))
Task.gatherUnordered(all).executeOn(ExecutionContext.ioScheduler).map { projectResults =>
val cache = new ResultsCache(Map.empty, Map.empty, logger)
val cache = new ResultsCache(Map.empty, Map.empty)
cache.addResults(projectResults)
}
}
Expand Down
129 changes: 129 additions & 0 deletions frontend/src/test/scala/bloop/tasks/CleanTaskSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package bloop.tasks

import bloop.Compiler
import bloop.cli.{Commands, ExitStatus}
import bloop.data.Project
import bloop.engine.{Build, Run, State}
import bloop.engine.caches.ResultsCache
import bloop.exec.JavaEnv
import bloop.io.AbsolutePath
import bloop.logging.BloopLogger
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.experimental.categories.Category

@Category(Array(classOf[bloop.FastTests]))
class CleanTaskSpec {

private def dummyProject(
buildPath: AbsolutePath,
name: String,
dependencies: Set[String]
): Project =
TestUtil.makeProject(
buildPath.underlying,
name,
sources = Map.empty,
dependencies = dependencies,
scalaInstance = None,
javaEnv = JavaEnv.default
)

/**
* Executes the given `command` and checks if it cleaned `expected` projects.
*
* @param projectsWithDeps The projects contained in this build along with their dependencies.
* @param command The command to execute.
* @param expectedProjects The projects expected to be cleaned by `command`.
* @param expectedStatus The exit status expected to be set by `command`.
*/
private def cleanAndCheck(
projectsWithDeps: Set[(String, Set[String])],
command: Commands.Clean,
expectedProjects: Set[String],
expectedStatus: ExitStatus = ExitStatus.Ok
): Unit = {
TestUtil.withTemporaryDirectory { temp =>
val buildPath = AbsolutePath(temp)
val logger = BloopLogger.default("test-logger")
val projects = projectsWithDeps.map {
case (project, deps) => dummyProject(buildPath, project, deps)
}
val build = Build(buildPath, projects.toList)
val results = projects.foldLeft(ResultsCache.forTests) { (cache, project) =>
cache.addResult(project, Compiler.Result.Empty)
}
val state = State.forTests(build, CompilationHelpers.getCompilerCache(logger), logger)
.copy(results = results)

val cleanState = TestUtil.blockingExecute(Run(command), state)

val projectsLeft = cleanState.results.allSuccessful.map {
case (project, _) => project.name
}.toSet
val projectsCleaned = projectsWithDeps.map(_._1).toSet -- projectsLeft
assertEquals(expectedProjects, projectsCleaned)
assertEquals(expectedStatus, cleanState.status)
}
}

@Test
def cleansSingleProject: Unit =
cleanAndCheck(
Set(
"foo" -> Set("bar"),
"bar" -> Set.empty,
"baz" -> Set.empty
),
Commands.Clean(List("foo")),
Set("foo")
)

@Test
def cleansMultipleProjects: Unit =
cleanAndCheck(
Set(
"foo" -> Set.empty,
"bar" -> Set.empty,
"baz" -> Set.empty
),
Commands.Clean(List("foo", "bar")),
Set("foo", "bar")
)

@Test
def cleansDependentProjects: Unit =
cleanAndCheck(
Set(
"foo" -> Set("bar"),
"bar" -> Set.empty,
"baz" -> Set.empty
),
Commands.Clean(List("foo"), includeDependencies = true),
Set("foo", "bar")
)

@Test
def cleansAllProjectsByDefault: Unit =
cleanAndCheck(
Set(
"foo" -> Set.empty,
"bar" -> Set.empty
),
Commands.Clean(Nil),
Set("foo", "bar")
)

@Test
def errorsOnMissingProjects: Unit =
cleanAndCheck(
Set(
"foo" -> Set("bar"),
"bar" -> Set.empty,
),
Commands.Clean(List("baz")),
Set(),
ExitStatus.merge(ExitStatus.UnexpectedError, ExitStatus.InvalidCommandLineOption)
)

}
12 changes: 5 additions & 7 deletions frontend/src/test/scala/bloop/tasks/TestUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ object TestUtil {
// Check that this is a clean compile!
val projects = state.build.projects
assert(projects.forall(p => noPreviousAnalysis(p, state)))
val project = getProject(rootProjectName, state)
val action = Run(Commands.Compile(rootProjectName, incremental = true))
val compiledState = TestUtil.blockingExecute(action, state)
afterCompile(compiledState)
Expand Down Expand Up @@ -206,10 +205,8 @@ object TestUtil {
def runAndCheck(state: State, cmd: Commands.CompilingCommand)(
check: List[(String, String)] => Unit): Unit = {
val recordingLogger = new RecordingLogger
val recordingStream = ProcessLogger.toOutputStream(recordingLogger.info _)
val commonOptions = state.commonOptions.copy(env = runAndTestProperties)
val recordingState = state.copy(logger = recordingLogger).copy(commonOptions = commonOptions)
val project = getProject(cmd.project, recordingState)
TestUtil.blockingExecute(Run(cmd), recordingState)
check(recordingLogger.getMessages)
}
Expand All @@ -225,7 +222,7 @@ object TestUtil {
val projects = projectStructures.map {
case (name, sources) =>
val projectDependencies = dependencies.getOrElse(name, Set.empty)
makeProject(temp, name, sources, projectDependencies, scalaInstance, javaEnv, order)
makeProject(temp, name, sources, projectDependencies, Some(scalaInstance), javaEnv, order)
}
val logger = BloopLogger.default(temp.toString)
val build = Build(AbsolutePath(temp), projects.toList)
Expand All @@ -248,7 +245,7 @@ object TestUtil {
name: String,
sources: Map[String, String],
dependencies: Set[String],
scalaInstance: ScalaInstance,
scalaInstance: Option[ScalaInstance],
javaEnv: JavaEnv,
compileOrder: CompileOrder = Config.Mixed
): Project = {
Expand All @@ -260,14 +257,15 @@ object TestUtil {

val target = classesDir(baseDir, name)
val depsTargets = (dependencies.map(classesDir(baseDir, _))).map(AbsolutePath.apply).toList
val classpath = depsTargets ++ scalaInstance.allJars.map(AbsolutePath.apply)
val allJars = scalaInstance.map(_.allJars.map(AbsolutePath.apply)).getOrElse(Array.empty)
val classpath = depsTargets ++ allJars
val sourceDirectories = List(AbsolutePath(srcs))
writeSources(srcs, sources)
Project(
name = name,
baseDirectory = AbsolutePath(baseDirectory),
dependencies = dependencies.toList,
scalaInstance = Some(scalaInstance),
scalaInstance = scalaInstance,
rawClasspath = classpath,
compileSetup = Config.CompileSetup.empty.copy(order = compileOrder),
classesDir = AbsolutePath(target),
Expand Down
4 changes: 3 additions & 1 deletion website/content/docs/commands-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ toc = true

<dl>
<dt><code>--project</code> or <code>-p</code> (type: <code>string*</code>)</dt>
<dd><p>The projects to clean.</p></dd>
<dd><p>The projects to clean (you can specify multiple). By default, all.</p></dd>
<dt><code>--include-dependencies</code> or <code>--propagate</code> (type: <code>bool</code>)</dt>
<dd><p>Run clean for the project's dependencies. By default, false.</p></dd>
<dt><code>--config-dir</code> or <code>-c</code> (type: <code>path?</code>)</dt>
Expand All @@ -91,7 +91,9 @@ toc = true

### Examples

* <samp>bloop clean</samp>
* <samp>bloop clean foobar</samp>
* <samp>bloop clean foobar baz</samp>
* <samp>bloop clean foobar --propagate</samp>

## `bloop compile`
Expand Down