xref: /XiangShan/build.sc (revision 8795ffc00d922e6e6cde93b1027e9f79782b0564)
1c6d43980SLemover/***************************************************************************************
2e3da8badSTang Haojin* Copyright (c) 2024 Beijing Institute of Open Source Chip (BOSC)
3e3da8badSTang Haojin* Copyright (c) 2020-2024 Institute of Computing Technology, Chinese Academy of Sciences
4f320e0f0SYinan Xu* Copyright (c) 2020-2021 Peng Cheng Laboratory
5c6d43980SLemover*
6c6d43980SLemover* XiangShan is licensed under Mulan PSL v2.
7c6d43980SLemover* You can use this software according to the terms and conditions of the Mulan PSL v2.
8c6d43980SLemover* You may obtain a copy of Mulan PSL v2 at:
9c6d43980SLemover*          http://license.coscl.org.cn/MulanPSL2
10c6d43980SLemover*
11c6d43980SLemover* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
12c6d43980SLemover* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
13c6d43980SLemover* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
14c6d43980SLemover*
15c6d43980SLemover* See the Mulan PSL v2 for more details.
16c6d43980SLemover***************************************************************************************/
17c6d43980SLemover
182102afb5SLinJiaweiimport mill._
19718a511dSLinJiaweiimport scalalib._
20cf7d6b7aSMuziimport scalafmt._
213956160fSTang Haojinimport $packages._
2272060888SJiawei Linimport $file.`rocket-chip`.common
238891a219SYinan Xuimport $file.`rocket-chip`.cde.common
243956160fSTang Haojinimport $file.`rocket-chip`.hardfloat.common
257f37d55fSTang Haojinimport $file.huancun.common
267f37d55fSTang Haojinimport $file.coupledL2.common
2778a8cd25Szhanglinjuanimport $file.openLLC.common
282905e463SZihao Yu
29ce34d21eSJiuyue Ma/* for publishVersion */
30ce34d21eSJiuyue Maimport $ivy.`de.tototec::de.tobiasroeser.mill.vcs.version::0.4.0`
31ce34d21eSJiuyue Maimport de.tobiasroeser.mill.vcs.version.VcsVersion
32844fba5bSXuan Huimport java.io.{BufferedReader, InputStreamReader}
33ce34d21eSJiuyue Maimport java.time.LocalDateTime
34ce34d21eSJiuyue Maimport java.time.format.DateTimeFormatter
3526f0fb0dSJiuyue Maimport java.util.Locale
36ce34d21eSJiuyue Maimport scala.util.matching.Regex
37ce34d21eSJiuyue Ma
3890804084SHaojin Tangval defaultScalaVersion = "2.13.15"
393956160fSTang Haojinval pwd = os.Path(sys.env("MILL_WORKSPACE_ROOT"))
4072060888SJiawei Lin
41e3da8badSTang Haojindef defaultVersions = Map(
4290804084SHaojin Tang  "chisel"        -> ivy"org.chipsalliance::chisel:6.6.0",
4390804084SHaojin Tang  "chisel-plugin" -> ivy"org.chipsalliance:::chisel-plugin:6.6.0",
44195ef4a5STang Haojin  "chiseltest"    -> ivy"edu.berkeley.cs::chiseltest:6.0.0"
45c21bff99SJiawei Lin)
467f37d55fSTang Haojin
47e3da8badSTang Haojintrait HasChisel extends SbtModule {
487f37d55fSTang Haojin  def chiselModule: Option[ScalaModule] = None
497f37d55fSTang Haojin
507f37d55fSTang Haojin  def chiselPluginJar: T[Option[PathRef]] = None
517f37d55fSTang Haojin
52e3da8badSTang Haojin  def chiselIvy: Option[Dep] = Some(defaultVersions("chisel"))
537f37d55fSTang Haojin
54e3da8badSTang Haojin  def chiselPluginIvy: Option[Dep] = Some(defaultVersions("chisel-plugin"))
557f37d55fSTang Haojin
567f37d55fSTang Haojin  override def scalaVersion = defaultScalaVersion
577f37d55fSTang Haojin
587f37d55fSTang Haojin  override def scalacOptions = super.scalacOptions() ++
597f37d55fSTang Haojin    Agg("-language:reflectiveCalls", "-Ymacro-annotations", "-Ytasty-reader")
607f37d55fSTang Haojin
61e3da8badSTang Haojin  override def ivyDeps = super.ivyDeps() ++ Agg(chiselIvy.get)
627f37d55fSTang Haojin
637f37d55fSTang Haojin  override def scalacPluginIvyDeps = super.scalacPluginIvyDeps() ++ Agg(chiselPluginIvy.get)
64c21bff99SJiawei Lin}
65c21bff99SJiawei Lin
66e3da8badSTang Haojinobject rocketchip
673956160fSTang Haojin  extends $file.`rocket-chip`.common.RocketChipModule
6851e45dbbSTang Haojin    with HasChisel {
697f37d55fSTang Haojin  def scalaVersion: T[String] = T(defaultScalaVersion)
7072060888SJiawei Lin
713956160fSTang Haojin  override def millSourcePath = pwd / "rocket-chip"
7272060888SJiawei Lin
737f37d55fSTang Haojin  def macrosModule = macros
7472060888SJiawei Lin
75e3da8badSTang Haojin  def hardfloatModule = hardfloat
7672060888SJiawei Lin
777f37d55fSTang Haojin  def cdeModule = cde
7872060888SJiawei Lin
79195ef4a5STang Haojin  def mainargsIvy = ivy"com.lihaoyi::mainargs:0.7.0"
807f37d55fSTang Haojin
81195ef4a5STang Haojin  def json4sJacksonIvy = ivy"org.json4s::json4s-jackson:4.0.7"
827f37d55fSTang Haojin
837f37d55fSTang Haojin  object macros extends Macros
847f37d55fSTang Haojin
857f37d55fSTang Haojin  trait Macros
863956160fSTang Haojin    extends $file.`rocket-chip`.common.MacrosModule
877f37d55fSTang Haojin      with SbtModule {
887f37d55fSTang Haojin
897f37d55fSTang Haojin    def scalaVersion: T[String] = T(defaultScalaVersion)
907f37d55fSTang Haojin
917f37d55fSTang Haojin    def scalaReflectIvy = ivy"org.scala-lang:scala-reflect:${defaultScalaVersion}"
922905e463SZihao Yu  }
932905e463SZihao Yu
94e3da8badSTang Haojin  object hardfloat
953956160fSTang Haojin    extends $file.`rocket-chip`.hardfloat.common.HardfloatModule with HasChisel {
962102afb5SLinJiawei
977f37d55fSTang Haojin    def scalaVersion: T[String] = T(defaultScalaVersion)
98718a511dSLinJiawei
993956160fSTang Haojin    override def millSourcePath = pwd / "rocket-chip" / "hardfloat" / "hardfloat"
10072060888SJiawei Lin
10172060888SJiawei Lin  }
10272060888SJiawei Lin
103e3da8badSTang Haojin  object cde
1043956160fSTang Haojin    extends $file.`rocket-chip`.cde.common.CDEModule with ScalaModule {
105718a511dSLinJiawei
1067f37d55fSTang Haojin    def scalaVersion: T[String] = T(defaultScalaVersion)
1077f37d55fSTang Haojin
1083956160fSTang Haojin    override def millSourcePath = pwd / "rocket-chip" / "cde" / "cde"
1097f37d55fSTang Haojin  }
110718a511dSLinJiawei}
111718a511dSLinJiawei
112e3da8badSTang Haojinobject utility extends HasChisel {
1133c02ee8fSwakafa
1143956160fSTang Haojin  override def millSourcePath = pwd / "utility"
1153c02ee8fSwakafa
1163c02ee8fSwakafa  override def moduleDeps = super.moduleDeps ++ Seq(
117e3da8badSTang Haojin    rocketchip
1183c02ee8fSwakafa  )
1197f37d55fSTang Haojin
1200a16c8b1SQiming Chu  override def ivyDeps = super.ivyDeps() ++ Agg(
1210a16c8b1SQiming Chu    ivy"com.lihaoyi::sourcecode:0.4.2",
1220a16c8b1SQiming Chu  )
1230a16c8b1SQiming Chu
124*8795ffc0SSam Castleberry  object test extends SbtTests with TestModule.ScalaTest {
125*8795ffc0SSam Castleberry    override def ivyDeps = Agg(ivy"org.scalatest::scalatest:3.2.7")
126*8795ffc0SSam Castleberry  }
1277f37d55fSTang Haojin}
1287f37d55fSTang Haojin
129e3da8badSTang Haojinobject yunsuan extends HasChisel {
1304b0d80d8SXuan Hu
1313956160fSTang Haojin  override def millSourcePath = pwd / "yunsuan"
1324b0d80d8SXuan Hu
1334b0d80d8SXuan Hu}
1344b0d80d8SXuan Hu
1353956160fSTang Haojinobject huancun extends $file.huancun.common.HuanCunModule with HasChisel {
1367f37d55fSTang Haojin
1373956160fSTang Haojin  override def millSourcePath = pwd / "huancun"
1387f37d55fSTang Haojin
139e3da8badSTang Haojin  def rocketModule: ScalaModule = rocketchip
1407f37d55fSTang Haojin
141e3da8badSTang Haojin  def utilityModule: ScalaModule = utility
1427f37d55fSTang Haojin
1437f37d55fSTang Haojin}
1447f37d55fSTang Haojin
1453956160fSTang Haojinobject coupledL2 extends $file.coupledL2.common.CoupledL2Module with HasChisel {
1467f37d55fSTang Haojin
1473956160fSTang Haojin  override def millSourcePath = pwd / "coupledL2"
1487f37d55fSTang Haojin
149e3da8badSTang Haojin  def rocketModule: ScalaModule = rocketchip
1507f37d55fSTang Haojin
151e3da8badSTang Haojin  def utilityModule: ScalaModule = utility
1527f37d55fSTang Haojin
153e3da8badSTang Haojin  def huancunModule: ScalaModule = huancun
1547f37d55fSTang Haojin
1557f37d55fSTang Haojin}
1567f37d55fSTang Haojin
1575c060727Ssumailyycobject openNCB extends SbtModule with HasChisel {
1585c060727Ssumailyyc
1593956160fSTang Haojin  override def millSourcePath = pwd / "openLLC" / "openNCB"
1605c060727Ssumailyyc
1615c060727Ssumailyyc  override def moduleDeps = super.moduleDeps ++ Seq(
1625c060727Ssumailyyc    rocketchip
1635c060727Ssumailyyc  )
1645c060727Ssumailyyc
1655c060727Ssumailyyc}
1665c060727Ssumailyyc
1673956160fSTang Haojinobject openLLC extends $file.openLLC.common.OpenLLCModule with HasChisel {
16878a8cd25Szhanglinjuan
1693956160fSTang Haojin  override def millSourcePath = pwd / "openLLC"
17078a8cd25Szhanglinjuan
171e3da8badSTang Haojin  def coupledL2Module: ScalaModule = coupledL2
17278a8cd25Szhanglinjuan
173e3da8badSTang Haojin  def rocketModule: ScalaModule = rocketchip
17478a8cd25Szhanglinjuan
175e3da8badSTang Haojin  def utilityModule: ScalaModule = utility
1765c060727Ssumailyyc
1775c060727Ssumailyyc  def openNCBModule: ScalaModule = openNCB
1785c060727Ssumailyyc
17978a8cd25Szhanglinjuan}
18078a8cd25Szhanglinjuan
181e3da8badSTang Haojinobject difftest extends HasChisel {
1827f37d55fSTang Haojin
1833956160fSTang Haojin  override def millSourcePath = pwd / "difftest"
1847f37d55fSTang Haojin
185831731b1STang Haojin  object test extends SbtTests with TestModule.ScalaTest {
186831731b1STang Haojin    override def sources = T.sources {
1875bd65c56STang Haojin      super.sources() ++ Seq(PathRef(this.millSourcePath / "src" / "generator" / "chisel"))
188831731b1STang Haojin    }
189831731b1STang Haojin  }
190831731b1STang Haojin
1917f37d55fSTang Haojin}
1927f37d55fSTang Haojin
193e3da8badSTang Haojinobject fudian extends HasChisel {
1947f37d55fSTang Haojin
1953956160fSTang Haojin  override def millSourcePath = pwd / "fudian"
1967f37d55fSTang Haojin
1973c02ee8fSwakafa}
1983c02ee8fSwakafa
1998cfc24b2STang Haojinobject chiselAIA extends HasChisel {
2008cfc24b2STang Haojin  override def millSourcePath = pwd / "ChiselAIA"
2018cfc24b2STang Haojin
2028cfc24b2STang Haojin  override def moduleDeps = super.moduleDeps ++ Seq(
2038cfc24b2STang Haojin    rocketchip,
2048cfc24b2STang Haojin    utility
2058cfc24b2STang Haojin  )
2068cfc24b2STang Haojin}
2078cfc24b2STang Haojin
208e3da8badSTang Haojinobject macros extends ScalaModule {
209039cdc35SXuan Hu
2103956160fSTang Haojin  override def millSourcePath = pwd / "macros"
211039cdc35SXuan Hu
212039cdc35SXuan Hu  override def scalaVersion: T[String] = T(defaultScalaVersion)
213039cdc35SXuan Hu
214039cdc35SXuan Hu  override def ivyDeps = super.ivyDeps() ++ Agg(ivy"org.scala-lang:scala-reflect:${defaultScalaVersion}")
215039cdc35SXuan Hu
216039cdc35SXuan Hu  def scalaReflectIvy = ivy"org.scala-lang:scala-reflect:${defaultScalaVersion}"
217039cdc35SXuan Hu}
218039cdc35SXuan Hu
21972060888SJiawei Lin// extends this trait to use XiangShan in other projects
2207f37d55fSTang Haojintrait XiangShanModule extends ScalaModule {
22172060888SJiawei Lin
2227f37d55fSTang Haojin  def rocketModule: ScalaModule
22372060888SJiawei Lin
2247f37d55fSTang Haojin  def difftestModule: ScalaModule
225718a511dSLinJiawei
2267f37d55fSTang Haojin  def huancunModule: ScalaModule
227718a511dSLinJiawei
2287f37d55fSTang Haojin  def coupledL2Module: ScalaModule
2297f37d55fSTang Haojin
23078a8cd25Szhanglinjuan  def openLLCModule: ScalaModule
23178a8cd25Szhanglinjuan
2327f37d55fSTang Haojin  def fudianModule: ScalaModule
2337f37d55fSTang Haojin
2347f37d55fSTang Haojin  def utilityModule: ScalaModule
23572060888SJiawei Lin
23683ba63b3SXuan Hu  def yunsuanModule: ScalaModule
237718a511dSLinJiawei
2388cfc24b2STang Haojin  def chiselAIAModule: ScalaModule
2398cfc24b2STang Haojin
240039cdc35SXuan Hu  def macrosModule: ScalaModule
241039cdc35SXuan Hu
242c5f31b5bSLinJiawei  override def moduleDeps = super.moduleDeps ++ Seq(
24372060888SJiawei Lin    rocketModule,
24472060888SJiawei Lin    difftestModule,
24572060888SJiawei Lin    huancunModule,
24615ee59e4Swakafa    coupledL2Module,
24778a8cd25Szhanglinjuan    openLLCModule,
24830056234SZhangZifei    yunsuanModule,
2493c02ee8fSwakafa    fudianModule,
2507f37d55fSTang Haojin    utilityModule,
2518cfc24b2STang Haojin    chiselAIAModule,
252039cdc35SXuan Hu    macrosModule,
253c5f31b5bSLinJiawei  )
2542905e463SZihao Yu
2553956160fSTang Haojin  val resourcesPATH = pwd.toString() + "/src/main/resources"
2564b0d80d8SXuan Hu  val envPATH = sys.env("PATH") + ":" + resourcesPATH
2576f021e01SJiawei Lin
2584b0d80d8SXuan Hu  override def forkEnv = Map("PATH" -> envPATH)
2597f37d55fSTang Haojin}
2607f37d55fSTang Haojin
261cf7d6b7aSMuziobject xiangshan extends XiangShanModule with HasChisel with ScalafmtModule {
2627f37d55fSTang Haojin
2633956160fSTang Haojin  override def millSourcePath = pwd
2647f37d55fSTang Haojin
265e3da8badSTang Haojin  def rocketModule = rocketchip
2667f37d55fSTang Haojin
267e3da8badSTang Haojin  def difftestModule = difftest
2687f37d55fSTang Haojin
269e3da8badSTang Haojin  def huancunModule = huancun
2707f37d55fSTang Haojin
271e3da8badSTang Haojin  def coupledL2Module = coupledL2
2727f37d55fSTang Haojin
273e3da8badSTang Haojin  def openLLCModule = openLLC
27478a8cd25Szhanglinjuan
275e3da8badSTang Haojin  def fudianModule = fudian
2767f37d55fSTang Haojin
277e3da8badSTang Haojin  def utilityModule = utility
2787f37d55fSTang Haojin
279e3da8badSTang Haojin  def yunsuanModule = yunsuan
28083ba63b3SXuan Hu
2818cfc24b2STang Haojin  def chiselAIAModule = chiselAIA
2828cfc24b2STang Haojin
283039cdc35SXuan Hu  def macrosModule = macros
284039cdc35SXuan Hu
2858c9adf0cSTang Haojin  // properties may be changed by user. Use `Task.Input` here.
2868c9adf0cSTang Haojin  def forkArgsTask = Task.Input {
2878c9adf0cSTang Haojin    Seq(s"-Xmx${sys.props.getOrElse("jvm-xmx", "40G")}", s"-Xss${sys.props.getOrElse("jvm-xss", "256m")}")
2888c9adf0cSTang Haojin  }
2898c9adf0cSTang Haojin
2908c9adf0cSTang Haojin  override def forkArgs = forkArgsTask()
2917f37d55fSTang Haojin
29283ba63b3SXuan Hu  override def ivyDeps = super.ivyDeps() ++ Agg(
293e3da8badSTang Haojin    defaultVersions("chiseltest"),
2945bd65c56STang Haojin    ivy"io.circe::circe-yaml:1.15.0",
2955bd65c56STang Haojin    ivy"io.circe::circe-generic-extras:0.14.4"
29683ba63b3SXuan Hu  )
29783ba63b3SXuan Hu
298e3da8badSTang Haojin  override def scalacOptions = super.scalacOptions() ++ Agg("-deprecation", "-feature")
299e3da8badSTang Haojin
300ce34d21eSJiuyue Ma  def publishVersion: T[String] = VcsVersion.vcsState().format(
301ce34d21eSJiuyue Ma    revHashDigits = 8,
302ce34d21eSJiuyue Ma    dirtyHashDigits = 0,
303ce34d21eSJiuyue Ma    commitCountPad = -1,
304ce34d21eSJiuyue Ma    countSep = "",
305ce34d21eSJiuyue Ma    tagModifier = (tag: String) => "[Rr]elease.*".r.findFirstMatchIn(tag) match {
306ce34d21eSJiuyue Ma      case Some(_) => "Kunminghu-Release-" + LocalDateTime.now().format(
30726f0fb0dSJiuyue Ma                                 DateTimeFormatter.ofPattern("MMM-dd-yyyy").withLocale(new Locale("en")))
308ce34d21eSJiuyue Ma      case None => "Kunminghu-dev"
309ce34d21eSJiuyue Ma    },
310ce34d21eSJiuyue Ma    /* add "username, buildhost, buildtime" for non-release version */
311ce34d21eSJiuyue Ma    untaggedSuffix = " (%s@%s) # %s".format(
312ce34d21eSJiuyue Ma      System.getProperty("user.name"),
313ce34d21eSJiuyue Ma      java.net.InetAddress.getLocalHost().getHostName(),
31426f0fb0dSJiuyue Ma      LocalDateTime.now().format(DateTimeFormatter.ofPattern("MMM dd hh:mm:ss yyyy").withLocale(new Locale("en")))),
315ce34d21eSJiuyue Ma  )
316ce34d21eSJiuyue Ma
31763e777d1STang Haojin  // gitStatus changes frequently and unpredictably. Use `Task.Input` here.
31863e777d1STang Haojin  def gitStatus: T[String] = Task.Input {
319844fba5bSXuan Hu    val gitRevParseBuilder = new ProcessBuilder("git", "rev-parse", "HEAD")
320844fba5bSXuan Hu    val gitRevParseProcess = gitRevParseBuilder.start()
321844fba5bSXuan Hu    val shaReader = new BufferedReader(new InputStreamReader(gitRevParseProcess.getInputStream))
322844fba5bSXuan Hu    val sha = shaReader.readLine()
323844fba5bSXuan Hu
324844fba5bSXuan Hu    val gitStatusBuilder = new ProcessBuilder("git", "status", "-uno", "--porcelain")
325844fba5bSXuan Hu    val gitStatusProcess = gitStatusBuilder.start()
326844fba5bSXuan Hu    val gitStatusReader = new BufferedReader(new InputStreamReader(gitStatusProcess.getInputStream))
327844fba5bSXuan Hu    val status = gitStatusReader.readLine()
328844fba5bSXuan Hu    val gitDirty = if (status == null) 0 else 1
329844fba5bSXuan Hu
330844fba5bSXuan Hu    val str =
331844fba5bSXuan Hu      s"""|SHA=$sha
332844fba5bSXuan Hu          |dirty=$gitDirty
333844fba5bSXuan Hu          |""".stripMargin
334844fba5bSXuan Hu    str
335844fba5bSXuan Hu  }
336844fba5bSXuan Hu
337f6f8d1f7SJiuyue Ma  def packDifftestResources(destDir: os.Path): Unit = {
338f6f8d1f7SJiuyue Ma    // package difftest source as resources, only git tracked files were collected
339f6f8d1f7SJiuyue Ma    val difftest_srcs = os.proc("git", "ls-files").call(cwd = pwd / "difftest").out
340f6f8d1f7SJiuyue Ma                          .text().split("\n").filter(_.nonEmpty).toSeq
341f6f8d1f7SJiuyue Ma                          .map(os.RelPath(_))
342f6f8d1f7SJiuyue Ma    difftest_srcs.foreach { f =>
343f6f8d1f7SJiuyue Ma      os.copy(pwd / "difftest" / f, destDir / "difftest-src" / f, createFolders = true)
344f6f8d1f7SJiuyue Ma    }
345f6f8d1f7SJiuyue Ma
346f6f8d1f7SJiuyue Ma    // package ready-to-run binary as resources
347f6f8d1f7SJiuyue Ma    val ready_to_run = Seq("riscv64-nemu-interpreter-dual-so",
348f6f8d1f7SJiuyue Ma                           "riscv64-nemu-interpreter-so",
349f6f8d1f7SJiuyue Ma                           "riscv64-spike-so")
350f6f8d1f7SJiuyue Ma    ready_to_run.foreach { f =>
351f6f8d1f7SJiuyue Ma      os.copy(pwd / "ready-to-run" / f, destDir / "ready-to-run" / f, createFolders = true)
352f6f8d1f7SJiuyue Ma    }
353f6f8d1f7SJiuyue Ma  }
354f6f8d1f7SJiuyue Ma
355ce34d21eSJiuyue Ma  override def resources = T.sources {
356ce34d21eSJiuyue Ma    os.write(T.dest / "publishVersion", publishVersion())
357844fba5bSXuan Hu    os.write(T.dest / "gitStatus", gitStatus())
3581825b5e5SJiuyue Ma    os.write(T.dest / "gitModules", os.proc("git", "submodule", "status").call().out.text())
359f6f8d1f7SJiuyue Ma    packDifftestResources(T.dest)
360ce34d21eSJiuyue Ma    super.resources() ++ Seq(PathRef(T.dest))
361ce34d21eSJiuyue Ma  }
362ce34d21eSJiuyue Ma
3633956160fSTang Haojin  object test extends SbtTests with TestModule.ScalaTest {
364831731b1STang Haojin    override def moduleDeps = super.moduleDeps ++ Seq(
365831731b1STang Haojin      difftestModule.test
366831731b1STang Haojin    )
367831731b1STang Haojin
3688c9adf0cSTang Haojin    override def forkArgs = forkArgsTask()
36951e45dbbSTang Haojin
370e3da8badSTang Haojin    override def scalacOptions = super.scalacOptions() ++ Agg("-deprecation", "-feature")
371e3da8badSTang Haojin
3723956160fSTang Haojin    val resourcesPATH = pwd.toString() + "/src/main/resources"
3734364f1d0SHaojin Tang    val envPATH = sys.env("PATH") + ":" + resourcesPATH
3744364f1d0SHaojin Tang
3754364f1d0SHaojin Tang    override def forkEnv = Map("PATH" -> envPATH)
376fc85214eSLinJiawei  }
3772905e463SZihao Yu}
378