Skip to content

Commit

Permalink
Eliminate circe-yaml dependency (#10326)
Browse files Browse the repository at this point in the history
* Eliminating circe-yaml

This change adds our very-own YAML parser on top of SnakeYAML. Compared
to Circe parser on top of SnakeYAML. The advantage? In some not-so-distant
future we might actually get rid of circe and the related performance
issues.

The logic is similar to what circe does i.e. analyzing SnakeYAML to
build our own structure.
This change is not complete, as there are still some tests failing, but
most common Configs are already parseable.
We _could_ auto-generate some of the code but still some of the logic
would have to be tweaked by hand; the current logic has a number of
special cases, as I found out the hard way.

* wip: more tests passing

* Fix remaining tests in ConfigSpec

* Fixing YAML decoder for editions

Dropping circe as a decoder for Editions revealed some problems. Turns
out the current implementation had even more special cases to deal with.

* nit

* Allow for empty exports

* Mostly complete encodin part

Replaced almost all `toYAML` locations with SnakeYAML equivalent.
The encoding has to use Java collections for which there exists a
built-in support. If we were to use Scala collections we would have to
deal with tagging, at the very least.

* Remove the last remaining Circe's YAML parser

* Bug fix + further loop optimization

* removal of some dependencies

* Remove circe-yaml

Added a custom SnakeYAML Node updater to mimick the JSON -> YAML -> JSON
conversion needed for updating fields. The algorithm recursively follows
the key-path and inserts the desired Node. This is not a performance
oriented code on purpose.

* Fix compilation issues

`circe-core` was marked as `provided` but no one eventually included it
in the final jar, hence `NoClassFoundException`.

* fix licensing

* Removing obsolete circe definitions

* fmt

* nits

* s/SnakeYamlDecoder/YamlDecoder

* fmt

* Partial revert, PM needs JSON decoders/encoders

* style

* incremental compilation gone wrong
  • Loading branch information
hubertp authored Jul 5, 2024
1 parent 0661f17 commit c54c3b7
Show file tree
Hide file tree
Showing 56 changed files with 1,979 additions and 851 deletions.
32 changes: 24 additions & 8 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ lazy val enso = (project in file("."))
`syntax-rust-definition`,
`text-buffer`,
yaml,
`scala-yaml`,
pkg,
cli,
`task-progress-notifications`,
Expand Down Expand Up @@ -417,10 +418,10 @@ val catsVersion = "2.9.0"
// === Circe ==================================================================

val circeVersion = "0.14.7"
val circeYamlVersion = "0.15.1"
val circeGenericExtrasVersion = "0.14.3"
val circe = Seq("circe-core", "circe-generic", "circe-parser")
.map("io.circe" %% _ % circeVersion)
val snakeyamlVersion = "2.2"

// === Commons ================================================================

Expand Down Expand Up @@ -751,7 +752,16 @@ lazy val yaml = (project in file("lib/java/yaml"))
frgaalJavaCompilerSetting,
version := "0.1",
libraryDependencies ++= Seq(
"io.circe" %% "circe-yaml" % circeYamlVersion % "provided"
"org.yaml" % "snakeyaml" % snakeyamlVersion % "provided"
)
)

lazy val `scala-yaml` = (project in file("lib/scala/yaml"))
.configs(Test)
.settings(
frgaalJavaCompilerSetting,
libraryDependencies ++= Seq(
"org.yaml" % "snakeyaml" % snakeyamlVersion % "provided"
)
)

Expand All @@ -762,7 +772,8 @@ lazy val pkg = (project in file("lib/scala/pkg"))
version := "0.1",
libraryDependencies ++= Seq(
"org.graalvm.truffle" % "truffle-api" % graalMavenPackagesVersion % "provided",
"io.circe" %% "circe-yaml" % circeYamlVersion % "provided",
"io.circe" %% "circe-core" % circeVersion % "provided",
"org.yaml" % "snakeyaml" % snakeyamlVersion % "provided",
"org.scalatest" %% "scalatest" % scalatestVersion % Test,
"org.apache.commons" % "commons-compress" % commonsCompressVersion
)
Expand Down Expand Up @@ -930,10 +941,12 @@ lazy val cli = project
version := "0.1",
libraryDependencies ++= circe ++ Seq(
"com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingVersion,
"org.yaml" % "snakeyaml" % snakeyamlVersion % "provided",
"org.scalatest" %% "scalatest" % scalatestVersion % Test
),
Test / parallelExecution := false
)
.dependsOn(`scala-yaml`)

