Skip to content

Commit

Permalink
Rebootstrap on top of master to make use of updated GraphvizTools s…
Browse files Browse the repository at this point in the history
…ubprocess API (#3694)

Also optimize `GraphvizTools` a bunch to re-use the javascript engine
when batch processing lots of diagrams
  • Loading branch information
lihaoyi authored Oct 9, 2024
1 parent 9b84ae4 commit b4a0bfc
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .config/mill-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.12.0-RC3-26-d3afbf
0.12.0-RC3-31-9b84ae
2 changes: 1 addition & 1 deletion .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }

- run: ./mill -i docs.githubPages
- run: ./mill installLocal && ./target/mill-release -i docs.githubPages

linux:
needs: build-linux
Expand Down
16 changes: 11 additions & 5 deletions docs/package.mill
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ object `package` extends RootModule {
createFolders = true
)

Graphviz.useEngine(new AbstractJsGraphvizEngine(true, () => new mill.main.graphviz.V8JavascriptEngine()) {})

// Walk all files al render graphviz templates ourselves because the only Antora graphviz
// plugin (Kroki) relies on an online web service that is super slow and flaky
def walkAllFiles(inputs: Map[(os.Path, Int), String]): Map[(os.Path, Int), String] = {
val output = collection.mutable.Map.empty[(os.Path, Int), String]
for (p <- os.walk(T.dest) if p.ext == "adoc"){
Expand Down Expand Up @@ -125,14 +125,20 @@ object `package` extends RootModule {
val diagrams = walkAllFiles(Map())
// Batch the rendering so later it can be done in one call to a single subprocess,
// minimizing per-subprocess overhead needed to spawn them over and over
val renderedDiagrams = diagrams
.map{case (k, s) => (k, Graphviz.fromString(s).render(Format.SVG).toString)}
val orderedDiagrams = diagrams.toSeq.map{case ((p, i), s) => (p, i, os.temp(s), os.temp.dir())}

mill.util.Jvm.runSubprocess(
"mill.main.graphviz.GraphvizTools",
mill.main.VisualizeModule.classpath().map(_.path),
mainArgs = orderedDiagrams.map{case (p, i, src, dest) => s"$src;$dest;svg"}
)

walkAllFiles(renderedDiagrams)
walkAllFiles(orderedDiagrams.map{case (p, i, src, dest) => ((p, i), os.read(dest / "out.svg"))}.toMap)

PathRef(T.dest)
}


def supplementalFiles = T.source(millSourcePath / "supplemental-ui")

/**
Expand Down
14 changes: 12 additions & 2 deletions main/graphviz/src/mill/main/graphviz/GraphvizTools.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,33 @@ import org.slf4j.LoggerFactory
import org.slf4j.Logger

import java.util.concurrent.Executors
import guru.nidi.graphviz.engine.{Format, Graphviz}
import scala.concurrent.{Await, ExecutionContext, Future, duration}

object GraphvizTools {

def main(args: Array[String]): Unit = {
val executor = Executors.newFixedThreadPool(Runtime.getRuntime.availableProcessors())

val threadLocalJsEngines =
new java.util.concurrent.ConcurrentHashMap[Thread, V8JavascriptEngine]()
Graphviz.useEngine(
new AbstractJsGraphvizEngine(
true,
() => {
threadLocalJsEngines.putIfAbsent(Thread.currentThread(), new V8JavascriptEngine())
threadLocalJsEngines.get(Thread.currentThread())
}
) {}
)
try {
implicit val ec: ExecutionContext = ExecutionContext.fromExecutor(executor)
val futures =
for (arg <- args.toSeq) yield Future {
val Array(src, dest0, commaSepExtensions) = arg.split(";")
val extensions = commaSepExtensions.split(',')
val dest = os.Path(dest0)
import guru.nidi.graphviz.engine.{Format, Graphviz}

Graphviz.useEngine(new AbstractJsGraphvizEngine(true, () => new V8JavascriptEngine()) {})
val gv = Graphviz.fromFile(new java.io.File(src)).totalMemory(128 * 1024 * 1024)

val outputs = extensions
Expand Down

0 comments on commit b4a0bfc

Please sign in to comment.