diff --git a/build.sc b/build.sc index 26fb86bd616..40385ea8025 100644 --- a/build.sc +++ b/build.sc @@ -1,11 +1,11 @@ import mill._ import mill.scalalib._ -import mill.scalalib.TestModule._ import mill.scalalib.publish._ import mill.scalalib.scalafmt._ -import coursier.maven.MavenRepository +import mill.define.Cross import mill.scalalib.api.ZincWorkerUtil.matchingVersions import $file.common +import $file.tests object v { val pluginScalaCrossVersions = Seq( @@ -65,7 +65,7 @@ trait Svsim object firrtlut extends Cross[FirrtlUnitTest](v.scalaCrossVersions) trait FirrtlUnitTest - extends common.FirrtlUnitTestModule + extends tests.FirrtlUnitTestModule with CrossModuleBase with ScalafmtModule { override def millSourcePath = firrtl(crossScalaVersion).millSourcePath @@ -185,7 +185,7 @@ trait Chisel object chiselut extends Cross[ChiselUnitTest](v.scalaCrossVersions) trait ChiselUnitTest - extends common.ChiselUnitTestModule + extends tests.ChiselUnitTestModule with CrossModuleBase with ScalafmtModule { override def millSourcePath = chisel(crossScalaVersion).millSourcePath @@ -256,7 +256,7 @@ trait CIRCTPanamaBinder object bindertest extends Cross[CIRCTPanamaBinderModuleTest](v.scalaCrossVersions) trait CIRCTPanamaBinderModuleTest - extends common.CIRCTPanamaBinderModuleTestModule + extends tests.CIRCTPanamaBinderModuleTestModule with CrossModuleBase with ScalafmtModule { override def millSourcePath = circtpanamabinder(crossScalaVersion).millSourcePath @@ -271,3 +271,27 @@ trait CIRCTPanamaBinderModuleTest Seq(PathRef(millSourcePath / "src" / "test")) } } + +object litutility extends Cross[LitUtility](v.scalaCrossVersions) + +trait LitUtility + extends tests.LitUtilityModule + with CrossModuleBase + with ScalafmtModule { + def millSourcePath = super.millSourcePath / os.up / "lit" / "utility" + def circtPanamaBinderModule = circtpanamabinder(crossScalaVersion) +} + +object lit extends Cross[Lit](v.scalaCrossVersions) + +trait Lit + extends tests.LitModule + with Cross.Module[String] { + def scalaVersion: T[String] = crossValue + def runClasspath: T[Seq[os.Path]] = T(litutility(crossValue).runClasspath().map(_.path)) + def pluginJars: T[Seq[os.Path]] = T(Seq(litutility(crossValue).circtPanamaBinderModule.pluginModule.jar().path)) + def javaLibraryPath: T[Seq[os.Path]] = T(litutility(crossValue).circtPanamaBinderModule.libraryPaths().map(_.path)) + def javaHome: T[os.Path] = T(os.Path(sys.props("java.home"))) + def chiselLitDir: T[os.Path] = T(millSourcePath) + def litConfigIn: T[PathRef] = T.source(millSourcePath / "tests" / "lit.site.cfg.py.in") +} diff --git a/common.sc b/common.sc index f571bdc09f9..0d0ee87032e 100644 --- a/common.sc +++ b/common.sc @@ -43,46 +43,6 @@ trait SvsimModule extends ScalaModule { } -trait SvsimUnitTestModule - extends TestModule - with ScalaModule - with TestModule.ScalaTest { - def svsimModule: SvsimModule - - def scalatestIvy: Dep - - def scalacheckIvy: Dep - - override def moduleDeps = Seq(svsimModule) - - override def defaultCommandName() = "test" - - override def ivyDeps = super.ivyDeps() ++ Agg( - scalatestIvy, - scalacheckIvy - ) -} - -trait FirrtlUnitTestModule - extends TestModule - with ScalaModule - with TestModule.ScalaTest { - def firrtlModule: FirrtlModule - - def scalatestIvy: Dep - - def scalacheckIvy: Dep - - override def moduleDeps = Seq(firrtlModule) - - override def defaultCommandName() = "test" - - override def ivyDeps = super.ivyDeps() ++ Agg( - scalatestIvy, - scalacheckIvy - ) -} - trait CoreModule extends ScalaModule with HasMacroAnnotations { @@ -158,24 +118,6 @@ trait HasChisel override def moduleDeps = super.moduleDeps ++ Some(chiselModule) } -trait ChiselUnitTestModule - extends TestModule - with ScalaModule - with HasChisel - with HasMacroAnnotations - with TestModule.ScalaTest { - def scalatestIvy: Dep - - def scalacheckIvy: Dep - - override def defaultCommandName() = "test" - - override def ivyDeps = super.ivyDeps() ++ Agg( - scalatestIvy, - scalacheckIvy - ) -} - trait HasJextractGeneratedSources extends JavaModule { def includePaths: T[Seq[PathRef]] @@ -280,20 +222,3 @@ trait HasCIRCTPanamaBinderModule ) } -trait CIRCTPanamaBinderModuleTestModule - extends TestModule - with ScalaModule - with HasCIRCTPanamaBinderModule - with HasMacroAnnotations - with TestModule.ScalaTest { - def scalatestIvy: Dep - - def scalacheckIvy: Dep - - override def defaultCommandName() = "test" - - override def ivyDeps = super.ivyDeps() ++ Agg( - scalatestIvy, - scalacheckIvy - ) -} diff --git a/flake.nix b/flake.nix index 03cebc32443..41d8c91b0f2 100644 --- a/flake.nix +++ b/flake.nix @@ -18,6 +18,9 @@ mill circt jextract + lit + llvm + scala-cli ]; in { diff --git a/lit/tests/CompilerPlugin/WithCompilerPlugin.sc b/lit/tests/CompilerPlugin/WithCompilerPlugin.sc new file mode 100644 index 00000000000..fda84c9c5fd --- /dev/null +++ b/lit/tests/CompilerPlugin/WithCompilerPlugin.sc @@ -0,0 +1,16 @@ +// RUN: scala-cli --server=false --java-home=%JAVAHOME --extra-jars=%RUNCLASSPATH --scala-version=%SCALAVERSION --scala-option="-Xplugin:%SCALAPLUGINJARS" --java-opt="--enable-native-access=ALL-UNNAMED --enable-preview -Djava.library.path=%JAVALIBRARYPATH" %s | FileCheck %s + +import chisel3._ +import circt.stage.ChiselStage +class FooBundle extends Bundle { + val foo = Input(UInt(3.W)) +} +// CHECK-LABEL: module FooModule +// CHECK: input clock : Clock +// CHECK: input reset : UInt<1> +class FooModule extends Module { + // CHECK: output io : { flip foo : UInt<3>} + val io = IO(new FooBundle) + // CHECK: skip +} +println(ChiselStage.emitCHIRRTL(new FooModule)) diff --git a/lit/tests/CompilerPlugin/WithoutCompilerPlugin.sc b/lit/tests/CompilerPlugin/WithoutCompilerPlugin.sc new file mode 100644 index 00000000000..44fbfde3888 --- /dev/null +++ b/lit/tests/CompilerPlugin/WithoutCompilerPlugin.sc @@ -0,0 +1,12 @@ +// RUN: not scala-cli --server=false --java-home=%JAVAHOME --extra-jars=%RUNCLASSPATH --scala-version=%SCALAVERSION --java-opt="--enable-native-access=ALL-UNNAMED --enable-preview -Djava.library.path=%JAVALIBRARYPATH" %s 2>&1 | FileCheck %s + +import chisel3._ +import circt.stage.ChiselStage +class FooBundle extends Bundle { + val foo = Input(UInt(3.W)) +} +class FooModule extends Module { + // CHECK: assertion failed: The Chisel compiler plugin is now required for compiling Chisel code. + val io = IO(new FooBundle) +} +println(ChiselStage.emitCHIRRTL(new FooModule)) diff --git a/lit/tests/lit.cfg.py b/lit/tests/lit.cfg.py new file mode 100644 index 00000000000..c4c4e23c34a --- /dev/null +++ b/lit/tests/lit.cfg.py @@ -0,0 +1,16 @@ +import platform +import lit.formats +from lit.llvm import llvm_config +from lit.llvm.subst import ToolSubst + +config.name = 'CHISEL' +config.test_format = lit.formats.ShTest(True) +config.suffixes = [".sc"] +config.substitutions = [ + ('%SCALAVERSION', config.scala_version), + ('%RUNCLASSPATH', ':'.join(config.run_classpath)), + ('%SCALAPLUGINJARS', ':'.join(config.scala_plugin_jars)), + ('%JAVAHOME', config.java_home), + ('%JAVALIBRARYPATH', ':'.join(config.java_library_path)) +] +config.test_source_root = os.path.dirname(__file__) diff --git a/lit/tests/lit.site.cfg.py.in b/lit/tests/lit.site.cfg.py.in new file mode 100644 index 00000000000..c08dbeb6ee2 --- /dev/null +++ b/lit/tests/lit.site.cfg.py.in @@ -0,0 +1,9 @@ +import sys +config.scala_version = "@SCALA_VERSION@" +config.run_classpath = "@RUN_CLASSPATH@".split(",") +config.scala_plugin_jars = "@SCALA_PLUGIN_JARS@".split(",") +config.java_home = "@JAVA_HOME@" +config.java_library_path = "@JAVA_LIBRARY_PATH@".split(",") +config.chisel_lit_dir = "@CHISEL_LIT_DIR@".split(",") +config.test_exec_root = os.path.dirname(__file__) +lit_config.load_config(config, "@CHISEL_LIT_DIR@/tests/lit.cfg.py") diff --git a/lit/utility/src/package.scala b/lit/utility/src/package.scala new file mode 100644 index 00000000000..03e63ea52bb --- /dev/null +++ b/lit/utility/src/package.scala @@ -0,0 +1,23 @@ +import chisel3._ + +package object utility { + object binding { + def streamString(module: => RawModule, stream: chisel3.internal.CIRCTConverter => geny.Writable): String = Seq( + new chisel3.stage.phases.Elaborate, + chisel3.internal.panama.Convert + ).foldLeft( + firrtl.AnnotationSeq(Seq(chisel3.stage.ChiselGeneratorAnnotation(() => module))) + ) { case (annos, phase) => phase.transform(annos) } + .collectFirst { + case chisel3.internal.panama.circt.PanamaCIRCTConverterAnnotation(converter) => + val string = new java.io.ByteArrayOutputStream + stream(converter).writeBytesTo(string) + new String(string.toByteArray) + } + .get + + def firrtlString(module: => RawModule): String = streamString(module, _.firrtlStream) + + def verilogString(module: => RawModule): String = streamString(module, _.verilogStream) + } +} \ No newline at end of file diff --git a/nix/scala-cli.nix b/nix/scala-cli.nix new file mode 100644 index 00000000000..7e63b472c5e --- /dev/null +++ b/nix/scala-cli.nix @@ -0,0 +1,88 @@ +{ stdenv +, coreutils +, lib +, installShellFiles +, zlib +, autoPatchelfHook +, fetchurl +, makeWrapper +, callPackage +, jre +, testers +, scala-cli +}: + +let + pname = "scala-cli"; + sources = lib.importJSON ./sources.json; + inherit (sources) version assets; + + platforms = builtins.attrNames assets; +in +stdenv.mkDerivation { + inherit pname version; + nativeBuildInputs = [ installShellFiles makeWrapper ] + ++ lib.optional stdenv.isLinux autoPatchelfHook; + buildInputs = + assert lib.assertMsg (lib.versionAtLeast jre.version "17.0.0") '' + scala-cli requires Java 17 or newer, but ${jre.name} is ${jre.version} + ''; + [ coreutils zlib stdenv.cc.cc ]; + src = + let + asset = assets."${stdenv.hostPlatform.system}" or (throw "Unsupported platform ${stdenv.hostPlatform.system}"); + in + fetchurl { + url = "https://github.com/Virtuslab/scala-cli/releases/download/v${version}/${asset.asset}"; + sha256 = asset.sha256; + }; + unpackPhase = '' + runHook preUnpack + gzip -d < $src > scala-cli + runHook postUnpack + ''; + + installPhase = '' + runHook preInstall + install -Dm755 scala-cli $out/bin/.scala-cli-wrapped + makeWrapper $out/bin/.scala-cli-wrapped $out/bin/scala-cli \ + --set JAVA_HOME ${jre.home} \ + --argv0 "$out/bin/scala-cli" + runHook postInstall + ''; + + # We need to call autopatchelf before generating completions + dontAutoPatchelf = true; + + postFixup = lib.optionalString stdenv.isLinux '' + autoPatchelf $out + '' + '' + # hack to ensure the completion function looks right + # as $0 is used to generate the compdef directive + mkdir temp + cp $out/bin/.scala-cli-wrapped temp/scala-cli + PATH="./temp:$PATH" + + installShellCompletion --cmd scala-cli \ + --bash <(scala-cli completions bash) \ + --zsh <(scala-cli completions zsh) + ''; + + meta = with lib; { + homepage = "https://scala-cli.virtuslab.org"; + downloadPage = "https://github.com/VirtusLab/scala-cli/releases/v${version}"; + sourceProvenance = with sourceTypes; [ binaryNativeCode ]; + license = licenses.asl20; + description = "Command-line tool to interact with the Scala language"; + maintainers = [ maintainers.kubukoz ]; + inherit platforms; + }; + + passthru.updateScript = callPackage ./update.nix { } { inherit platforms pname version; }; + + passthru.tests.version = testers.testVersion { + package = scala-cli; + command = "scala-cli version --offline"; + }; +} + diff --git a/nix/sources.json b/nix/sources.json new file mode 100644 index 00000000000..fa1f690317c --- /dev/null +++ b/nix/sources.json @@ -0,0 +1,21 @@ +{ + "version": "1.1.2", + "assets": { + "aarch64-darwin": { + "asset": "scala-cli-aarch64-apple-darwin.gz", + "sha256": "1m5ac0pkp68k446xd8p21hc8caaahpyd3vm5pn1i1cc176f6fm8b" + }, + "aarch64-linux": { + "asset": "scala-cli-aarch64-pc-linux.gz", + "sha256": "1aqw8xmim7rmh8h98h10dlhm0hzl9bs89a51mbd3d99yxa5dq4z0" + }, + "x86_64-darwin": { + "asset": "scala-cli-x86_64-apple-darwin.gz", + "sha256": "06473l04z3nfadi72klk3aw4i39qkyzl3qp7kb1ysn5wx58ci51w" + }, + "x86_64-linux": { + "asset": "scala-cli-x86_64-pc-linux.gz", + "sha256": "1khdzv0s9l6wcb47hnv96prvzgzwcw0db34j6525bdgakj62fhf3" + } + } +} diff --git a/overlay.nix b/overlay.nix index 33037308f9c..740e9b5b614 100644 --- a/overlay.nix +++ b/overlay.nix @@ -50,4 +50,5 @@ final: prev: circt.llvm.lib ]; }; + scala-cli = prev.callPackage ./nix/scala-cli.nix { }; } diff --git a/tests.sc b/tests.sc new file mode 100644 index 00000000000..6742f3810c3 --- /dev/null +++ b/tests.sc @@ -0,0 +1,114 @@ +import mill._ +import mill.api.Result +import mill.scalalib._ +import mill.scalalib.api.CompilationResult +import mill.util.Jvm + +import java.util +import scala.jdk.StreamConverters.StreamHasToScala + +trait SvsimUnitTestModule + extends TestModule + with ScalaModule + with TestModule.ScalaTest { + def svsimModule: common.SvsimModule + + def scalatestIvy: Dep + + def scalacheckIvy: Dep + + override def moduleDeps = Seq(svsimModule) + + override def defaultCommandName() = "test" + + override def ivyDeps = super.ivyDeps() ++ Agg( + scalatestIvy, + scalacheckIvy + ) +} + +trait FirrtlUnitTestModule + extends TestModule + with ScalaModule + with TestModule.ScalaTest { + def firrtlModule: common.FirrtlModule + + def scalatestIvy: Dep + + def scalacheckIvy: Dep + + override def moduleDeps = Seq(firrtlModule) + + override def defaultCommandName() = "test" + + override def ivyDeps = super.ivyDeps() ++ Agg( + scalatestIvy, + scalacheckIvy + ) +} + +trait ChiselUnitTestModule + extends TestModule + with ScalaModule + with common.HasChisel + with common.HasMacroAnnotations + with TestModule.ScalaTest { + def scalatestIvy: Dep + + def scalacheckIvy: Dep + + override def defaultCommandName() = "test" + + override def ivyDeps = super.ivyDeps() ++ Agg( + scalatestIvy, + scalacheckIvy + ) +} + +trait CIRCTPanamaBinderModuleTestModule + extends TestModule + with ScalaModule + with common.HasCIRCTPanamaBinderModule + with common.HasMacroAnnotations + with TestModule.ScalaTest { + def scalatestIvy: Dep + + def scalacheckIvy: Dep + + override def defaultCommandName() = "test" + + override def ivyDeps = super.ivyDeps() ++ Agg( + scalatestIvy, + scalacheckIvy + ) +} + +trait LitUtilityModule + extends ScalaModule + with common.HasCIRCTPanamaBinderModule + with common.HasMacroAnnotations + +trait LitModule + extends Module { + def scalaVersion: T[String] + def runClasspath: T[Seq[os.Path]] + def pluginJars: T[Seq[os.Path]] + def javaLibraryPath: T[Seq[os.Path]] + def javaHome: T[os.Path] + def chiselLitDir: T[os.Path] + def litConfigIn: T[PathRef] + def litConfig: T[PathRef] = T { + os.write( + T.dest / "lit.site.cfg.py", + os.read(litConfigIn().path) + .replaceAll("@SCALA_VERSION@", scalaVersion()) + .replaceAll("@RUN_CLASSPATH@", runClasspath().mkString(",")) + .replaceAll("@SCALA_PLUGIN_JARS@", pluginJars().mkString(",")) + .replaceAll("@JAVA_HOME@", javaHome().toString) + .replaceAll("@JAVA_LIBRARY_PATH@", javaLibraryPath().mkString(",")) + .replaceAll("@CHISEL_LIT_DIR@", chiselLitDir().toString) + ) + PathRef(T.dest) + } + def run(args: String*) = T.command(os.proc("lit", litConfig().path).call(T.dest, stdout = os.ProcessOutput.Readlines(line => T.ctx().log.info("[lit] " + line)))) + }