Skip to content

Commit

Permalink
Merge branch 'master' of github.com:lihaoyi/mill
Browse files Browse the repository at this point in the history
  • Loading branch information
lihaoyi committed Nov 2, 2018
2 parents cb0a0d5 + 7b4ced6 commit bbe2ce1
Show file tree
Hide file tree
Showing 23 changed files with 734 additions and 107 deletions.
6 changes: 5 additions & 1 deletion build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,10 @@ object contrib extends MillModule {
}
}

object tut extends MillModule {
def moduleDeps = Seq(scalalib)
def testArgs = Seq("-DMILL_VERSION=" + build.publishVersion()._2)
}
}


Expand Down Expand Up @@ -362,7 +366,7 @@ def launcherScript(shellJvmArgs: Seq[String],
}

object dev extends MillModule{
def moduleDeps = Seq(scalalib, scalajslib, scalanativelib, contrib.scalapblib)
def moduleDeps = Seq(scalalib, scalajslib, scalanativelib, contrib.scalapblib, contrib.tut)

def forkArgs =
(
Expand Down
132 changes: 132 additions & 0 deletions contrib/tut/src/mill/contrib/tut/TutModule.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package mill
package contrib.tut

import ammonite.ops._
import coursier.MavenRepository
import mill.scalalib._
import scala.util.matching.Regex

/**
* Tut is a documentation tool which compiles and evaluates Scala code in documentation files and provides various options for configuring how the results will be displayed in the compiled documentation.
*
* Extending this trait declares a Scala module which compiles markdown, HTML and `.txt` files in the `tut` folder of the module with Tut.
*
* By default the resulting documents are simply placed in the Mill build output folder but they can be placed elsewhere by overriding the [[mill.contrib.tut.TutModule#tutTargetDirectory]] task.
*
* For example:
*
* {{{
* // build.sc
* import mill._, scalalib._, contrib.tut.__
*
* object example extends TutModule {
* def scalaVersion = "2.12.6"
* def tutVersion = "0.6.7"
* }
* }}}
*
* This defines a project with the following layout:
*
* {{{
* build.sc
* example/
* src/
* tut/
* resources/
* }}}
*
* In order to compile documentation we can execute the `tut` task in the module:
*
* {{{
* sh> mill example.tut
* }}}
*/
trait TutModule extends ScalaModule {
/**
* This task determines where documentation files must be placed in order to be compiled with Tut. By default this is the `tut` folder at the root of the module.
*/
def tutSourceDirectory = T.sources { millSourcePath / 'tut }

/**
* A task which determines where the compiled documentation files will be placed. By default this is simply the Mill build's output folder for this task,
* but this can be reconfigured so that documentation goes to the root of the module (e.g. `millSourcePath`) or to a dedicated folder (e.g. `millSourcePath / 'docs`)
*/
def tutTargetDirectory: T[Path] = T { T.ctx().dest }

/**
* A task which determines what classpath is used when compiling documentation. By default this is configured to use the same inputs as the [[mill.contrib.tut.TutModule#runClasspath]],
* except for using [[mill.contrib.tut.TutModule#tutIvyDeps]] rather than the module's [[mill.contrib.tut.TutModule#runIvyDeps]].
*/
def tutClasspath: T[Agg[PathRef]] = T {
// Same as runClasspath but with tut added to ivyDeps from the start
// This prevents duplicate, differently versioned copies of scala-library ending up on the classpath which can happen when resolving separately
transitiveLocalClasspath() ++
resources() ++
localClasspath() ++
unmanagedClasspath() ++
tutIvyDeps()
}

/**
* A task which determines the scalac plugins which will be used when compiling code examples with Tut. The default is to use the [[mill.contrib.tut.TutModule#scalacPluginIvyDeps]] for the module.
*/
def tutScalacPluginIvyDeps: T[Agg[Dep]] = scalacPluginIvyDeps()

/**
* A [[scala.util.matching.Regex]] task which will be used to determine which files should be compiled with tut. The default pattern is as follows: `.*\.(md|markdown|txt|htm|html)`.
*/
def tutNameFilter: T[Regex] = T { """.*\.(md|markdown|txt|htm|html)""".r }

/**
* The scalac options which will be used when compiling code examples with Tut. The default is to use the [[mill.contrib.tut.TutModule#scalacOptions]] for the module,
* but filtering out options which are problematic in the REPL, e.g. `-Xfatal-warnings`, `-Ywarn-unused-imports`.
*/
def tutScalacOptions: T[Seq[String]] =
scalacOptions().filterNot(Set(
"-Ywarn-unused:imports",
"-Ywarn-unused-import",
"-Ywarn-dead-code",
"-Xfatal-warnings"
))

/**
* The version of Tut to use.
*/
def tutVersion: T[String]

/**
* A task which determines how to fetch the Tut jar file and all of the dependencies required to compile documentation for the module and returns the resulting files.
*/
def tutIvyDeps: T[Agg[PathRef]] = T {
Lib.resolveDependencies(
repositories :+ MavenRepository(s"https://dl.bintray.com/tpolecat/maven"),
Lib.depToDependency(_, scalaVersion()),
compileIvyDeps() ++ transitiveIvyDeps() ++ Seq(
ivy"org.tpolecat::tut-core:${tutVersion()}"
)
)
}

/**
* A task which performs the dependency resolution for the scalac plugins to be used with Tut.
*/
def tutPluginJars: T[Agg[PathRef]] = resolveDeps(tutScalacPluginIvyDeps)()

/**
* Run Tut using the configuration specified in this module. The working directory used is the [[mill.contrib.tut.TutModule#millSourcePath]].
*/
def tut: T[CommandResult] = T {
val in = tutSourceDirectory().head.path.toIO.getAbsolutePath
val out = tutTargetDirectory().toIO.getAbsolutePath
val re = tutNameFilter()
val opts = tutScalacOptions()
val pOpts = tutPluginJars().map(pathRef => "-Xplugin:" + pathRef.path.toIO.getAbsolutePath)
val tutArgs = List(in, out, re.pattern.toString) ++ opts ++ pOpts
%%(
'java,
"-cp", tutClasspath().map(_.path.toIO.getAbsolutePath).mkString(java.io.File.pathSeparator),
"tut.TutMain",
tutArgs
)(wd = millSourcePath)
}
}
124 changes: 124 additions & 0 deletions contrib/tut/test/src/mill/contrib/tut/TutTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package mill.contrib
package tut

import ammonite.ops._
import mill._
import mill.eval.Result._
import mill.scalalib._
import mill.util.{TestEvaluator, TestUtil}
import utest._
import utest.framework.TestPath

object TutTests extends TestSuite {

trait TutTestModule extends TestUtil.BaseModule with TutModule {
def millSourcePath = TestUtil.getSrcPathBase() / millOuterCtx.enclosing.split('.')
def scalaVersion = "2.12.4"
def tutVersion = "0.6.7"
}

object TutTest extends TutTestModule

object TutCustomTest extends TutTestModule {
def tutTargetDirectory = millSourcePath
}

object TutLibrariesTest extends TutTestModule {
def ivyDeps = Agg(ivy"org.typelevel::cats-core:1.4.0")
def tutSourceDirectory = T.sources { resourcePathWithLibraries }
def scalacPluginIvyDeps = Agg(ivy"org.spire-math::kind-projector:0.9.8")
}

val resourcePath = pwd / 'contrib / 'tut / 'test / 'tut
val resourcePathWithLibraries = pwd / 'contrib / 'tut / 'test / "tut-with-libraries"

def workspaceTest[T](m: TestUtil.BaseModule, resourcePath: Path = resourcePath)
(t: TestEvaluator => T)
(implicit tp: TestPath): T = {
val eval = new TestEvaluator(m)
rm(m.millSourcePath)
rm(eval.outPath)
mkdir(m.millSourcePath)
cp(resourcePath, m.millSourcePath / 'tut)
t(eval)
}

def tests: Tests = Tests {
'tut - {
'createOutputFile - workspaceTest(TutTest) { eval =>
val expectedPath =
eval.outPath / 'tutTargetDirectory / 'dest / "TutExample.md"

val expected =
"""
|```scala
|scala> 1 + 1
|res0: Int = 2
|```
|
""".trim.stripMargin

val Right((result, evalCount)) = eval.apply(TutTest.tut)

assert(
exists(expectedPath) &&
read! expectedPath == expected
)
}

'supportCustomSettings - workspaceTest(TutCustomTest) { eval =>
val defaultPath =
eval.outPath / 'tutTargetDirectory / 'dest / "TutExample.md"
val expectedPath =
TutCustomTest.millSourcePath / "TutExample.md"

val expected =
"""
|```scala
|scala> 1 + 1
|res0: Int = 2
|```
|
""".trim.stripMargin

val Right((result, evalCount)) = eval.apply(TutCustomTest.tut)

assert(
!exists(defaultPath) &&
exists(expectedPath) &&
read! expectedPath == expected
)
}

'supportUsingLibraries - workspaceTest(TutLibrariesTest, resourcePath = resourcePathWithLibraries) { eval =>
val expectedPath =
eval.outPath / 'tutTargetDirectory / 'dest / "TutWithLibraries.md"

val expected =
"""
|```scala
|import cats._
|import cats.arrow.FunctionK
|import cats.implicits._
|```
|
|```scala
|scala> List(1, 2, 3).combineAll
|res0: Int = 6
|
|scala> λ[FunctionK[List, Option]](_.headOption)(List(1, 2 ,3))
|res1: Option[Int] = Some(1)
|```
|
""".trim.stripMargin

val Right(_) = eval.apply(TutLibrariesTest.tut)

assert(
exists(expectedPath) &&
read! expectedPath == expected
)
}
}
}
}
10 changes: 10 additions & 0 deletions contrib/tut/test/tut-with-libraries/TutWithLibraries.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
```tut:silent
import cats._
import cats.arrow.FunctionK
import cats.implicits._
```

