import ammonite.ops._ import ammonite.ops.ImplicitWd._ import coursier.MavenRepository import mill._ import mill.scalalib._ import mill.scalalib.publish._ import mill.eval.{Evaluator, Result} import mill.define.Task import mill.modules.Jvm._ import $file.CommonBuild /** Utility types for changing a dependency between Ivy and Module (source) */ sealed trait IvyOrModuleDep case class IvyDep(dep: Dep) extends IvyOrModuleDep case class ModuleDep(dep: PublishModule) extends IvyOrModuleDep object chiselCompileOptions { def scalacOptions = Seq( "-deprecation", "-explaintypes", "-feature", "-language:reflectiveCalls", "-unchecked", "-Xlint:infer-any" /* "-Xlint:missing-interpolator" // this causes a: //[error] .../chisel3/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala:605:48: recursive value outer needs type //[error] val outer = clazz.getDeclaredField("$outer").get(this) //[error] ^ //[error] one error found */ ) } val crossVersions = Seq("2.12.10", "2.11.12") // Provide a managed dependency on X if -DXVersion="" is supplied on the command line. val defaultVersions = Map("firrtl" -> "1.3-SNAPSHOT") def getVersion(dep: String, org: String = "edu.berkeley.cs") = { val version = sys.env.getOrElse(dep + "Version", defaultVersions(dep)) ivy"$org::$dep:$version" } // Define the common chisel module. trait CommonChiselModule extends SbtModule { // Normally defined in CrossSbtModule, our submodules don't have it by default def crossScalaVersion: String // This build uses an ivy dependency but allows overriding with a module (source) dependency def firrtlDep: IvyOrModuleDep override def scalacOptions = chiselCompileOptions.scalacOptions ++ CommonBuild.scalacOptionsVersion(crossScalaVersion) override def javacOptions = CommonBuild.javacOptionsVersion(crossScalaVersion) val macroPlugins = Agg(ivy"org.scalamacros:::paradise:2.1.0") def scalacPluginIvyDeps = macroPlugins def compileIvyDeps = macroPlugins def chiselDeps = firrtlDep match { case IvyDep(dep) => Agg(dep) case ModuleDep(_) => Agg() } override def ivyDeps = T { chiselDeps } // Include sonatype snapshots by default for parity with sbt def repositories = super.repositories ++ Seq( MavenRepository("https://oss.sonatype.org/content/repositories/snapshots") ) } trait PublishChiselModule extends CommonChiselModule with PublishModule { override def artifactName = "chisel3" def publishVersion = "3.3-SNAPSHOT" def pomSettings = PomSettings( description = artifactName(), organization = "edu.berkeley.cs", url = "https://chisel.eecs.berkeley.edu", licenses = Seq(License.`BSD-3-Clause`), versionControl = VersionControl.github("freechipsproject", "chisel3"), developers = Seq( Developer("jackbackrack", "Jonathan Bachrach", "https://eecs.berkeley.edu/~jrb/") ) ) } // Make this available to external tools. object chisel3 extends Cross[ChiselTopModule](crossVersions: _*) { def defaultVersion(ev: Evaluator) = T.command{ println(crossVersions.head) } def compile = T{ chisel3(crossVersions.head).compile() } def jar = T{ chisel3(crossVersions.head).jar() } def test = T{ chisel3(crossVersions.head).test.test() } def testOne(args: String*) = T.command { chisel3(crossVersions.head).test.testOne(args: _*) } def publishLocal = T{ chisel3(crossVersions.head).publishLocal() } def docJar = T{ chisel3(crossVersions.head).docJar() } } class ChiselTopModule(val crossScalaVersion: String) extends AbstractChiselModule { // This build uses an ivy dependency but allows overriding with a module (source) dependency def firrtlDep: IvyOrModuleDep = IvyDep(getVersion("firrtl")) } trait AbstractChiselModule extends PublishChiselModule with CommonBuild.BuildInfo with CrossSbtModule { top => // If would be nice if we didn't need to do this, but PublishModule may only be dependent on // other PublishModules. trait UnpublishedChiselModule extends PublishChiselModule object coreMacros extends UnpublishedChiselModule { def crossScalaVersion = top.crossScalaVersion def scalaVersion = crossScalaVersion def firrtlDep: IvyOrModuleDep = top.firrtlDep } object chiselFrontend extends UnpublishedChiselModule { def crossScalaVersion = top.crossScalaVersion def scalaVersion = crossScalaVersion def moduleDeps = Seq(coreMacros) ++ (firrtlDep match { case ModuleDep(dep) => Seq(dep) case IvyDep(_) => Seq() }) def firrtlDep: IvyOrModuleDep = top.firrtlDep } override def moduleDeps = Seq(coreMacros, chiselFrontend) // This submodule is unrooted - its source directory is in the top level directory. override def millSourcePath = super.millSourcePath / ammonite.ops.up // In order to preserve our "all-in-one" policy for published jars, // we define allModuleSources() to include transitive sources, and define // allModuleClasspath() to include transitive classes. def transitiveSources = T { Task.traverse(moduleDeps)(m => T.task{m.allSources()} )().flatten } def allModuleSources = T { allSources() ++ transitiveSources() } def transitiveResources = T { Task.traverse(moduleDeps)(m => T.task{m.resources()} )().flatten } def allModuleResources = T { resources() ++ transitiveResources() } // We package all classes in a singe jar. def allModuleClasspath = T { localClasspath() ++ transitiveLocalClasspath() } // Override publishXmlDeps so we don't include dependencies on our sub-modules. override def publishXmlDeps = T.task { val ivyPomDeps = ivyDeps().map(resolvePublishDependency().apply(_)) ivyPomDeps } // We need to copy (and override) the `jar` and `docJar` targets so we can build // single jars implementing our "all-in-one" policy. override def jar = T { createJar( allModuleClasspath().map(_.path).filter(exists), mainClass() ) } override def docJar = T { val outDir = T.ctx().dest val javadocDir = outDir / 'javadoc mkdir(javadocDir) val files = for{ ref <- allModuleSources() if exists(ref.path) p <- (if (ref.path.isDir) ls.rec(ref.path) else Seq(ref.path)) if (p.isFile && ((p.ext == "scala") || (p.ext == "java"))) } yield p.toNIO.toString val pluginOptions = scalacPluginClasspath().map(pluginPathRef => s"-Xplugin:${pluginPathRef.path}") val options = Seq("-d", javadocDir.toNIO.toString, "-usejavacp") ++ pluginOptions ++ scalacOptions() if (files.nonEmpty) runSubprocess( "scala.tools.nsc.ScalaDoc", scalaCompilerClasspath().map(_.path) ++ compileClasspath().filter(_.path.ext != "pom").map(_.path), mainArgs = (files ++ options).toSeq ) createJar(Agg(javadocDir), None)(outDir) } def sourceJar = T { createJar((allModuleSources() ++ allModuleResources()).map(_.path).filter(exists), None) } override def ivyDeps = Agg( ivy"com.github.scopt::scopt:3.7.1" ) ++ chiselDeps object test extends Tests { override def ivyDeps = Agg( ivy"org.scalatest::scalatest:3.0.8", ivy"org.scalacheck::scalacheck:1.14.0" ) def testFrameworks = Seq("org.scalatest.tools.Framework") def testOne(args: String*) = T.command { super.runMain("org.scalatest.run", args: _*) } } // This is required for building a library, but not for a `run` target. // In the latter case, mill will determine this on its own. def mainClass = Some("chisel3.Driver") override def buildInfoMembers = T { Map[String, String]( "buildInfoPackage" -> artifactName(), "version" -> publishVersion(), "scalaVersion" -> scalaVersion() ) } }