lazy val `task-progress-notifications` = project
.in(file("lib/scala/task-progress-notifications"))
Expand Down Expand Up @@ -1461,11 +1474,11 @@ lazy val `polyglot-api` = project
"runtime-fat-jar"
) / Compile / fullClasspath).value,
libraryDependencies ++= Seq(
"io.circe" %% "circe-core" % circeVersion % "provided",
"org.graalvm.sdk" % "polyglot-tck" % graalMavenPackagesVersion % "provided",
"org.graalvm.truffle" % "truffle-api" % graalMavenPackagesVersion % "provided",
"com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % jsoniterVersion,
"com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % jsoniterVersion,
"io.circe" %% "circe-yaml" % circeYamlVersion % "provided", // as required by `pkg` and `editions`
"com.google.flatbuffers" % "flatbuffers-java" % flatbuffersVersion,
"org.scalatest" %% "scalatest" % scalatestVersion % Test,
"org.scalacheck" %% "scalacheck" % scalacheckVersion % Test
Expand Down Expand Up @@ -2764,7 +2777,7 @@ lazy val `distribution-manager` = project
resolvers += Resolver.bintrayRepo("gn0s1s", "releases"),
libraryDependencies ++= Seq(
"com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingVersion,
"io.circe" %% "circe-yaml" % circeYamlVersion,
"org.yaml" % "snakeyaml" % snakeyamlVersion,
"commons-io" % "commons-io" % commonsIoVersion,
"org.scalatest" %% "scalatest" % scalatestVersion % Test
)
Expand Down Expand Up @@ -2944,7 +2957,8 @@ lazy val editions = project
.settings(
frgaalJavaCompilerSetting,
libraryDependencies ++= Seq(
"io.circe" %% "circe-yaml" % circeYamlVersion % "provided",
"io.circe" %% "circe-core" % circeVersion % "provided",
"org.yaml" % "snakeyaml" % snakeyamlVersion % "provided",
"org.scalatest" %% "scalatest" % scalatestVersion % Test
)
)
Expand Down Expand Up @@ -2973,7 +2987,8 @@ lazy val semver = project
.settings(
frgaalJavaCompilerSetting,
libraryDependencies ++= Seq(
"io.circe" %% "circe-yaml" % circeYamlVersion % "provided",
"io.circe" %% "circe-core" % circeVersion % "provided",
"org.yaml" % "snakeyaml" % snakeyamlVersion % "provided",
"org.scalatest" %% "scalatest" % scalatestVersion % Test,
"junit" % "junit" % junitVersion % Test,
"com.github.sbt" % "junit-interface" % junitIfVersion % Test
Expand All @@ -2996,6 +3011,7 @@ lazy val semver = project
cleanFiles += baseDirectory.value / ".." / ".." / "distribution" / "editions"
)
.dependsOn(testkit % Test)
.dependsOn(`scala-yaml`)

lazy val downloader = (project in file("lib/scala/downloader"))
.settings(
Expand Down Expand Up @@ -3040,7 +3056,7 @@ lazy val `edition-uploader` = project
.settings(
frgaalJavaCompilerSetting,
libraryDependencies ++= Seq(
"io.circe" %% "circe-yaml" % circeYamlVersion % "provided"
"io.circe" %% "circe-core" % circeVersion % "provided"
)
)
.dependsOn(editions)
Expand Down
10 changes: 0 additions & 10 deletions distribution/engine/THIRD-PARTY/NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -211,16 +211,6 @@ The license file can be found at `licenses/APACHE2.0`.
Copyright notices related to this dependency can be found in the directory `io.circe.circe-parser_2.13-0.14.7`.


'circe-yaml-common_2.13', licensed under the Apache-2.0, is distributed with the engine.
The license file can be found at `licenses/APACHE2.0`.
Copyright notices related to this dependency can be found in the directory `io.circe.circe-yaml-common_2.13-0.15.1`.


'circe-yaml_2.13', licensed under the Apache-2.0, is distributed with the engine.
The license file can be found at `licenses/APACHE2.0`.
Copyright notices related to this dependency can be found in the directory `io.circe.circe-yaml_2.13-0.15.1`.


'helidon-builder-api', licensed under the Apache 2.0, is distributed with the engine.
The license file can be found at `licenses/APACHE2.0`.
Copyright notices related to this dependency can be found in the directory `io.helidon.builder.helidon-builder-api-4.0.8`.
Expand Down

This file was deleted.

This file was deleted.

10 changes: 0 additions & 10 deletions distribution/launcher/THIRD-PARTY/NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -86,16 +86,6 @@ The license file can be found at `licenses/APACHE2.0`.
Copyright notices related to this dependency can be found in the directory `io.circe.circe-parser_2.13-0.14.7`.


'circe-yaml-common_2.13', licensed under the Apache-2.0, is distributed with the launcher.
The license file can be found at `licenses/APACHE2.0`.
Copyright notices related to this dependency can be found in the directory `io.circe.circe-yaml-common_2.13-0.15.1`.


'circe-yaml_2.13', licensed under the Apache-2.0, is distributed with the launcher.
The license file can be found at `licenses/APACHE2.0`.
Copyright notices related to this dependency can be found in the directory `io.circe.circe-yaml_2.13-0.15.1`.


'commons-compress', licensed under the Apache-2.0, is distributed with the launcher.
The license file can be found at `licenses/APACHE2.0`.
Copyright notices related to this dependency can be found in the directory `org.apache.commons.commons-compress-1.23.0`.
Expand Down

This file was deleted.

This file was deleted.

10 changes: 0 additions & 10 deletions distribution/project-manager/THIRD-PARTY/NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -186,16 +186,6 @@ The license file can be found at `licenses/APACHE2.0`.
Copyright notices related to this dependency can be found in the directory `io.circe.circe-parser_2.13-0.14.7`.


'circe-yaml-common_2.13', licensed under the Apache-2.0, is distributed with the project-manager.
The license file can be found at `licenses/APACHE2.0`.
Copyright notices related to this dependency can be found in the directory `io.circe.circe-yaml-common_2.13-0.15.1`.


'circe-yaml_2.13', licensed under the Apache-2.0, is distributed with the project-manager.
The license file can be found at `licenses/APACHE2.0`.
Copyright notices related to this dependency can be found in the directory `io.circe.circe-yaml_2.13-0.15.1`.


'helidon-builder-api', licensed under the Apache 2.0, is distributed with the project-manager.
The license file can be found at `licenses/APACHE2.0`.
Copyright notices related to this dependency can be found in the directory `io.helidon.builder.helidon-builder-api-4.0.8`.
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package org.enso.launcher

import java.nio.file.Path
import com.typesafe.scalalogging.Logger
import io.circe.Json
import org.enso.semver.SemVer
import org.enso.distribution.config.DefaultVersion
import org.enso.editions.updater.EditionManager
Expand Down Expand Up @@ -368,7 +367,7 @@ case class Launcher(cliOptions: GlobalCLIOptions) {
s"(${configurationManager.configLocation.toAbsolutePath})."
)
} else {
configurationManager.updateConfigRaw(key, Json.fromString(value))
configurationManager.updateConfigRaw(key, value)
InfoLogger.info(
s"""Key `$key` set to "$value" in the global configuration file """ +
s"(${configurationManager.configLocation.toAbsolutePath})."
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package org.enso.launcher.releases.fallback.staticwebsite

import io.circe.Decoder
import org.enso.yaml.YamlDecoder
import org.yaml.snakeyaml.nodes.{MappingNode, Node}

import java.io.StringReader
import scala.util.Try

/** Manifest of the fallback mechanism.
Expand All @@ -13,6 +15,23 @@ case class FallbackManifest(enabled: Boolean)

object FallbackManifest {

implicit val yamlDecoder: YamlDecoder[FallbackManifest] =
new YamlDecoder[FallbackManifest] {
override def decode(node: Node) = {
node match {
case node: MappingNode =>
val booleanDecoder = implicitly[YamlDecoder[Boolean]]
val bindings = mappingKV(node)
for {
enabled <- bindings
.get(Fields.enabled)
.map(booleanDecoder.decode(_))
.getOrElse(Right(false))
} yield FallbackManifest(enabled)
}
}
}

/** Defines a part of the URL scheme of the fallback mechanism - the name of
* manifest file.
*
Expand All @@ -25,17 +44,10 @@ object FallbackManifest {
val enabled = "enabled"
}

/** [[Decoder]] instance for [[FallbackManifest]].
*
* It should always remain backwards compatible, since the fallback mechanism
* must work for all released launcher versions.
*/
implicit val decoder: Decoder[FallbackManifest] = { json =>
for {
enabled <- json.get[Boolean](Fields.enabled)
} yield FallbackManifest(enabled)
def parseString(yamlString: String): Try[FallbackManifest] = {
val snakeYaml = new org.yaml.snakeyaml.Yaml()
Try(snakeYaml.compose(new StringReader(yamlString))).toEither
.flatMap(implicitly[YamlDecoder[FallbackManifest]].decode(_))
.toTry
}

def parseString(string: String): Try[FallbackManifest] =
io.circe.yaml.parser.parse(string).flatMap(_.as[FallbackManifest]).toTry
}
Loading

0 comments on commit c54c3b7

Please sign in to comment.