```tut
List(1, 2, 3).combineAll
λ[FunctionK[List, Option]](_.headOption)(List(1, 2 ,3))
```
3 changes: 3 additions & 0 deletions contrib/tut/test/tut/TutExample.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```tut
1 + 1
```
3 changes: 3 additions & 0 deletions docs/pages/4 - Tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@ task are streamed to standard out/error as you would expect, but each task's
specific output is also streamed to a log file on disk, e.g. `out/run/log` or
`out/classFiles/log` for you to inspect later.

Messages logged with `log.debug` appear by default only in the log files.
You can use the `--debug` option when running mill to show them on the console too.

### mill.util.Ctx.Env

- `T.ctx().env`
Expand Down
7 changes: 3 additions & 4 deletions docs/pages/7 - Extending Mill.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ def idea(ev: Evaluator) = T.command {
```

Many built-in tools are implemented as custom evaluator commands:
[all](intro.html#all), [inspect](intro.html#inspect),
[resolve](intro.html#resolve), [show](intro.html#show). If you want a way to run Mill
commands and programmatically manipulate the tasks and outputs, you do so with
your own evaluator command.
[all](http://www.lihaoyi.com/mill/#all), [inspect](http://www.lihaoyi.com/mill/#inspect),
[resolve](http://www.lihaoyi.com/mill/#resolve), [show](http://www.lihaoyi.com/mill/#show).
If you want a way to run Mill commands and programmatically manipulate the tasks and outputs, you do so with your own evaluator command.
Loading

0 comments on commit bbe2ce1

Please sign in to comment.