mirror of
https://github.com/enso-org/enso.git
synced 2024-11-26 08:52:58 +03:00
Define Editions (#1797)
This commit is contained in:
parent
c819f42130
commit
241a1e7d74
7
.github/workflows/nightly.yml
vendored
7
.github/workflows/nightly.yml
vendored
@ -65,6 +65,13 @@ jobs:
|
|||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
path: repo
|
path: repo
|
||||||
|
- name: Configure Pagefile (Windows)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
uses: al-cheb/configure-pagefile-action@v1.2
|
||||||
|
with:
|
||||||
|
minimum-size: 16GB
|
||||||
|
maximum-size: 16GB
|
||||||
|
disk-root: "C:"
|
||||||
- name: Enable Developer Command Prompt (Windows)
|
- name: Enable Developer Command Prompt (Windows)
|
||||||
uses: ilammy/msvc-dev-cmd@v1.9.0
|
uses: ilammy/msvc-dev-cmd@v1.9.0
|
||||||
- name: Disable TCP/UDP Offloading (macOS)
|
- name: Disable TCP/UDP Offloading (macOS)
|
||||||
|
7
.github/workflows/release.yml
vendored
7
.github/workflows/release.yml
vendored
@ -32,6 +32,13 @@ jobs:
|
|||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
path: repo
|
path: repo
|
||||||
|
- name: Configure Pagefile (Windows)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
uses: al-cheb/configure-pagefile-action@v1.2
|
||||||
|
with:
|
||||||
|
minimum-size: 16GB
|
||||||
|
maximum-size: 16GB
|
||||||
|
disk-root: "C:"
|
||||||
- name: Enable Developer Command Prompt (Windows)
|
- name: Enable Developer Command Prompt (Windows)
|
||||||
uses: ilammy/msvc-dev-cmd@v1.9.0
|
uses: ilammy/msvc-dev-cmd@v1.9.0
|
||||||
- name: Disable TCP/UDP Offloading (macOS)
|
- name: Disable TCP/UDP Offloading (macOS)
|
||||||
|
7
.github/workflows/scala.yml
vendored
7
.github/workflows/scala.yml
vendored
@ -27,6 +27,13 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
- name: Configure Pagefile (Windows)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
uses: al-cheb/configure-pagefile-action@v1.2
|
||||||
|
with:
|
||||||
|
minimum-size: 16GB
|
||||||
|
maximum-size: 16GB
|
||||||
|
disk-root: "C:"
|
||||||
- name: Enable Developer Command Prompt (Windows)
|
- name: Enable Developer Command Prompt (Windows)
|
||||||
uses: ilammy/msvc-dev-cmd@v1.9.0
|
uses: ilammy/msvc-dev-cmd@v1.9.0
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
|
12
RELEASES.md
12
RELEASES.md
@ -39,6 +39,18 @@
|
|||||||
- Added support for multiple content roots in the language server
|
- Added support for multiple content roots in the language server
|
||||||
([#1800](https://github.com/enso-org/enso/pull/1800/)). It is not yet exposed
|
([#1800](https://github.com/enso-org/enso/pull/1800/)). It is not yet exposed
|
||||||
to the IDE, as this will be done as part of future work.
|
to the IDE, as this will be done as part of future work.
|
||||||
|
- Modified the `package.yaml` format in preparation for the library ecosystem
|
||||||
|
([#1797](https://github.com/enso-org/enso/pull/1797)). The `engine-version`
|
||||||
|
field has been deprecated in favour of an `edition` field that allows to set
|
||||||
|
up the engine version and dependency resolution using the upcoming Edition
|
||||||
|
system. New tools will still be able to read the old format, but upon
|
||||||
|
modification, they will save changes in the new format. As the `edition` file
|
||||||
|
did not exist in the older version, old tools will actually correctly load the
|
||||||
|
migrated package file (as we allow for unknown fields), but they will not know
|
||||||
|
how to interpret the new `edition` field and so will fall back to using the
|
||||||
|
`default` engine version, which may be unexpected. Ideally, after migration,
|
||||||
|
the project should be used only with the new tools. The affected tools are the
|
||||||
|
Launcher and the Project Manager.
|
||||||
|
|
||||||
## Libraries
|
## Libraries
|
||||||
|
|
||||||
|
56
build.sbt
56
build.sbt
@ -583,11 +583,11 @@ lazy val pkg = (project in file("lib/scala/pkg"))
|
|||||||
version := "0.1",
|
version := "0.1",
|
||||||
libraryDependencies ++= circe ++ Seq(
|
libraryDependencies ++= circe ++ Seq(
|
||||||
"org.scalatest" %% "scalatest" % scalatestVersion % Test,
|
"org.scalatest" %% "scalatest" % scalatestVersion % Test,
|
||||||
"nl.gn0s1s" %% "bump" % bumpVersion,
|
|
||||||
"io.circe" %% "circe-yaml" % circeYamlVersion, // separate from other circe deps because its independent project with its own versioning
|
"io.circe" %% "circe-yaml" % circeYamlVersion, // separate from other circe deps because its independent project with its own versioning
|
||||||
"commons-io" % "commons-io" % commonsIoVersion
|
"commons-io" % "commons-io" % commonsIoVersion
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
.dependsOn(editions)
|
||||||
|
|
||||||
lazy val `akka-native` = project
|
lazy val `akka-native` = project
|
||||||
.in(file("lib/scala/akka-native"))
|
.in(file("lib/scala/akka-native"))
|
||||||
@ -728,9 +728,12 @@ lazy val `project-manager` = (project in file("lib/scala/project-manager"))
|
|||||||
)
|
)
|
||||||
.dependsOn(`akka-native`)
|
.dependsOn(`akka-native`)
|
||||||
.dependsOn(`version-output`)
|
.dependsOn(`version-output`)
|
||||||
.dependsOn(pkg)
|
.dependsOn(editions)
|
||||||
|
.dependsOn(cli)
|
||||||
.dependsOn(`polyglot-api`)
|
.dependsOn(`polyglot-api`)
|
||||||
.dependsOn(`runtime-version-manager`)
|
.dependsOn(`runtime-version-manager`)
|
||||||
|
.dependsOn(`library-manager`)
|
||||||
|
.dependsOn(pkg)
|
||||||
.dependsOn(`json-rpc-server`)
|
.dependsOn(`json-rpc-server`)
|
||||||
.dependsOn(`json-rpc-server-test` % Test)
|
.dependsOn(`json-rpc-server-test` % Test)
|
||||||
.dependsOn(testkit % Test)
|
.dependsOn(testkit % Test)
|
||||||
@ -883,6 +886,7 @@ lazy val `polyglot-api` = project
|
|||||||
.dependsOn(pkg)
|
.dependsOn(pkg)
|
||||||
.dependsOn(`text-buffer`)
|
.dependsOn(`text-buffer`)
|
||||||
.dependsOn(`logging-utils`)
|
.dependsOn(`logging-utils`)
|
||||||
|
.dependsOn(testkit % Test)
|
||||||
|
|
||||||
lazy val `language-server` = (project in file("engine/language-server"))
|
lazy val `language-server` = (project in file("engine/language-server"))
|
||||||
.settings(
|
.settings(
|
||||||
@ -1184,8 +1188,53 @@ lazy val launcher = project
|
|||||||
.dependsOn(`version-output`)
|
.dependsOn(`version-output`)
|
||||||
.dependsOn(pkg)
|
.dependsOn(pkg)
|
||||||
.dependsOn(`logging-service`)
|
.dependsOn(`logging-service`)
|
||||||
|
.dependsOn(`distribution-manager` % Test)
|
||||||
.dependsOn(`runtime-version-manager-test` % Test)
|
.dependsOn(`runtime-version-manager-test` % Test)
|
||||||
|
|
||||||
|
lazy val `distribution-manager` = project
|
||||||
|
.in(file("lib/scala/distribution-manager"))
|
||||||
|
.configs(Test)
|
||||||
|
.settings(
|
||||||
|
resolvers += Resolver.bintrayRepo("gn0s1s", "releases"),
|
||||||
|
libraryDependencies ++= Seq(
|
||||||
|
"com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingVersion,
|
||||||
|
"io.circe" %% "circe-yaml" % circeYamlVersion,
|
||||||
|
"commons-io" % "commons-io" % commonsIoVersion,
|
||||||
|
"org.scalatest" %% "scalatest" % scalatestVersion % Test
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.dependsOn(editions)
|
||||||
|
.dependsOn(pkg)
|
||||||
|
.dependsOn(`logging-utils`)
|
||||||
|
|
||||||
|
lazy val editions = project
|
||||||
|
.in(file("lib/scala/editions"))
|
||||||
|
.configs(Test)
|
||||||
|
.settings(
|
||||||
|
resolvers += Resolver.bintrayRepo("gn0s1s", "releases"),
|
||||||
|
libraryDependencies ++= Seq(
|
||||||
|
"com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingVersion,
|
||||||
|
"nl.gn0s1s" %% "bump" % bumpVersion,
|
||||||
|
"io.circe" %% "circe-yaml" % circeYamlVersion,
|
||||||
|
"org.scalatest" %% "scalatest" % scalatestVersion % Test
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
lazy val `library-manager` = project
|
||||||
|
.in(file("lib/scala/library-manager"))
|
||||||
|
.configs(Test)
|
||||||
|
.settings(
|
||||||
|
resolvers += Resolver.bintrayRepo("gn0s1s", "releases"),
|
||||||
|
libraryDependencies ++= Seq(
|
||||||
|
"com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingVersion,
|
||||||
|
"org.scalatest" %% "scalatest" % scalatestVersion % Test
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.dependsOn(editions)
|
||||||
|
.dependsOn(cli)
|
||||||
|
.dependsOn(`distribution-manager`)
|
||||||
|
.dependsOn(testkit % Test)
|
||||||
|
|
||||||
lazy val `runtime-version-manager` = project
|
lazy val `runtime-version-manager` = project
|
||||||
.in(file("lib/scala/runtime-version-manager"))
|
.in(file("lib/scala/runtime-version-manager"))
|
||||||
.configs(Test)
|
.configs(Test)
|
||||||
@ -1204,6 +1253,7 @@ lazy val `runtime-version-manager` = project
|
|||||||
.dependsOn(`logging-service`)
|
.dependsOn(`logging-service`)
|
||||||
.dependsOn(cli)
|
.dependsOn(cli)
|
||||||
.dependsOn(`version-output`)
|
.dependsOn(`version-output`)
|
||||||
|
.dependsOn(`distribution-manager`)
|
||||||
|
|
||||||
lazy val `runtime-version-manager-test` = project
|
lazy val `runtime-version-manager-test` = project
|
||||||
.in(file("lib/scala/runtime-version-manager-test"))
|
.in(file("lib/scala/runtime-version-manager-test"))
|
||||||
@ -1224,6 +1274,8 @@ lazy val `runtime-version-manager-test` = project
|
|||||||
.dependsOn(`runtime-version-manager`)
|
.dependsOn(`runtime-version-manager`)
|
||||||
.dependsOn(`logging-service`)
|
.dependsOn(`logging-service`)
|
||||||
.dependsOn(testkit)
|
.dependsOn(testkit)
|
||||||
|
.dependsOn(cli)
|
||||||
|
.dependsOn(`distribution-manager`)
|
||||||
|
|
||||||
lazy val `locking-test-helper` = project
|
lazy val `locking-test-helper` = project
|
||||||
.in(file("lib/scala/locking-test-helper"))
|
.in(file("lib/scala/locking-test-helper"))
|
||||||
|
@ -26,6 +26,11 @@ The license information can be found along with the copyright notices.
|
|||||||
Copyright notices related to this dependency can be found in the directory `com.thoughtworks.paranamer.paranamer-2.8`.
|
Copyright notices related to this dependency can be found in the directory `com.thoughtworks.paranamer.paranamer-2.8`.
|
||||||
|
|
||||||
|
|
||||||
|
'slf4j-api', licensed under the MIT License, is distributed with the engine.
|
||||||
|
The license file can be found at `licenses/MIT`.
|
||||||
|
Copyright notices related to this dependency can be found in the directory `org.slf4j.slf4j-api-1.7.26`.
|
||||||
|
|
||||||
|
|
||||||
'izumi-reflect_2.13', licensed under the BSD-style, is distributed with the engine.
|
'izumi-reflect_2.13', licensed under the BSD-style, is distributed with the engine.
|
||||||
The license information can be found along with the copyright notices.
|
The license information can be found along with the copyright notices.
|
||||||
Copyright notices related to this dependency can be found in the directory `dev.zio.izumi-reflect_2.13-1.0.0-M5`.
|
Copyright notices related to this dependency can be found in the directory `dev.zio.izumi-reflect_2.13-1.0.0-M5`.
|
||||||
@ -246,11 +251,6 @@ The license file can be found at `licenses/APACHE2.0`.
|
|||||||
Copyright notices related to this dependency can be found in the directory `com.typesafe.scala-logging.scala-logging_2.13-3.9.2`.
|
Copyright notices related to this dependency can be found in the directory `com.typesafe.scala-logging.scala-logging_2.13-3.9.2`.
|
||||||
|
|
||||||
|
|
||||||
'slf4j-api', licensed under the MIT License, is distributed with the engine.
|
|
||||||
The license file can be found at `licenses/MIT`.
|
|
||||||
Copyright notices related to this dependency can be found in the directory `org.slf4j.slf4j-api-1.7.25`.
|
|
||||||
|
|
||||||
|
|
||||||
'commons-cli', licensed under the Apache License, Version 2.0, is distributed with the engine.
|
'commons-cli', licensed under the Apache License, Version 2.0, is distributed with the engine.
|
||||||
The license file can be found at `licenses/APACHE2.0`.
|
The license file can be found at `licenses/APACHE2.0`.
|
||||||
Copyright notices related to this dependency can be found in the directory `commons-cli.commons-cli-1.4`.
|
Copyright notices related to this dependency can be found in the directory `commons-cli.commons-cli-1.4`.
|
||||||
|
@ -1,19 +1,3 @@
|
|||||||
/*
|
|
||||||
* Copyright 2008 Google LLC
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2013 Google LLC
|
* Copyright 2013 Google LLC
|
||||||
*
|
*
|
||||||
@ -43,3 +27,19 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2008 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2014 The Error Prone Authors.
|
* Copyright 2016 The Error Prone Authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2016 The Error Prone Authors.
|
* Copyright 2015 The Error Prone Authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -47,7 +47,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2015 The Error Prone Authors.
|
* Copyright 2014 The Error Prone Authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2009-2020 Lightbend Inc. <https://www.lightbend.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2017-2020 Lightbend Inc. <https://www.lightbend.com>
|
* Copyright (C) 2017-2020 Lightbend Inc. <https://www.lightbend.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2020 Lightbend Inc. <https://www.lightbend.com>
|
||||||
|
*/
|
||||||
|
@ -15,8 +15,6 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Copyright 2017-2019 John A. De Goes and the ZIO Contributors
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2017-2020 John A. De Goes and the ZIO Contributors
|
* Copyright 2017-2020 John A. De Goes and the ZIO Contributors
|
||||||
* Copyright 2017-2018 Łukasz Biały, Paul Chiusano, Michael Pilquist,
|
* Copyright 2017-2018 Łukasz Biały, Paul Chiusano, Michael Pilquist,
|
||||||
@ -36,3 +34,5 @@ Copyright 2017-2019 John A. De Goes and the ZIO Contributors
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
Copyright 2017-2019 John A. De Goes and the ZIO Contributors
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2011,2012 Mathias Doenitz, Johannes Rudolph
|
* Copyright (C) 2011 Mathias Doenitz
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2011 Mathias Doenitz
|
* Copyright (C) 2011,2012 Mathias Doenitz, Johannes Rudolph
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2009-2020 Lightbend Inc. <https://www.lightbend.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2017-2020 Lightbend Inc. <https://www.lightbend.com>
|
* Copyright (C) 2017-2020 Lightbend Inc. <https://www.lightbend.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2020 Lightbend Inc. <https://www.lightbend.com>
|
||||||
|
*/
|
||||||
|
@ -15,8 +15,6 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Copyright 2017-2019 John A. De Goes and the ZIO Contributors
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2017-2020 John A. De Goes and the ZIO Contributors
|
* Copyright 2017-2020 John A. De Goes and the ZIO Contributors
|
||||||
* Copyright 2017-2018 Łukasz Biały, Paul Chiusano, Michael Pilquist,
|
* Copyright 2017-2018 Łukasz Biały, Paul Chiusano, Michael Pilquist,
|
||||||
@ -36,3 +34,5 @@ Copyright 2017-2019 John A. De Goes and the ZIO Contributors
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
Copyright 2017-2019 John A. De Goes and the ZIO Contributors
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2011,2012 Mathias Doenitz, Johannes Rudolph
|
* Copyright (C) 2011 Mathias Doenitz
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2011 Mathias Doenitz
|
* Copyright (C) 2011,2012 Mathias Doenitz, Johannes Rudolph
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -47,3 +47,4 @@ It is broken up into categories as follows:
|
|||||||
- [**Parser:**](./parser) Design and specification of the Enso parser.
|
- [**Parser:**](./parser) Design and specification of the Enso parser.
|
||||||
- [**Infrastructure:**](./infrastructure) Description of the infrastructure for
|
- [**Infrastructure:**](./infrastructure) Description of the infrastructure for
|
||||||
building Enso.
|
building Enso.
|
||||||
|
- [**Libraries:**](./libraries) Description of Enso's Library Ecosystem.
|
||||||
|
@ -20,7 +20,7 @@ structured and how it should behave.
|
|||||||
- [Installing from a Portable Distribution](#installing-from-a-portable-distribution)
|
- [Installing from a Portable Distribution](#installing-from-a-portable-distribution)
|
||||||
- [Layout of an Enso Version Package](#layout-of-an-enso-version-package)
|
- [Layout of an Enso Version Package](#layout-of-an-enso-version-package)
|
||||||
- [Standard Library](#standard-library)
|
- [Standard Library](#standard-library)
|
||||||
- [Resolvers](#resolvers)
|
- [Enso Home Layout](#enso-home-layout)
|
||||||
|
|
||||||
<!-- /MarkdownTOC -->
|
<!-- /MarkdownTOC -->
|
||||||
|
|
||||||
@ -64,19 +64,19 @@ extraction-location
|
|||||||
│ └── 1.2.0 # A full distribution of given Enso version, described below.
|
│ └── 1.2.0 # A full distribution of given Enso version, described below.
|
||||||
│ └── <truncated>
|
│ └── <truncated>
|
||||||
├── runtime # A directory storing distributions of the JVM used by the Enso distributions.
|
├── runtime # A directory storing distributions of the JVM used by the Enso distributions.
|
||||||
│ └── graalvm-ce-27.1.1
|
│ └── graalvm-ce-java11-27.1.1
|
||||||
├── lib
|
├── lib # Contains sources of downloaded libraries.
|
||||||
│ └── src # Contains sources of downloaded libraries.
|
│ └── Standard # Each prefix (usually corresponding to the author) is placed in a separate directory.
|
||||||
│ └── Dataframe # Each library may be stored in multiple version.
|
│ └── Dataframe # Each library may be stored in multiple versions.
|
||||||
│ └── 1.7.0 # Each version contains a standard Enso package.
|
│ └── 1.7.0 # Each version contains a standard Enso package.
|
||||||
│ ├── package.yaml
|
│ ├── package.yaml
|
||||||
│ └── src
|
│ └── src
|
||||||
│ ├── List.enso
|
│ ├── List.enso
|
||||||
│ ├── Number.enso
|
│ ├── Number.enso
|
||||||
│ └── Text.enso
|
│ └── Text.enso
|
||||||
├── resolvers # Contains resolver specifications, described below.
|
├── editions # Contains Edition specifications.
|
||||||
│ ├── lts-1.56.7.yaml
|
│ ├── 2021.4.yaml
|
||||||
│ └── lts-2.0.8.yaml
|
│ └── nightly-2021-06-31.yaml
|
||||||
├── README.md # Information on layout and usage of the Enso distribution.
|
├── README.md # Information on layout and usage of the Enso distribution.
|
||||||
├── .enso.portable # A file that allows the universal launcher to detect that if it is run from this directory, it should run in portable distribution mode.
|
├── .enso.portable # A file that allows the universal launcher to detect that if it is run from this directory, it should run in portable distribution mode.
|
||||||
└── THIRD-PARTY # Contains licences of distributed components, including the NOTICE file.
|
└── THIRD-PARTY # Contains licences of distributed components, including the NOTICE file.
|
||||||
@ -94,19 +94,19 @@ ENSO_DATA_DIRECTORY
|
|||||||
│ └── 1.2.0 # A full distribution of given Enso version, described below.
|
│ └── 1.2.0 # A full distribution of given Enso version, described below.
|
||||||
│ └── <truncated>
|
│ └── <truncated>
|
||||||
├── runtime # A directory storing (optional) distributions of the JVM used by the Enso distributions.
|
├── runtime # A directory storing (optional) distributions of the JVM used by the Enso distributions.
|
||||||
│ └── graalvm-ce-27.1.1
|
│ └── graalvm-ce-java11-27.1.1
|
||||||
├── lib
|
├── lib # Contains sources of downloaded libraries.
|
||||||
│ └── src # Contains sources of downloaded libraries.
|
│ └── Standard # Each prefix (usually corresponding to the author) is placed in a separate directory.
|
||||||
│ └── Dataframe # Each library may be stored in multiple version.
|
│ └── Dataframe # Each library may be stored in multiple versions.
|
||||||
│ └── 1.7.0 # Each version contains a standard Enso package.
|
│ └── 1.7.0 # Each version contains a standard Enso package.
|
||||||
│ ├── package.yaml
|
│ ├── package.yaml
|
||||||
│ └── src
|
│ └── src
|
||||||
│ ├── List.enso
|
│ ├── List.enso
|
||||||
│ ├── Number.enso
|
│ ├── Number.enso
|
||||||
│ └── Text.enso
|
│ └── Text.enso
|
||||||
└── resolvers # Contains resolver specifications, described below.
|
└── editions # Contains Edition specifications.
|
||||||
├── lts-1.56.7.yaml
|
├── 2021.4.yaml
|
||||||
└── lts-2.0.8.yaml
|
└── nightly-2021-06-31.yaml
|
||||||
|
|
||||||
ENSO_CONFIG_DIRECTORY
|
ENSO_CONFIG_DIRECTORY
|
||||||
└── global-config.yaml # Global user configuration.
|
└── global-config.yaml # Global user configuration.
|
||||||
@ -202,20 +202,19 @@ but the following are some guidelines:
|
|||||||
3. Packages that the compiler relies on, e.g. compile error definitions, stack
|
3. Packages that the compiler relies on, e.g. compile error definitions, stack
|
||||||
traces etc.
|
traces etc.
|
||||||
|
|
||||||
### Resolvers
|
## Enso Home Layout
|
||||||
|
|
||||||
**Note** This system is not implemented yet.
|
The location called in some places `<ENSO_HOME>` is the place where user's
|
||||||
|
projects and similar files are stored. Currently it is specified to always be
|
||||||
|
`$HOME/enso`.
|
||||||
|
|
||||||
A resolver is a manifest containing library versions that are automatically
|
It has the following structure:
|
||||||
available for import in any project using the resolver.
|
|
||||||
|
|
||||||
Example contents of a resolver file are as follows:
|
```
|
||||||
|
<ENSO_HOME>
|
||||||
```yaml
|
├── projects # Contains all user projects.
|
||||||
enso-version: 1.0.7
|
├── libraries # Contains all local libraries that can be edited by the user.
|
||||||
libraries:
|
│ └── Prefix1 # Contains libraries with the given prefix.
|
||||||
- name: Base
|
│ └── Library_Name # Contains a package of a local library.
|
||||||
version: 1.0.0
|
└── editions # Contains custom, user-defined editions that can be used as a base for project configurations.
|
||||||
- name: Http
|
|
||||||
version: 5.3.5
|
|
||||||
```
|
```
|
||||||
|
@ -102,19 +102,20 @@ The following is an example of this manifest file.
|
|||||||
license: MIT
|
license: MIT
|
||||||
name: My_Package
|
name: My_Package
|
||||||
version: 1.0.1
|
version: 1.0.1
|
||||||
enso-version: 0.2.0
|
edition:
|
||||||
|
extends: 2021.3
|
||||||
|
enso-version: 0.2.12
|
||||||
|
libraries:
|
||||||
|
- name: Foo.Bar
|
||||||
|
version: 1.2.3
|
||||||
|
repository: main
|
||||||
|
prefer-local-libraries: false
|
||||||
authors:
|
authors:
|
||||||
- name: John Doe
|
- name: John Doe
|
||||||
email: john.doe@example.com
|
email: john.doe@example.com
|
||||||
maintainers:
|
maintainers:
|
||||||
- name: Jane Doe
|
- name: Jane Doe
|
||||||
email: jane.doe@example.com
|
email: jane.doe@example.com
|
||||||
resolver: lts-1.2.0
|
|
||||||
extra-dependencies:
|
|
||||||
- name: Base
|
|
||||||
version: "1.2.0"
|
|
||||||
- name: Http
|
|
||||||
version: "4.5.3"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The following is the specification of the manifest fields. Fields marked as
|
The following is the specification of the manifest fields. Fields marked as
|
||||||
@ -129,12 +130,37 @@ fields cannot be published.
|
|||||||
package. Defaults to `None`, meaning the package is not safe for use by third
|
package. Defaults to `None`, meaning the package is not safe for use by third
|
||||||
parties.
|
parties.
|
||||||
|
|
||||||
|
#### edition
|
||||||
|
|
||||||
|
**Optional (required for publishing)** _Edition_: Defines the Edition settings
|
||||||
|
of the package that determine the engine version and library resolution
|
||||||
|
settings. It is a sub-setting that can consist of multiple fields, see
|
||||||
|
[the Edition documentation](../libraries/editions.md#the-edition-file) for the
|
||||||
|
format description.
|
||||||
|
|
||||||
|
The field was added in version 0.2.12 as a replacement for `enso-version` as it
|
||||||
|
supersedes its functionality.
|
||||||
|
|
||||||
|
If the `edition` field is not specified, a default edition is used.
|
||||||
|
|
||||||
#### enso-version
|
#### enso-version
|
||||||
|
|
||||||
**Optional (required for publishing)** _String_: Specifies the Enso version that
|
**Deprecated** _String_: Specifies the Enso version that should be used for this
|
||||||
should be used for this project. If not set or set to `default`, the default
|
project. If not set or set to `default`, the default locally installed Enso
|
||||||
locally installed Enso version will be used. The version should not be `default`
|
version will be used.
|
||||||
if the package is to be published.
|
|
||||||
|
The field was deprecated in version 0.2.12. Currently it is still supported, but
|
||||||
|
the newer tools will migrate it to the `edition` format when the config is
|
||||||
|
modified.
|
||||||
|
|
||||||
|
If old tools see a config file that includes an `edition` setting but does not
|
||||||
|
include the `engine-version` (for example after the migration), they will fall
|
||||||
|
back to using the default engine version - that is because old tools were not
|
||||||
|
aware of the `edition` field, so they will simply ignore it.
|
||||||
|
|
||||||
|
If a config defines the `edition` field it should not define the
|
||||||
|
`engine-version` field anymore, as that could lead to inconsistent engine
|
||||||
|
version settings.
|
||||||
|
|
||||||
#### version
|
#### version
|
||||||
|
|
||||||
@ -163,32 +189,18 @@ present.
|
|||||||
**Optional** _List of contacts_: The name(s) and contact info(s) of the current
|
**Optional** _List of contacts_: The name(s) and contact info(s) of the current
|
||||||
maintainer(s) of this library, in the same format as `authors` above.
|
maintainer(s) of this library, in the same format as `authors` above.
|
||||||
|
|
||||||
#### resolver
|
#### prefer-local-libraries
|
||||||
|
|
||||||
**Note** This field is not currently implemented. **Optional (required for
|
**Optional** _Boolean_: A flag that tells the library resolver to prefer local
|
||||||
publishing)** _String_: The resolver name, used to choose compiler version and
|
library versions over the ones specified by the edition configuration. This is
|
||||||
basic libraries set. If not set, the system-default resolver will be used.
|
useful to make all local libraries easily accessible, but in more sophisticated
|
||||||
|
scenarios individual `local` repository overrides should be used instead of
|
||||||
|
that. See [Library Resolution](../libraries/editions.md#library-resolution) for
|
||||||
|
more details.
|
||||||
|
|
||||||
> The actionables for this section are:
|
If the flag is not specified, it defaults to `false`, delegating all library
|
||||||
>
|
resolution to the edition configuration. However, newly created projects will
|
||||||
> - Extend the compiler version to handle version bounds.
|
have it set to `true`.
|
||||||
|
|
||||||
#### extra-dependencies
|
|
||||||
|
|
||||||
**Note** This field is not currently implemented. **Optional** _List of Library
|
|
||||||
objects_: The list of libraries this package requires to function properly and
|
|
||||||
that are not included in the resolver. Defaults to an empty list.
|
|
||||||
|
|
||||||
A library object is of the form:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
name: <name of the library>
|
|
||||||
version: <semver string of the required library version>
|
|
||||||
```
|
|
||||||
|
|
||||||
> The actionables for this section are:
|
|
||||||
>
|
|
||||||
> - Extend the library version field to handle version bounds.
|
|
||||||
|
|
||||||
### The `visualization` Directory
|
### The `visualization` Directory
|
||||||
|
|
||||||
|
14
docs/libraries/README.md
Normal file
14
docs/libraries/README.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
layout: section-summary
|
||||||
|
title: Enso Library Ecosystem
|
||||||
|
category: libraries
|
||||||
|
tags: [libraries, readme]
|
||||||
|
order: 0
|
||||||
|
---
|
||||||
|
|
||||||
|
# Enso Library Ecosystem
|
||||||
|
|
||||||
|
Documents in this section describe Enso's library ecosystem.
|
||||||
|
|
||||||
|
- [**Editions:**](./editions.md) Information on Editions, the concept that
|
||||||
|
organizes library versioning.
|
217
docs/libraries/editions.md
Normal file
217
docs/libraries/editions.md
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
---
|
||||||
|
layout: developer-doc
|
||||||
|
title: Editions
|
||||||
|
category: libraries
|
||||||
|
tags: [libraries, editions]
|
||||||
|
order: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
# Editions
|
||||||
|
|
||||||
|
This document describes the concept of Editions.
|
||||||
|
|
||||||
|
<!-- MarkdownTOC levels="2,3" autolink="true" -->
|
||||||
|
|
||||||
|
- [What Is An Edition](#what-is-an-edition)
|
||||||
|
- [The Edition File](#the-edition-file)
|
||||||
|
- [Repositories](#repositories)
|
||||||
|
- [Libraries](#libraries)
|
||||||
|
- [Extending the Editions](#extending-the-editions)
|
||||||
|
- [An Example Configuration](#an-example-configuration)
|
||||||
|
- [Edition Resolution](#edition-resolution)
|
||||||
|
- [Updating the Editions](#updating-the-editions)
|
||||||
|
- [Library Resolution](#library-resolution)
|
||||||
|
|
||||||
|
<!-- /MarkdownTOC -->
|
||||||
|
|
||||||
|
## What Is An Edition
|
||||||
|
|
||||||
|
An Edition, is in principle, a list of library versions that should be
|
||||||
|
compatible with each other. An Edition specifies the engine version and a set of
|
||||||
|
library versions that can be used together.
|
||||||
|
|
||||||
|
If a library included in an Edition depends on another library, that other
|
||||||
|
library must also be included in that Edition and the version at which it is
|
||||||
|
included must be compatible with the library that depends on it. Each Edition
|
||||||
|
may only include a single version of each library.
|
||||||
|
|
||||||
|
Thus, when a library is to be installed, its version is uniquely determined by
|
||||||
|
the selected Edition. A curated Edition file will guarantee that all libraries
|
||||||
|
are compatible which simplifies version resolution - the exact version that is
|
||||||
|
specified in the Edition is always used.
|
||||||
|
|
||||||
|
## The Edition File
|
||||||
|
|
||||||
|
The Edition file is a YAML file that can contain the following fields:
|
||||||
|
|
||||||
|
- `engine-version` which should be a semantic versioning string specifying the
|
||||||
|
engine version that should be associated with that edition,
|
||||||
|
- `repositories` which defines the repositories which are sources of library
|
||||||
|
packages, its format is [described below](#repositories),
|
||||||
|
- `extends` which can contain a name of another Edition that this Edition
|
||||||
|
extends,
|
||||||
|
- `libraries` which defines the libraries that this Edition should include, its
|
||||||
|
format is [described below](#libraries).
|
||||||
|
|
||||||
|
Every field is optional, but for an Edition file to be valid it must specify at
|
||||||
|
least the engine version to be used (either by specifying it directly or
|
||||||
|
extending another edition that specifies it).
|
||||||
|
|
||||||
|
### Repositories
|
||||||
|
|
||||||
|
The `repositories` field is a list of repository objects.
|
||||||
|
|
||||||
|
Each object must have:
|
||||||
|
|
||||||
|
- a `name` field which specifies the name under which this repository will be
|
||||||
|
referred to in the rest of the file,
|
||||||
|
- a `url` field which specifies the URL of the root of that repository.
|
||||||
|
|
||||||
|
The `name` can be any string which only needs to be consistent with the names
|
||||||
|
used in the package definitions. The only reserved name is `local`, which is a
|
||||||
|
special repository name, as [explained below](#library-resolution).
|
||||||
|
|
||||||
|
### Libraries
|
||||||
|
|
||||||
|
The `libraries` field defines the set of libraries included in the edition.
|
||||||
|
|
||||||
|
Each library is represented by an object that must have:
|
||||||
|
|
||||||
|
- a `name` field which is the fully qualified name of the library (consisting of
|
||||||
|
its prefix and the name itself),
|
||||||
|
- a `repository` field which specifies which repository this package should be
|
||||||
|
downloaded from. The `repository` field should refer to the `name` of one of
|
||||||
|
the repositories defined in the edition or to `local`,
|
||||||
|
- a `version` field which specifies which exact package version should be used
|
||||||
|
when the library is imported; it is normally required, but if the `repository`
|
||||||
|
is set to `local`, the version must not be specified as the version will only
|
||||||
|
depend on what is available in the local repository,
|
||||||
|
- an optional `hash` that can be included to verify the integrity of the
|
||||||
|
package.
|
||||||
|
|
||||||
|
The `hash` field is currently not implemented.
|
||||||
|
|
||||||
|
### Extending the Editions
|
||||||
|
|
||||||
|
An edition may extend another one by using the `extends` property specifying the
|
||||||
|
name of the edition that is to be extended. Henceforth we will call the edition
|
||||||
|
that is being extended 'the parent edition' and the other one 'the local
|
||||||
|
edition'.
|
||||||
|
|
||||||
|
The current edition inherits all configuration of the parent edition, but it can
|
||||||
|
also override specific settings.
|
||||||
|
|
||||||
|
If the `engine-version` is specified in the current edition, it overrides the
|
||||||
|
engine version that was implied from the parent edition.
|
||||||
|
|
||||||
|
If the current edition specifies its libraries, they are added to the set of
|
||||||
|
available libraries defined by the parent edition. If the current edition
|
||||||
|
defines a library that has the same fully qualified name as a library that was
|
||||||
|
already defined in the parent edition, the definition from the current edition
|
||||||
|
takes precedence. This is the most important mechanism of extending editions
|
||||||
|
that allows to override library settings.
|
||||||
|
|
||||||
|
The libraries defined in the current edition can refer to the repositories
|
||||||
|
defined both in the current edition and in the parent. However, if the current
|
||||||
|
edition defines a repository with the same name as some repository defined in
|
||||||
|
the parent edition, the definition from the current edition takes precedence for
|
||||||
|
the package definitions of the current definition, **but** the package
|
||||||
|
definitions in the parent edition are not affected (they still refer to the
|
||||||
|
definition from the their own edition). So you can shadow a repository name, but
|
||||||
|
you cannot override it for libraries from the parent edition - instead libraries
|
||||||
|
whose repository should be changed must all be overridden in the current
|
||||||
|
edition.
|
||||||
|
|
||||||
|
Extending editions can be arbitrarily nested. That is, an edition can extend
|
||||||
|
another edition that extends another one etc. The only limitation is that
|
||||||
|
obviously there can be no cycles in the chain of extensions. Multiple extensions
|
||||||
|
are resolved as follows: first the parent edition is completely resolved (which
|
||||||
|
may recursively need to first resolve its parents etc.) and only then the
|
||||||
|
current edition applies its overrides.
|
||||||
|
|
||||||
|
### An Example Configuration
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
extends: 2021.4
|
||||||
|
engine-version: 1.2.3
|
||||||
|
repositories:
|
||||||
|
- name: secondary
|
||||||
|
url: https://example.com/
|
||||||
|
libraries:
|
||||||
|
- name: Foo.Bar
|
||||||
|
version: 1.0.0
|
||||||
|
repository: secondary
|
||||||
|
```
|
||||||
|
|
||||||
|
The edition file shown above extends a base edition file called `2021.4`. It
|
||||||
|
overrides the engine version set in the parent edition to `1.2.3`. Moreover it
|
||||||
|
adds a library `Foo.Bar` from the `secondary` repository, or if `2021.4`
|
||||||
|
included the library `Foo.Bar`, its definition is overridden with the one
|
||||||
|
provided here.
|
||||||
|
|
||||||
|
## Edition Resolution
|
||||||
|
|
||||||
|
The edition configuration for a project is loaded from the `edition` section in
|
||||||
|
its `package.yaml` configuration. This 'per-project' edition has no assigned
|
||||||
|
names, but it can refer to other editions by their names (when extending them).
|
||||||
|
These editions are resolved using the logic below:
|
||||||
|
|
||||||
|
1. Each `<edition-name>` corresponds to a file `<edition-name>.yaml`.
|
||||||
|
2. First, the directory `<ENSO_HOME>/editions` is scanned for a matching edition
|
||||||
|
file.
|
||||||
|
3. If none is found above, the directory `$ENSO_DATA_DIRECTORY/editions` is
|
||||||
|
checked.
|
||||||
|
|
||||||
|
See [The Enso Distribution](../distribution/distribution.md) for definitions of
|
||||||
|
the directories.
|
||||||
|
|
||||||
|
### Updating the Editions
|
||||||
|
|
||||||
|
The global user configuration file should contain a list of URLs specifying
|
||||||
|
edition providers that should be used. By default (if the field is missing), it
|
||||||
|
will default to our official edition provider, but users may add other providers
|
||||||
|
or remove the official one.
|
||||||
|
|
||||||
|
When `enso update-editions` is called or when requested by the IDE, these
|
||||||
|
providers are queried and any new edition files are downloaded to the
|
||||||
|
`$ENSO_DATA_DIRECTORY/editions` directory. Editions are assumed to be immutable,
|
||||||
|
so edition files that already exist on disk are not redownloaded.
|
||||||
|
|
||||||
|
## Library Resolution
|
||||||
|
|
||||||
|
Below are listed the steps that are taken when resolving an import of library
|
||||||
|
`Foo.Bar`:
|
||||||
|
|
||||||
|
1. If and only if the project has `prefer-local-libraries` set to `true` and if
|
||||||
|
any directory on the library path contains `Foo/Bar`, that local instance is
|
||||||
|
chosen as the library that should be used, regardless of the version that is
|
||||||
|
there;
|
||||||
|
2. Otherwise, the list of libraries defined directly in the `edition` section of
|
||||||
|
`package.yaml` of the current project is checked, and if the library is
|
||||||
|
defined there, it is selected.
|
||||||
|
3. Otherwise, any parent editions are consulted; if they too do not contain the
|
||||||
|
library that we are searching for, an error is reported.
|
||||||
|
4. Once we know the library version to be used:
|
||||||
|
1. If the repository associated with the library is `local`, the library path
|
||||||
|
is searched for the first directory to contain `Foo/Bar` and this path is
|
||||||
|
loaded. If the library is not present on the library path, an error is
|
||||||
|
reported.
|
||||||
|
2. Otherwise, the edition must have defined an exact `<version>` of the
|
||||||
|
library that is supposed to be used.
|
||||||
|
3. If the library is already downloaded in the local repository cache, that
|
||||||
|
is the directory `$ENSO_DATA_DIRECTORY/lib/Foo/Bar/<version>` exists, that
|
||||||
|
package is loaded.
|
||||||
|
4. Otherwise, the library is missing and must be downloaded from its
|
||||||
|
associated repository (and placed in the cache as above).
|
||||||
|
|
||||||
|
By default, the library path is `<ENSO_HOME>/libraries/` but it can be
|
||||||
|
overridden by setting the `ENSO_LIBRARY_PATH` environment variable. It may
|
||||||
|
include a list of directories (separated by the system specific path separator);
|
||||||
|
the first directory on the list has the highest precedence.
|
||||||
|
|
||||||
|
In particular, if `prefer-local-libraries` is `false`, and the edition does not
|
||||||
|
define a library at all, when trying to resolve such a library, it is reported
|
||||||
|
as not found even if a local version of it exists. That is because
|
||||||
|
auto-discovery of local libraries is only done with `prefer-local-libraries` set
|
||||||
|
to `true`. In all other cases, the `local` repository overrides should be set
|
||||||
|
explicitly.
|
@ -1,10 +1,10 @@
|
|||||||
package org.enso.launcher
|
package org.enso.launcher
|
||||||
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
import com.typesafe.scalalogging.Logger
|
import com.typesafe.scalalogging.Logger
|
||||||
import io.circe.Json
|
import io.circe.Json
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
|
import org.enso.distribution.EditionManager
|
||||||
import org.enso.runtimeversionmanager.CurrentVersion
|
import org.enso.runtimeversionmanager.CurrentVersion
|
||||||
import org.enso.runtimeversionmanager.config.{
|
import org.enso.runtimeversionmanager.config.{
|
||||||
DefaultVersion,
|
DefaultVersion,
|
||||||
@ -42,12 +42,14 @@ case class Launcher(cliOptions: GlobalCLIOptions) {
|
|||||||
runtimeVersionManager(cliOptions, alwaysInstallMissing = false)
|
runtimeVersionManager(cliOptions, alwaysInstallMissing = false)
|
||||||
private lazy val configurationManager =
|
private lazy val configurationManager =
|
||||||
new GlobalConfigurationManager(componentsManager, distributionManager)
|
new GlobalConfigurationManager(componentsManager, distributionManager)
|
||||||
private lazy val projectManager = new ProjectManager(configurationManager)
|
private lazy val editionManager = EditionManager(distributionManager)
|
||||||
|
private lazy val projectManager = new ProjectManager
|
||||||
private lazy val runner =
|
private lazy val runner =
|
||||||
new LauncherRunner(
|
new LauncherRunner(
|
||||||
projectManager,
|
projectManager,
|
||||||
configurationManager,
|
configurationManager,
|
||||||
componentsManager,
|
componentsManager,
|
||||||
|
editionManager,
|
||||||
LauncherEnvironment,
|
LauncherEnvironment,
|
||||||
LauncherLogging.loggingServiceEndpoint()
|
LauncherLogging.loggingServiceEndpoint()
|
||||||
)
|
)
|
||||||
|
@ -3,12 +3,12 @@ package org.enso.launcher.cli
|
|||||||
import com.typesafe.scalalogging.Logger
|
import com.typesafe.scalalogging.Logger
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
import org.enso.cli.CLIOutput
|
import org.enso.cli.CLIOutput
|
||||||
|
import org.enso.distribution.locking.Resource
|
||||||
import org.enso.launcher.InfoLogger
|
import org.enso.launcher.InfoLogger
|
||||||
import org.enso.runtimeversionmanager.components.{
|
import org.enso.runtimeversionmanager.components.{
|
||||||
GraalVMVersion,
|
GraalVMVersion,
|
||||||
RuntimeVersionManagementUserInterface
|
RuntimeVersionManagementUserInterface
|
||||||
}
|
}
|
||||||
import org.enso.runtimeversionmanager.locking.Resource
|
|
||||||
|
|
||||||
/** [[RuntimeVersionManagementUserInterface]] that reports information and progress
|
/** [[RuntimeVersionManagementUserInterface]] that reports information and progress
|
||||||
* to the command line.
|
* to the command line.
|
||||||
|
@ -2,13 +2,13 @@ package org.enso.launcher.cli
|
|||||||
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.file.{Files, NoSuchFileException, Path}
|
import java.nio.file.{Files, NoSuchFileException, Path}
|
||||||
|
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
import org.enso.cli.arguments.Opts
|
import org.enso.cli.arguments.Opts
|
||||||
import org.enso.cli.arguments.Opts.implicits._
|
import org.enso.cli.arguments.Opts.implicits._
|
||||||
import org.enso.runtimeversionmanager.{CurrentVersion, FileSystem, OS}
|
import org.enso.distribution.{FileSystem, OS}
|
||||||
import org.enso.runtimeversionmanager.FileSystem.PathSyntax
|
import org.enso.runtimeversionmanager.CurrentVersion
|
||||||
|
import org.enso.distribution.FileSystem.PathSyntax
|
||||||
import org.enso.runtimeversionmanager.cli.Arguments._
|
import org.enso.runtimeversionmanager.cli.Arguments._
|
||||||
import org.enso.launcher.distribution.LauncherEnvironment
|
import org.enso.launcher.distribution.LauncherEnvironment
|
||||||
import org.enso.launcher.upgrade.LauncherUpgrader
|
import org.enso.launcher.upgrade.LauncherUpgrader
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
package org.enso.launcher.cli
|
package org.enso.launcher.cli
|
||||||
|
|
||||||
import java.nio.file.{Files, Path}
|
import java.nio.file.{Files, Path}
|
||||||
|
|
||||||
import org.enso.cli.arguments
|
import org.enso.cli.arguments
|
||||||
import org.enso.cli.arguments.CommandHelp
|
import org.enso.cli.arguments.CommandHelp
|
||||||
import org.enso.runtimeversionmanager.{Environment, FileSystem}
|
import org.enso.distribution.{Environment, FileSystem}
|
||||||
import org.enso.launcher.distribution.LauncherEnvironment
|
import org.enso.launcher.distribution.LauncherEnvironment
|
||||||
|
|
||||||
import scala.sys.process._
|
import scala.sys.process._
|
||||||
|
@ -1,22 +1,15 @@
|
|||||||
package org.enso.launcher.components
|
package org.enso.launcher.components
|
||||||
|
|
||||||
import java.nio.file.{Files, Path}
|
|
||||||
|
|
||||||
import akka.http.scaladsl.model.Uri
|
import akka.http.scaladsl.model.Uri
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
import org.enso.runtimeversionmanager.Environment
|
import org.enso.distribution.{EditionManager, Environment}
|
||||||
import org.enso.runtimeversionmanager.components.RuntimeVersionManager
|
|
||||||
import org.enso.runtimeversionmanager.config.GlobalConfigurationManager
|
|
||||||
import org.enso.runtimeversionmanager.runner.{
|
|
||||||
LanguageServerOptions,
|
|
||||||
RunSettings,
|
|
||||||
Runner,
|
|
||||||
RunnerError,
|
|
||||||
WhichEngine
|
|
||||||
}
|
|
||||||
import org.enso.launcher.project.ProjectManager
|
import org.enso.launcher.project.ProjectManager
|
||||||
import org.enso.loggingservice.LogLevel
|
import org.enso.loggingservice.LogLevel
|
||||||
|
import org.enso.runtimeversionmanager.components.RuntimeVersionManager
|
||||||
|
import org.enso.runtimeversionmanager.config.GlobalConfigurationManager
|
||||||
|
import org.enso.runtimeversionmanager.runner._
|
||||||
|
|
||||||
|
import java.nio.file.{Files, Path}
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
|
|
||||||
@ -26,10 +19,13 @@ class LauncherRunner(
|
|||||||
projectManager: ProjectManager,
|
projectManager: ProjectManager,
|
||||||
configurationManager: GlobalConfigurationManager,
|
configurationManager: GlobalConfigurationManager,
|
||||||
componentsManager: RuntimeVersionManager,
|
componentsManager: RuntimeVersionManager,
|
||||||
|
editionManager: EditionManager,
|
||||||
environment: Environment,
|
environment: Environment,
|
||||||
loggerConnection: Future[Option[Uri]]
|
loggerConnection: Future[Option[Uri]]
|
||||||
) extends Runner(
|
) extends Runner(
|
||||||
componentsManager,
|
componentsManager,
|
||||||
|
configurationManager,
|
||||||
|
editionManager,
|
||||||
environment,
|
environment,
|
||||||
loggerConnection
|
loggerConnection
|
||||||
) {
|
) {
|
||||||
@ -53,12 +49,7 @@ class LauncherRunner(
|
|||||||
projectManager.findProject(currentWorkingDirectory).get
|
projectManager.findProject(currentWorkingDirectory).get
|
||||||
}
|
}
|
||||||
|
|
||||||
val version =
|
val version = resolveVersion(versionOverride, inProject)
|
||||||
versionOverride.getOrElse {
|
|
||||||
inProject
|
|
||||||
.map(_.version)
|
|
||||||
.getOrElse(configurationManager.defaultVersion)
|
|
||||||
}
|
|
||||||
val arguments = inProject match {
|
val arguments = inProject match {
|
||||||
case Some(project) =>
|
case Some(project) =>
|
||||||
val projectPackagePath =
|
val projectPackagePath =
|
||||||
@ -110,9 +101,7 @@ class LauncherRunner(
|
|||||||
val project =
|
val project =
|
||||||
if (projectMode) Some(projectManager.loadProject(actualPath).get)
|
if (projectMode) Some(projectManager.loadProject(actualPath).get)
|
||||||
else projectManager.findProject(actualPath).get
|
else projectManager.findProject(actualPath).get
|
||||||
val version = versionOverride
|
val version = resolveVersion(versionOverride, project)
|
||||||
.orElse(project.map(_.version))
|
|
||||||
.getOrElse(configurationManager.defaultVersion)
|
|
||||||
|
|
||||||
val arguments =
|
val arguments =
|
||||||
if (projectMode) Seq("--run", actualPath.toString)
|
if (projectMode) Seq("--run", actualPath.toString)
|
||||||
@ -184,8 +173,7 @@ class LauncherRunner(
|
|||||||
for {
|
for {
|
||||||
project <- projectManager.findProject(currentWorkingDirectory)
|
project <- projectManager.findProject(currentWorkingDirectory)
|
||||||
} yield {
|
} yield {
|
||||||
val version =
|
val version = resolveVersion(None, project)
|
||||||
project.map(_.version).getOrElse(configurationManager.defaultVersion)
|
|
||||||
val arguments =
|
val arguments =
|
||||||
Seq("--version") ++ (if (useJSON) Seq("--json") else Seq())
|
Seq("--version") ++ (if (useJSON) Seq("--json") else Seq())
|
||||||
|
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
package org.enso.launcher.distribution
|
package org.enso.launcher.distribution
|
||||||
|
|
||||||
|
import org.enso.distribution.PortableDistributionManager
|
||||||
|
import org.enso.distribution.locking.{
|
||||||
|
ResourceManager,
|
||||||
|
ThreadSafeFileLockManager
|
||||||
|
}
|
||||||
import org.enso.launcher.cli.{
|
import org.enso.launcher.cli.{
|
||||||
CLIRuntimeVersionManagementUserInterface,
|
CLIRuntimeVersionManagementUserInterface,
|
||||||
GlobalCLIOptions
|
GlobalCLIOptions
|
||||||
@ -11,14 +16,7 @@ import org.enso.runtimeversionmanager.components.{
|
|||||||
RuntimeComponentUpdaterFactory,
|
RuntimeComponentUpdaterFactory,
|
||||||
RuntimeVersionManager
|
RuntimeVersionManager
|
||||||
}
|
}
|
||||||
import org.enso.runtimeversionmanager.distribution.{
|
import org.enso.runtimeversionmanager.distribution.TemporaryDirectoryManager
|
||||||
PortableDistributionManager,
|
|
||||||
TemporaryDirectoryManager
|
|
||||||
}
|
|
||||||
import org.enso.runtimeversionmanager.locking.{
|
|
||||||
ResourceManager,
|
|
||||||
ThreadSafeFileLockManager
|
|
||||||
}
|
|
||||||
import org.enso.runtimeversionmanager.releases.engine.EngineRepository
|
import org.enso.runtimeversionmanager.releases.engine.EngineRepository
|
||||||
import org.enso.runtimeversionmanager.releases.graalvm.GraalCEReleaseProvider
|
import org.enso.runtimeversionmanager.releases.graalvm.GraalCEReleaseProvider
|
||||||
|
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
package org.enso.launcher.distribution
|
package org.enso.launcher.distribution
|
||||||
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
import com.typesafe.scalalogging.Logger
|
import com.typesafe.scalalogging.Logger
|
||||||
import org.enso.runtimeversionmanager.Environment
|
import org.enso.distribution.Environment
|
||||||
|
|
||||||
/** Default [[Environment]] to use in the launcher.
|
/** Default [[Environment]] to use in the launcher.
|
||||||
*
|
*
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
package org.enso.launcher.installation
|
package org.enso.launcher.installation
|
||||||
|
|
||||||
import java.nio.file.{Files, Path}
|
import java.nio.file.{Files, Path}
|
||||||
|
|
||||||
import com.typesafe.scalalogging.Logger
|
import com.typesafe.scalalogging.Logger
|
||||||
import org.enso.cli.CLIOutput
|
import org.enso.cli.CLIOutput
|
||||||
import org.enso.runtimeversionmanager.FileSystem.PathSyntax
|
import org.enso.distribution.{
|
||||||
|
DistributionManager,
|
||||||
|
FileSystem,
|
||||||
|
OS,
|
||||||
|
PortableDistributionManager
|
||||||
|
}
|
||||||
|
import org.enso.distribution.locking.ResourceManager
|
||||||
|
import org.enso.distribution.FileSystem.PathSyntax
|
||||||
import org.enso.runtimeversionmanager.config.GlobalConfigurationManager
|
import org.enso.runtimeversionmanager.config.GlobalConfigurationManager
|
||||||
import org.enso.runtimeversionmanager.distribution.PortableDistributionManager
|
|
||||||
import org.enso.runtimeversionmanager.locking.ResourceManager
|
|
||||||
import org.enso.runtimeversionmanager.{FileSystem, OS}
|
|
||||||
import org.enso.launcher.InfoLogger
|
import org.enso.launcher.InfoLogger
|
||||||
import org.enso.launcher.cli.{GlobalCLIOptions, InternalOpts, Main}
|
import org.enso.launcher.cli.{GlobalCLIOptions, InternalOpts, Main}
|
||||||
import org.enso.launcher.distribution.DefaultManagers
|
import org.enso.launcher.distribution.DefaultManagers
|
||||||
@ -54,9 +57,9 @@ class DistributionInstaller(
|
|||||||
private val nonEssentialDirectories = Seq("THIRD-PARTY")
|
private val nonEssentialDirectories = Seq("THIRD-PARTY")
|
||||||
|
|
||||||
private val enginesDirectory =
|
private val enginesDirectory =
|
||||||
installed.dataDirectory / manager.ENGINES_DIRECTORY
|
installed.dataDirectory / DistributionManager.ENGINES_DIRECTORY
|
||||||
private val runtimesDirectory =
|
private val runtimesDirectory =
|
||||||
installed.dataDirectory / manager.RUNTIMES_DIRECTORY
|
installed.dataDirectory / DistributionManager.RUNTIMES_DIRECTORY
|
||||||
|
|
||||||
/** Installs the distribution under configured location.
|
/** Installs the distribution under configured location.
|
||||||
*
|
*
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
package org.enso.launcher.installation
|
package org.enso.launcher.installation
|
||||||
|
|
||||||
import java.nio.file.{Files, Path}
|
import java.nio.file.{Files, Path}
|
||||||
|
|
||||||
import com.typesafe.scalalogging.Logger
|
import com.typesafe.scalalogging.Logger
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
import org.enso.cli.CLIOutput
|
import org.enso.cli.CLIOutput
|
||||||
import org.enso.runtimeversionmanager.FileSystem.PathSyntax
|
import org.enso.distribution.{
|
||||||
|
DistributionManager,
|
||||||
|
FileSystem,
|
||||||
|
OS,
|
||||||
|
PortableDistributionManager
|
||||||
|
}
|
||||||
|
import org.enso.distribution.locking.ResourceManager
|
||||||
|
import org.enso.distribution.FileSystem.PathSyntax
|
||||||
import org.enso.runtimeversionmanager.config.GlobalConfigurationManager
|
import org.enso.runtimeversionmanager.config.GlobalConfigurationManager
|
||||||
import org.enso.runtimeversionmanager.distribution.PortableDistributionManager
|
|
||||||
import org.enso.runtimeversionmanager.locking.ResourceManager
|
|
||||||
import org.enso.runtimeversionmanager.{FileSystem, OS}
|
|
||||||
import org.enso.launcher.InfoLogger
|
import org.enso.launcher.InfoLogger
|
||||||
import org.enso.launcher.cli.{
|
import org.enso.launcher.cli.{
|
||||||
GlobalCLIOptions,
|
GlobalCLIOptions,
|
||||||
@ -178,7 +181,7 @@ class DistributionUninstaller(
|
|||||||
private val knownDataDirectories =
|
private val knownDataDirectories =
|
||||||
Set.from(
|
Set.from(
|
||||||
manager.LocallyInstalledDirectories.possibleDirectoriesInsideDataDirectory
|
manager.LocallyInstalledDirectories.possibleDirectoriesInsideDataDirectory
|
||||||
) - manager.LOCK_DIRECTORY
|
) - DistributionManager.LOCK_DIRECTORY
|
||||||
|
|
||||||
/** Removes all files contained in the ENSO_DATA_DIRECTORY and possibly the
|
/** Removes all files contained in the ENSO_DATA_DIRECTORY and possibly the
|
||||||
* directory itself.
|
* directory itself.
|
||||||
@ -211,7 +214,7 @@ class DistributionUninstaller(
|
|||||||
resourceManager.unlockTemporaryDirectory()
|
resourceManager.unlockTemporaryDirectory()
|
||||||
resourceManager.releaseMainLock()
|
resourceManager.releaseMainLock()
|
||||||
|
|
||||||
val lockDirectory = dataRoot / manager.LOCK_DIRECTORY
|
val lockDirectory = dataRoot / DistributionManager.LOCK_DIRECTORY
|
||||||
if (Files.isDirectory(lockDirectory)) {
|
if (Files.isDirectory(lockDirectory)) {
|
||||||
for (lock <- FileSystem.listDirectory(lockDirectory)) {
|
for (lock <- FileSystem.listDirectory(lockDirectory)) {
|
||||||
try {
|
try {
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
package org.enso.launcher.project
|
package org.enso.launcher.project
|
||||||
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
import org.enso.runtimeversionmanager.config.GlobalConfigurationManager
|
|
||||||
import org.enso.runtimeversionmanager.runner.Project
|
|
||||||
import org.enso.pkg.PackageManager
|
import org.enso.pkg.PackageManager
|
||||||
|
import org.enso.runtimeversionmanager.runner.Project
|
||||||
|
|
||||||
|
import java.nio.file.Path
|
||||||
import scala.util.{Failure, Try}
|
import scala.util.{Failure, Try}
|
||||||
|
|
||||||
/** A helper class for project management.
|
/** A helper class for project management.
|
||||||
@ -13,7 +11,7 @@ import scala.util.{Failure, Try}
|
|||||||
* It allows to create new project, open existing ones or traverse the
|
* It allows to create new project, open existing ones or traverse the
|
||||||
* directory tree to find a project based on a path inside it.
|
* directory tree to find a project based on a path inside it.
|
||||||
*/
|
*/
|
||||||
class ProjectManager(globalConfigurationManager: GlobalConfigurationManager) {
|
class ProjectManager {
|
||||||
|
|
||||||
private val packageManager = PackageManager.Default
|
private val packageManager = PackageManager.Default
|
||||||
|
|
||||||
@ -22,7 +20,7 @@ class ProjectManager(globalConfigurationManager: GlobalConfigurationManager) {
|
|||||||
def loadProject(path: Path): Try[Project] =
|
def loadProject(path: Path): Try[Project] =
|
||||||
packageManager
|
packageManager
|
||||||
.loadPackage(path.toFile)
|
.loadPackage(path.toFile)
|
||||||
.map(new Project(_, globalConfigurationManager))
|
.map(new Project(_))
|
||||||
.recoverWith(error => Failure(ProjectLoadingError(path, error)))
|
.recoverWith(error => Failure(ProjectLoadingError(path, error)))
|
||||||
|
|
||||||
/** Traverses the directory tree looking for a project in one of the ancestors
|
/** Traverses the directory tree looking for a project in one of the ancestors
|
||||||
@ -40,7 +38,7 @@ class ProjectManager(globalConfigurationManager: GlobalConfigurationManager) {
|
|||||||
private def tryFindingProject(root: Path): Try[Project] =
|
private def tryFindingProject(root: Path): Try[Project] =
|
||||||
packageManager
|
packageManager
|
||||||
.loadPackage(root.toFile)
|
.loadPackage(root.toFile)
|
||||||
.map(new Project(_, globalConfigurationManager))
|
.map(new Project(_))
|
||||||
.recoverWith {
|
.recoverWith {
|
||||||
case PackageManager.PackageNotFound() if root.getParent != null =>
|
case PackageManager.PackageNotFound() if root.getParent != null =>
|
||||||
tryFindingProject(root.getParent)
|
tryFindingProject(root.getParent)
|
||||||
|
@ -3,7 +3,7 @@ package org.enso.launcher.releases.launcher
|
|||||||
import io.circe.{yaml, Decoder}
|
import io.circe.{yaml, Decoder}
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
import org.enso.runtimeversionmanager.releases.ReleaseProviderException
|
import org.enso.runtimeversionmanager.releases.ReleaseProviderException
|
||||||
import org.enso.pkg.SemVerJson._
|
import org.enso.editions.SemVerJson._
|
||||||
|
|
||||||
import scala.util.{Failure, Try}
|
import scala.util.{Failure, Try}
|
||||||
|
|
||||||
|
@ -1,32 +1,32 @@
|
|||||||
package org.enso.launcher.upgrade
|
package org.enso.launcher.upgrade
|
||||||
|
|
||||||
import java.nio.file.{Files, Path}
|
import java.nio.file.{Files, Path}
|
||||||
|
|
||||||
import com.typesafe.scalalogging.Logger
|
import com.typesafe.scalalogging.Logger
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
import org.enso.cli.CLIOutput
|
import org.enso.cli.CLIOutput
|
||||||
import org.enso.runtimeversionmanager.{CurrentVersion, FileSystem, OS}
|
import org.enso.distribution.{DistributionManager, FileSystem, OS}
|
||||||
import org.enso.runtimeversionmanager.FileSystem.PathSyntax
|
import org.enso.distribution.locking.{
|
||||||
import org.enso.runtimeversionmanager.archive.Archive
|
|
||||||
import org.enso.runtimeversionmanager.components.UpgradeRequiredError
|
|
||||||
import org.enso.runtimeversionmanager.distribution.DistributionManager
|
|
||||||
import org.enso.launcher.cli.{
|
|
||||||
CLIProgressReporter,
|
|
||||||
GlobalCLIOptions,
|
|
||||||
InternalOpts
|
|
||||||
}
|
|
||||||
import org.enso.runtimeversionmanager.locking.{
|
|
||||||
LockType,
|
LockType,
|
||||||
LockUserInterface,
|
LockUserInterface,
|
||||||
Resource,
|
Resource,
|
||||||
ResourceManager
|
ResourceManager
|
||||||
}
|
}
|
||||||
|
import org.enso.runtimeversionmanager.CurrentVersion
|
||||||
|
import org.enso.distribution.FileSystem.PathSyntax
|
||||||
|
import org.enso.runtimeversionmanager.archive.Archive
|
||||||
|
import org.enso.runtimeversionmanager.components.UpgradeRequiredError
|
||||||
|
import org.enso.launcher.cli.{
|
||||||
|
CLIProgressReporter,
|
||||||
|
GlobalCLIOptions,
|
||||||
|
InternalOpts
|
||||||
|
}
|
||||||
import org.enso.launcher.releases.launcher.LauncherRelease
|
import org.enso.launcher.releases.launcher.LauncherRelease
|
||||||
import org.enso.runtimeversionmanager.releases.ReleaseProvider
|
import org.enso.runtimeversionmanager.releases.ReleaseProvider
|
||||||
import org.enso.launcher.releases.LauncherRepository
|
import org.enso.launcher.releases.LauncherRepository
|
||||||
import org.enso.launcher.InfoLogger
|
import org.enso.launcher.InfoLogger
|
||||||
import org.enso.launcher.distribution.DefaultManagers
|
import org.enso.launcher.distribution.DefaultManagers
|
||||||
import org.enso.logger.LoggerSyntax
|
import org.enso.logger.LoggerSyntax
|
||||||
|
import org.enso.runtimeversionmanager.locking.Resources
|
||||||
|
|
||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
@ -66,7 +66,7 @@ class LauncherUpgrader(
|
|||||||
}
|
}
|
||||||
resourceManager.withResource(
|
resourceManager.withResource(
|
||||||
failIfAnotherUpgradeIsRunning,
|
failIfAnotherUpgradeIsRunning,
|
||||||
Resource.LauncherExecutable,
|
Resources.LauncherExecutable,
|
||||||
LockType.Exclusive
|
LockType.Exclusive
|
||||||
) {
|
) {
|
||||||
runCleanup(isStartup = true)
|
runCleanup(isStartup = true)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package org.enso.launcher
|
package org.enso.launcher
|
||||||
|
|
||||||
import java.nio.file.{Files, Path}
|
import org.enso.distribution.OS
|
||||||
|
|
||||||
import org.enso.runtimeversionmanager.OS
|
import java.nio.file.{Files, Path}
|
||||||
import org.enso.runtimeversionmanager.test.NativeTestHelper
|
import org.enso.runtimeversionmanager.test.NativeTestHelper
|
||||||
import org.scalatest.concurrent.{Signaler, TimeLimitedTests}
|
import org.scalatest.concurrent.{Signaler, TimeLimitedTests}
|
||||||
import org.scalatest.matchers.should.Matchers
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
@ -4,7 +4,8 @@ import java.nio.file.{Files, Path}
|
|||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import akka.http.scaladsl.model.Uri
|
import akka.http.scaladsl.model.Uri
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
import org.enso.runtimeversionmanager.FileSystem.PathSyntax
|
import org.enso.distribution.EditionManager
|
||||||
|
import org.enso.distribution.FileSystem.PathSyntax
|
||||||
import org.enso.runtimeversionmanager.config.GlobalConfigurationManager
|
import org.enso.runtimeversionmanager.config.GlobalConfigurationManager
|
||||||
import org.enso.runtimeversionmanager.runner._
|
import org.enso.runtimeversionmanager.runner._
|
||||||
import org.enso.runtimeversionmanager.test.RuntimeVersionManagerTest
|
import org.enso.runtimeversionmanager.test.RuntimeVersionManagerTest
|
||||||
@ -30,13 +31,15 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest {
|
|||||||
new GlobalConfigurationManager(componentsManager, distributionManager) {
|
new GlobalConfigurationManager(componentsManager, distributionManager) {
|
||||||
override def defaultVersion: SemVer = defaultEngineVersion
|
override def defaultVersion: SemVer = defaultEngineVersion
|
||||||
}
|
}
|
||||||
val projectManager = new ProjectManager(configurationManager)
|
val editionManager = EditionManager(distributionManager)
|
||||||
|
val projectManager = new ProjectManager()
|
||||||
val cwd = cwdOverride.getOrElse(getTestDirectory)
|
val cwd = cwdOverride.getOrElse(getTestDirectory)
|
||||||
val runner =
|
val runner =
|
||||||
new LauncherRunner(
|
new LauncherRunner(
|
||||||
projectManager,
|
projectManager,
|
||||||
configurationManager,
|
configurationManager,
|
||||||
componentsManager,
|
componentsManager,
|
||||||
|
editionManager,
|
||||||
env,
|
env,
|
||||||
Future.successful(Some(fakeUri))
|
Future.successful(Some(fakeUri))
|
||||||
) {
|
) {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package org.enso.launcher.installation
|
package org.enso.launcher.installation
|
||||||
|
|
||||||
import java.nio.file.{Files, Path}
|
import org.enso.distribution.{FileSystem, OS}
|
||||||
|
|
||||||
import org.enso.runtimeversionmanager.{FileSystem, OS}
|
import java.nio.file.{Files, Path}
|
||||||
import org.enso.runtimeversionmanager.FileSystem.PathSyntax
|
import FileSystem.PathSyntax
|
||||||
import org.enso.runtimeversionmanager.test.WithTemporaryDirectory
|
import org.enso.runtimeversionmanager.test.WithTemporaryDirectory
|
||||||
import org.enso.launcher._
|
import org.enso.launcher._
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package org.enso.launcher.installation
|
package org.enso.launcher.installation
|
||||||
|
|
||||||
import java.nio.file.{Files, Path}
|
import org.enso.distribution.{FileSystem, OS}
|
||||||
|
|
||||||
import org.enso.runtimeversionmanager.{FileSystem, OS}
|
import java.nio.file.{Files, Path}
|
||||||
import org.enso.runtimeversionmanager.FileSystem.PathSyntax
|
import FileSystem.PathSyntax
|
||||||
import org.enso.runtimeversionmanager.test.WithTemporaryDirectory
|
import org.enso.runtimeversionmanager.test.WithTemporaryDirectory
|
||||||
import org.enso.launcher.NativeTest
|
import org.enso.launcher.NativeTest
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package org.enso.launcher.project
|
package org.enso.launcher.project
|
||||||
|
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
|
import org.enso.distribution.DistributionManager
|
||||||
import org.enso.runtimeversionmanager.config.GlobalConfigurationManager
|
import org.enso.runtimeversionmanager.config.GlobalConfigurationManager
|
||||||
import org.enso.runtimeversionmanager.distribution.DistributionManager
|
|
||||||
import org.enso.runtimeversionmanager.test.RuntimeVersionManagerTest
|
import org.enso.runtimeversionmanager.test.RuntimeVersionManagerTest
|
||||||
import org.enso.pkg.Contact
|
import org.enso.pkg.Contact
|
||||||
import org.scalatest.{Inside, OptionValues}
|
import org.scalatest.{Inside, OptionValues}
|
||||||
@ -19,7 +19,7 @@ class ProjectManagerSpec
|
|||||||
new GlobalConfigurationManager(null, distributionManager) {
|
new GlobalConfigurationManager(null, distributionManager) {
|
||||||
override def defaultVersion: SemVer = defaultEnsoVersion
|
override def defaultVersion: SemVer = defaultEnsoVersion
|
||||||
}
|
}
|
||||||
(fakeConfigurationManager, new ProjectManager(fakeConfigurationManager))
|
(fakeConfigurationManager, new ProjectManager())
|
||||||
}
|
}
|
||||||
|
|
||||||
"ProjectManager" should {
|
"ProjectManager" should {
|
||||||
@ -43,7 +43,8 @@ class ProjectManagerSpec
|
|||||||
projectDir.resolve("src").resolve("Main.enso").toFile should exist
|
projectDir.resolve("src").resolve("Main.enso").toFile should exist
|
||||||
|
|
||||||
val project = projectManager.loadProject(projectDir).get
|
val project = projectManager.loadProject(projectDir).get
|
||||||
project.version shouldEqual defaultEnsoVersion
|
// TODO [RW] this test may change once we switch to deriving a particular edition by default
|
||||||
|
project.edition.engineVersion should contain(defaultEnsoVersion)
|
||||||
project.config.authors.headOption.value shouldEqual author
|
project.config.authors.headOption.value shouldEqual author
|
||||||
project.config.maintainers.headOption.value shouldEqual author
|
project.config.maintainers.headOption.value shouldEqual author
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package org.enso.launcher.releases.fallback
|
package org.enso.launcher.releases.fallback
|
||||||
|
|
||||||
import java.nio.file.Path
|
import org.enso.distribution.FileSystem
|
||||||
|
|
||||||
import org.enso.runtimeversionmanager.FileSystem
|
import java.nio.file.Path
|
||||||
import org.enso.launcher.TestHelpers
|
import org.enso.launcher.TestHelpers
|
||||||
import org.enso.launcher.releases.fallback.staticwebsite.FileStorageFallbackReleaseProvider
|
import org.enso.launcher.releases.fallback.staticwebsite.FileStorageFallbackReleaseProvider
|
||||||
import org.scalatest.matchers.should.Matchers
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
package org.enso.launcher.releases.fallback
|
package org.enso.launcher.releases.fallback
|
||||||
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
import org.enso.cli.task.{TaskProgress, TaskProgressImplementation}
|
import org.enso.cli.task.{TaskProgress, TaskProgressImplementation}
|
||||||
import org.enso.runtimeversionmanager.FileSystem
|
import org.enso.distribution.FileSystem
|
||||||
import org.enso.launcher.TestHelpers
|
import org.enso.launcher.TestHelpers
|
||||||
import org.enso.launcher.releases.fallback.staticwebsite.FileStorage
|
import org.enso.launcher.releases.fallback.staticwebsite.FileStorage
|
||||||
|
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
package org.enso.launcher.upgrade
|
package org.enso.launcher.upgrade
|
||||||
|
|
||||||
import java.nio.file.{Files, Path, StandardCopyOption}
|
import java.nio.file.{Files, Path, StandardCopyOption}
|
||||||
|
|
||||||
import io.circe.parser
|
import io.circe.parser
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
import org.enso.runtimeversionmanager.{FileSystem, OS}
|
import org.enso.distribution.{FileSystem, OS}
|
||||||
import org.enso.runtimeversionmanager.FileSystem.PathSyntax
|
import org.enso.distribution.locking.{FileLockManager, LockType}
|
||||||
|
import FileSystem.PathSyntax
|
||||||
import org.enso.launcher._
|
import org.enso.launcher._
|
||||||
import org.enso.runtimeversionmanager.locking.{FileLockManager, LockType}
|
|
||||||
import org.enso.runtimeversionmanager.test.WithTemporaryDirectory
|
import org.enso.runtimeversionmanager.test.WithTemporaryDirectory
|
||||||
import org.enso.testkit.RetrySpec
|
import org.enso.testkit.RetrySpec
|
||||||
import org.scalatest.exceptions.TestFailedException
|
import org.scalatest.exceptions.TestFailedException
|
||||||
|
@ -4,7 +4,7 @@ import org.enso.polyglot.debugger.protocol.{
|
|||||||
ExceptionRepresentation,
|
ExceptionRepresentation,
|
||||||
ObjectRepresentation
|
ObjectRepresentation
|
||||||
}
|
}
|
||||||
|
import org.enso.testkit.EitherValue
|
||||||
import org.scalatest.matchers.should.Matchers
|
import org.scalatest.matchers.should.Matchers
|
||||||
import org.scalatest.wordspec.AnyWordSpec
|
import org.scalatest.wordspec.AnyWordSpec
|
||||||
|
|
||||||
|
@ -3,10 +3,11 @@ package org.enso.runner
|
|||||||
import akka.http.scaladsl.model.{IllegalUriException, Uri}
|
import akka.http.scaladsl.model.{IllegalUriException, Uri}
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
import org.apache.commons.cli.{Option => CliOption, _}
|
import org.apache.commons.cli.{Option => CliOption, _}
|
||||||
|
import org.enso.editions.SemVerEnsoVersion
|
||||||
import org.enso.languageserver.boot
|
import org.enso.languageserver.boot
|
||||||
import org.enso.languageserver.boot.LanguageServerConfig
|
import org.enso.languageserver.boot.LanguageServerConfig
|
||||||
import org.enso.loggingservice.LogLevel
|
import org.enso.loggingservice.LogLevel
|
||||||
import org.enso.pkg.{Contact, PackageManager, SemVerEnsoVersion}
|
import org.enso.pkg.{Contact, PackageManager}
|
||||||
import org.enso.polyglot.{LanguageInfo, Module, PolyglotContext}
|
import org.enso.polyglot.{LanguageInfo, Module, PolyglotContext}
|
||||||
import org.enso.version.VersionDescription
|
import org.enso.version.VersionDescription
|
||||||
import org.graalvm.polyglot.PolyglotException
|
import org.graalvm.polyglot.PolyglotException
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
package org.enso.runtimeversionmanager.distribution
|
package org.enso.distribution
|
||||||
|
|
||||||
import java.nio.file.{Files, Path}
|
|
||||||
|
|
||||||
import com.typesafe.scalalogging.Logger
|
import com.typesafe.scalalogging.Logger
|
||||||
import org.enso.runtimeversionmanager.FileSystem.PathSyntax
|
import org.enso.distribution.FileSystem.PathSyntax
|
||||||
import org.enso.runtimeversionmanager.{Environment, FileSystem, OS}
|
|
||||||
|
|
||||||
|
import java.nio.file.{Files, Path}
|
||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
|
|
||||||
@ -27,6 +25,10 @@ import scala.util.control.NonFatal
|
|||||||
* @param unsafeTemporaryDirectory path to the temporary directory, should not
|
* @param unsafeTemporaryDirectory path to the temporary directory, should not
|
||||||
* be used directly, see
|
* be used directly, see
|
||||||
* [[TemporaryDirectoryManager]]
|
* [[TemporaryDirectoryManager]]
|
||||||
|
* @param customEditions the search paths for editions
|
||||||
|
* @param localLibrariesSearchPaths a sequence of paths to search for local
|
||||||
|
* libraries, in order of precedence
|
||||||
|
* @param ensoHome the home directory for user's projects etc.
|
||||||
*/
|
*/
|
||||||
case class DistributionPaths(
|
case class DistributionPaths(
|
||||||
dataRoot: Path,
|
dataRoot: Path,
|
||||||
@ -36,7 +38,10 @@ case class DistributionPaths(
|
|||||||
config: Path,
|
config: Path,
|
||||||
locks: Path,
|
locks: Path,
|
||||||
logs: Path,
|
logs: Path,
|
||||||
unsafeTemporaryDirectory: Path
|
unsafeTemporaryDirectory: Path,
|
||||||
|
customEditions: Seq[Path],
|
||||||
|
localLibrariesSearchPaths: Seq[Path],
|
||||||
|
ensoHome: Path
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
@ -48,7 +53,8 @@ case class DistributionPaths(
|
|||||||
| bundle = $bundle,
|
| bundle = $bundle,
|
||||||
| config = $config,
|
| config = $config,
|
||||||
| locks = $locks,
|
| locks = $locks,
|
||||||
| tmp = $unsafeTemporaryDirectory
|
| tmp = $unsafeTemporaryDirectory,
|
||||||
|
| ensoHome = $ensoHome
|
||||||
|)""".stripMargin
|
|)""".stripMargin
|
||||||
|
|
||||||
/** Sequence of paths to search for engine installations, in order of
|
/** Sequence of paths to search for engine installations, in order of
|
||||||
@ -61,6 +67,18 @@ case class DistributionPaths(
|
|||||||
*/
|
*/
|
||||||
def runtimeSearchPaths: Seq[Path] =
|
def runtimeSearchPaths: Seq[Path] =
|
||||||
Seq(runtimes) ++ bundle.map(_.runtimes).toSeq
|
Seq(runtimes) ++ bundle.map(_.runtimes).toSeq
|
||||||
|
|
||||||
|
/** The directory for cached editions managed by us. */
|
||||||
|
def cachedEditions: Path = dataRoot / DistributionManager.EDITIONS_DIRECTORY
|
||||||
|
|
||||||
|
/** The directory for cached libraries managed by us. */
|
||||||
|
def cachedLibraries: Path = dataRoot / DistributionManager.LIBRARIES_DIRECTORY
|
||||||
|
|
||||||
|
/** Sequence of paths to search for edition configurations, in order of
|
||||||
|
* precedence.
|
||||||
|
*/
|
||||||
|
def editionSearchPaths: Seq[Path] =
|
||||||
|
customEditions ++ Seq(cachedEditions)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Paths to secondary directories for additionally bundled engine
|
/** Paths to secondary directories for additionally bundled engine
|
||||||
@ -84,6 +102,7 @@ case class Bundle(engines: Path, runtimes: Path)
|
|||||||
*/
|
*/
|
||||||
class DistributionManager(val env: Environment) {
|
class DistributionManager(val env: Environment) {
|
||||||
private val logger = Logger[DistributionManager]
|
private val logger = Logger[DistributionManager]
|
||||||
|
import DistributionManager._
|
||||||
|
|
||||||
/** Determines paths that should be used by the launcher.
|
/** Determines paths that should be used by the launcher.
|
||||||
*/
|
*/
|
||||||
@ -93,30 +112,56 @@ class DistributionManager(val env: Environment) {
|
|||||||
paths
|
paths
|
||||||
}
|
}
|
||||||
|
|
||||||
val ENGINES_DIRECTORY = "dist"
|
|
||||||
val RUNTIMES_DIRECTORY = "runtime"
|
|
||||||
val CONFIG_DIRECTORY = "config"
|
|
||||||
val BIN_DIRECTORY = "bin"
|
|
||||||
val LOCK_DIRECTORY = "lock"
|
|
||||||
val LOG_DIRECTORY = "log"
|
|
||||||
val TMP_DIRECTORY = "tmp"
|
|
||||||
|
|
||||||
protected def detectPaths(): DistributionPaths = {
|
protected def detectPaths(): DistributionPaths = {
|
||||||
val dataRoot = LocallyInstalledDirectories.dataDirectory
|
val dataRoot = LocallyInstalledDirectories.dataDirectory
|
||||||
val configRoot = LocallyInstalledDirectories.configDirectory
|
val configRoot = LocallyInstalledDirectories.configDirectory
|
||||||
val runRoot = LocallyInstalledDirectories.runtimeDirectory
|
val runRoot = LocallyInstalledDirectories.runtimeDirectory
|
||||||
|
val home = detectEnsoHome()
|
||||||
DistributionPaths(
|
DistributionPaths(
|
||||||
dataRoot = dataRoot,
|
dataRoot = dataRoot,
|
||||||
runtimes = dataRoot / RUNTIMES_DIRECTORY,
|
runtimes = dataRoot / RUNTIMES_DIRECTORY,
|
||||||
engines = dataRoot / ENGINES_DIRECTORY,
|
engines = dataRoot / ENGINES_DIRECTORY,
|
||||||
bundle = detectBundle(),
|
bundle = detectBundle(),
|
||||||
config = configRoot,
|
config = configRoot,
|
||||||
locks = runRoot / LOCK_DIRECTORY,
|
locks = runRoot / LOCK_DIRECTORY,
|
||||||
logs = LocallyInstalledDirectories.logDirectory,
|
logs = LocallyInstalledDirectories.logDirectory,
|
||||||
unsafeTemporaryDirectory = dataRoot / TMP_DIRECTORY
|
unsafeTemporaryDirectory = dataRoot / TMP_DIRECTORY,
|
||||||
|
customEditions = detectCustomEditionPaths(home),
|
||||||
|
localLibrariesSearchPaths = detectLocalLibraryPaths(home),
|
||||||
|
ensoHome = home
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val ENSO_HOME = "ENSO_HOME"
|
||||||
|
private val ENSO_EDITION_PATH = "ENSO_EDITION_PATH"
|
||||||
|
private val ENSO_LIBRARY_PATH = "ENSO_LIBRARY_PATH"
|
||||||
|
|
||||||
|
/** Finds the path to the ENSO_HOME directory that is used for keeping user's
|
||||||
|
* projects, libraries and other custom artifacts.
|
||||||
|
*/
|
||||||
|
protected def detectEnsoHome(): Path =
|
||||||
|
env.getEnvPath(ENSO_HOME).getOrElse(env.getUserProfile / "enso")
|
||||||
|
|
||||||
|
/** Finds the paths to look for custom editions, which may be overridden by
|
||||||
|
* setting the ENSO_EDITION_PATH environment variable.
|
||||||
|
*/
|
||||||
|
protected def detectCustomEditionPaths(ensoHome: Path): Seq[Path] =
|
||||||
|
env
|
||||||
|
.getEnvPaths(ENSO_EDITION_PATH)
|
||||||
|
.getOrElse {
|
||||||
|
Seq(ensoHome / DistributionManager.Home.EDITIONS_DIRECTORY)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Finds the paths to look for local libraries, which may be overridden by
|
||||||
|
* setting the ENSO_LIBRARY_PATH environment variable.
|
||||||
|
*/
|
||||||
|
protected def detectLocalLibraryPaths(ensoHome: Path): Seq[Path] =
|
||||||
|
env
|
||||||
|
.getEnvPaths(ENSO_LIBRARY_PATH)
|
||||||
|
.getOrElse {
|
||||||
|
Seq(ensoHome / DistributionManager.Home.LIBRARIES_DIRECTORY)
|
||||||
|
}
|
||||||
|
|
||||||
/** Name of the file that should be placed in the distribution root to mark it
|
/** Name of the file that should be placed in the distribution root to mark it
|
||||||
* as running in portable mode.
|
* as running in portable mode.
|
||||||
*/
|
*/
|
||||||
@ -315,3 +360,24 @@ class DistributionManager(val env: Environment) {
|
|||||||
safeDataDirectory.exists(Files.isDirectory(_))
|
safeDataDirectory.exists(Files.isDirectory(_))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A helper object that contains constants defining names of various
|
||||||
|
* directories used by Enso components.
|
||||||
|
*/
|
||||||
|
object DistributionManager {
|
||||||
|
val ENGINES_DIRECTORY = "dist"
|
||||||
|
val RUNTIMES_DIRECTORY = "runtime"
|
||||||
|
val CONFIG_DIRECTORY = "config"
|
||||||
|
val BIN_DIRECTORY = "bin"
|
||||||
|
val LOCK_DIRECTORY = "lock"
|
||||||
|
val LOG_DIRECTORY = "log"
|
||||||
|
val TMP_DIRECTORY = "tmp"
|
||||||
|
val EDITIONS_DIRECTORY = "editions"
|
||||||
|
val LIBRARIES_DIRECTORY = "lib"
|
||||||
|
|
||||||
|
/** Defines paths inside of the ENSO_HOME directory. */
|
||||||
|
object Home {
|
||||||
|
val EDITIONS_DIRECTORY = "editions"
|
||||||
|
val LIBRARIES_DIRECTORY = "libraries"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package org.enso.distribution
|
||||||
|
|
||||||
|
import org.enso.editions
|
||||||
|
import org.enso.editions.provider.FileSystemEditionProvider
|
||||||
|
import org.enso.editions.{EditionResolver, Editions, EnsoVersion}
|
||||||
|
|
||||||
|
import scala.util.Try
|
||||||
|
|
||||||
|
/** A helper class for resolving editions backed by the Edition storage managed
|
||||||
|
* by the DistributionManager.
|
||||||
|
*/
|
||||||
|
case class EditionManager(distributionManager: DistributionManager) {
|
||||||
|
private val editionProvider = FileSystemEditionProvider(
|
||||||
|
distributionManager.paths.editionSearchPaths.toList
|
||||||
|
)
|
||||||
|
|
||||||
|
private val editionResolver = EditionResolver(editionProvider)
|
||||||
|
private val engineVersionResolver =
|
||||||
|
editions.EngineVersionResolver(editionProvider)
|
||||||
|
|
||||||
|
/** Resolves a raw edition, loading its parents from the edition search path.
|
||||||
|
* @param edition the edition to resolve
|
||||||
|
* @return the resolved edition
|
||||||
|
*/
|
||||||
|
def resolveEdition(
|
||||||
|
edition: Editions.RawEdition
|
||||||
|
): Try[Editions.ResolvedEdition] =
|
||||||
|
editionResolver.resolve(edition).toTry
|
||||||
|
|
||||||
|
/** Resolves the engine version that should be used based on the provided raw
|
||||||
|
* edition configuration.
|
||||||
|
* @param edition the edition configuration to base the selected version on
|
||||||
|
* @return the resolved engine version
|
||||||
|
*/
|
||||||
|
def resolveEngineVersion(edition: Editions.RawEdition): Try[EnsoVersion] =
|
||||||
|
engineVersionResolver.resolveEnsoVersion(edition).toTry
|
||||||
|
}
|
@ -1,11 +1,10 @@
|
|||||||
package org.enso.runtimeversionmanager
|
package org.enso.distribution
|
||||||
|
|
||||||
import java.io.File
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
import com.typesafe.scalalogging.Logger
|
import com.typesafe.scalalogging.Logger
|
||||||
import org.enso.logger.masking.MaskedString
|
import org.enso.logger.masking.MaskedString
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Path
|
||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
|
|
||||||
/** Gathers some helper methods querying the system environment.
|
/** Gathers some helper methods querying the system environment.
|
||||||
@ -39,26 +38,38 @@ trait Environment {
|
|||||||
*
|
*
|
||||||
* If it is not defined or is not a valid path, returns None.
|
* If it is not defined or is not a valid path, returns None.
|
||||||
*/
|
*/
|
||||||
def getEnvPath(key: String): Option[Path] = {
|
def getEnvPath(key: String): Option[Path] =
|
||||||
def parsePathWithWarning(str: String): Option[Path] = {
|
getEnvVar(key).flatMap(parsePathWithWarning(key))
|
||||||
val result = safeParsePath(str)
|
|
||||||
if (result.isEmpty) {
|
/** Queries the system environment for the given variable that should
|
||||||
Logger[Environment].warn(
|
* represent a list of valid filesystem paths.
|
||||||
"System variable [{}] was set to [{}], but it did not " +
|
*
|
||||||
"represent a valid path, so it has been ignored.",
|
* If a path is on the list but is invalid, it is skipped.
|
||||||
key,
|
*/
|
||||||
MaskedString(str)
|
def getEnvPaths(key: String): Option[Seq[Path]] =
|
||||||
)
|
getEnvVar(key)
|
||||||
|
.map { value =>
|
||||||
|
value
|
||||||
|
.split(File.pathSeparator)
|
||||||
|
.toSeq
|
||||||
|
.flatMap(str => parsePathWithWarning(key)(str).toSeq)
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
private def parsePathWithWarning(key: String)(str: String): Option[Path] = {
|
||||||
|
val result = safeParsePath(str)
|
||||||
|
if (result.isEmpty) {
|
||||||
|
Logger[Environment].warn(
|
||||||
|
"System variable [{}] was set to [{}], but it did not " +
|
||||||
|
"represent a valid path, so it has been ignored.",
|
||||||
|
key,
|
||||||
|
MaskedString(str)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
getEnvVar(key).flatMap(parsePathWithWarning)
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the system PATH, if available.
|
/** Returns the system PATH, if available. */
|
||||||
*/
|
|
||||||
def getSystemPath: Seq[Path] =
|
def getSystemPath: Seq[Path] =
|
||||||
getEnvVar("PATH")
|
getEnvVar("PATH")
|
||||||
.map(_.split(File.pathSeparatorChar).toSeq.flatMap(safeParsePath))
|
.map(_.split(File.pathSeparatorChar).toSeq.flatMap(safeParsePath))
|
||||||
@ -85,6 +96,33 @@ trait Environment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns the location of the user profile directory (`%UserProfile%`) on
|
||||||
|
* Windows.
|
||||||
|
*/
|
||||||
|
def getWindowsUserProfile: Path = {
|
||||||
|
if (!OS.isWindows)
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"fatal error: USERPROFILE should be queried only on Windows."
|
||||||
|
)
|
||||||
|
else {
|
||||||
|
getEnvVar("USERPROFILE").flatMap(safeParsePath) match {
|
||||||
|
case Some(path) => path
|
||||||
|
case None =>
|
||||||
|
throw new RuntimeException(
|
||||||
|
"fatal error: %USERPROFILE% environment variable is not defined."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a path to the user's home directory, as defined on their
|
||||||
|
* particular Operating System.
|
||||||
|
*
|
||||||
|
* On UNIX, this is $HOME and on Windows it is %UserProfile%.
|
||||||
|
*/
|
||||||
|
def getUserProfile: Path =
|
||||||
|
if (OS.isWindows) getWindowsUserProfile else getHome
|
||||||
|
|
||||||
/** Returns the location of the local application data directory
|
/** Returns the location of the local application data directory
|
||||||
* (`%LocalAppData%`) on Windows.
|
* (`%LocalAppData%`) on Windows.
|
||||||
*
|
*
|
@ -1,4 +1,7 @@
|
|||||||
package org.enso.runtimeversionmanager
|
package org.enso.distribution
|
||||||
|
|
||||||
|
import com.typesafe.scalalogging.Logger
|
||||||
|
import org.apache.commons.io.FileUtils
|
||||||
|
|
||||||
import java.io.PrintWriter
|
import java.io.PrintWriter
|
||||||
import java.nio.file.attribute.PosixFilePermissions
|
import java.nio.file.attribute.PosixFilePermissions
|
||||||
@ -8,10 +11,6 @@ import java.nio.file.{
|
|||||||
Path,
|
Path,
|
||||||
StandardCopyOption
|
StandardCopyOption
|
||||||
}
|
}
|
||||||
|
|
||||||
import com.typesafe.scalalogging.Logger
|
|
||||||
import org.apache.commons.io.FileUtils
|
|
||||||
|
|
||||||
import scala.collection.Factory
|
import scala.collection.Factory
|
||||||
import scala.jdk.StreamConverters._
|
import scala.jdk.StreamConverters._
|
||||||
import scala.util.Using
|
import scala.util.Using
|
@ -1,4 +1,4 @@
|
|||||||
package org.enso.runtimeversionmanager
|
package org.enso.distribution
|
||||||
|
|
||||||
import com.typesafe.scalalogging.Logger
|
import com.typesafe.scalalogging.Logger
|
||||||
import io.circe.{Decoder, DecodingFailure}
|
import io.circe.{Decoder, DecodingFailure}
|
@ -1,10 +1,9 @@
|
|||||||
package org.enso.runtimeversionmanager.distribution
|
package org.enso.distribution
|
||||||
|
|
||||||
import java.nio.file.{Files, Path}
|
|
||||||
|
|
||||||
import com.typesafe.scalalogging.Logger
|
import com.typesafe.scalalogging.Logger
|
||||||
import org.enso.runtimeversionmanager.Environment
|
import org.enso.distribution.FileSystem.PathSyntax
|
||||||
import org.enso.runtimeversionmanager.FileSystem.PathSyntax
|
|
||||||
|
import java.nio.file.{Files, Path}
|
||||||
|
|
||||||
/** A specialized variant of [[DistributionManager]] that is able to detect if
|
/** A specialized variant of [[DistributionManager]] that is able to detect if
|
||||||
* the currently running distribution is running in portable or locally
|
* the currently running distribution is running in portable or locally
|
||||||
@ -69,15 +68,20 @@ class PortableDistributionManager(env: Environment)
|
|||||||
override protected def detectPaths(): DistributionPaths =
|
override protected def detectPaths(): DistributionPaths =
|
||||||
if (isRunningPortable) {
|
if (isRunningPortable) {
|
||||||
val root = env.getPathToRunningExecutable.getParent.getParent
|
val root = env.getPathToRunningExecutable.getParent.getParent
|
||||||
|
val home = detectEnsoHome()
|
||||||
|
import DistributionManager._
|
||||||
DistributionPaths(
|
DistributionPaths(
|
||||||
dataRoot = root,
|
dataRoot = root,
|
||||||
runtimes = root / RUNTIMES_DIRECTORY,
|
runtimes = root / RUNTIMES_DIRECTORY,
|
||||||
engines = root / ENGINES_DIRECTORY,
|
engines = root / ENGINES_DIRECTORY,
|
||||||
bundle = None,
|
bundle = None,
|
||||||
config = root / CONFIG_DIRECTORY,
|
config = root / CONFIG_DIRECTORY,
|
||||||
locks = root / LOCK_DIRECTORY,
|
locks = root / LOCK_DIRECTORY,
|
||||||
logs = root / LOG_DIRECTORY,
|
logs = root / LOG_DIRECTORY,
|
||||||
unsafeTemporaryDirectory = root / TMP_DIRECTORY
|
unsafeTemporaryDirectory = root / TMP_DIRECTORY,
|
||||||
|
customEditions = detectCustomEditionPaths(home),
|
||||||
|
localLibrariesSearchPaths = detectLocalLibraryPaths(home),
|
||||||
|
ensoHome = home
|
||||||
)
|
)
|
||||||
} else super.detectPaths()
|
} else super.detectPaths()
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package org.enso.runtimeversionmanager.locking
|
package org.enso.distribution.locking
|
||||||
|
|
||||||
import java.nio.channels.{FileChannel, FileLock}
|
import java.nio.channels.{FileChannel, FileLock}
|
||||||
import java.nio.file.{Files, Path, StandardOpenOption}
|
import java.nio.file.{Files, Path, StandardOpenOption}
|
@ -1,4 +1,4 @@
|
|||||||
package org.enso.runtimeversionmanager.locking
|
package org.enso.distribution.locking
|
||||||
|
|
||||||
import scala.util.Using.Releasable
|
import scala.util.Using.Releasable
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package org.enso.runtimeversionmanager.locking
|
package org.enso.distribution.locking
|
||||||
|
|
||||||
/** Manages locks that can be used to synchronize different launcher processes
|
/** Manages locks that can be used to synchronize different launcher processes
|
||||||
* running in parallel to avoid components corruption caused by simultaneous
|
* running in parallel to avoid components corruption caused by simultaneous
|
@ -1,4 +1,4 @@
|
|||||||
package org.enso.runtimeversionmanager.locking
|
package org.enso.distribution.locking
|
||||||
|
|
||||||
/** Defines the lock type.
|
/** Defines the lock type.
|
||||||
*/
|
*/
|
@ -1,4 +1,4 @@
|
|||||||
package org.enso.runtimeversionmanager.locking
|
package org.enso.distribution.locking
|
||||||
|
|
||||||
/** Defines callbacks that can be called when a requested resource is locked and
|
/** Defines callbacks that can be called when a requested resource is locked and
|
||||||
* the application has to wait for other processes.
|
* the application has to wait for other processes.
|
@ -0,0 +1,16 @@
|
|||||||
|
package org.enso.distribution.locking
|
||||||
|
|
||||||
|
/** Represents a resource that can be locked. */
|
||||||
|
trait Resource {
|
||||||
|
|
||||||
|
/** Name of the resource.
|
||||||
|
*
|
||||||
|
* Must be a valid filename part.
|
||||||
|
*/
|
||||||
|
def name: String
|
||||||
|
|
||||||
|
/** A message that is displayed by default if the lock on that resource cannot
|
||||||
|
* be acquired immediately.
|
||||||
|
*/
|
||||||
|
def waitMessage: String
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package org.enso.runtimeversionmanager.locking
|
package org.enso.distribution.locking
|
||||||
|
|
||||||
import com.typesafe.scalalogging.Logger
|
import com.typesafe.scalalogging.Logger
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package org.enso.runtimeversionmanager.locking
|
package org.enso.distribution.locking
|
||||||
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
@ -0,0 +1,65 @@
|
|||||||
|
package org.enso.editions
|
||||||
|
|
||||||
|
import cats.Show
|
||||||
|
|
||||||
|
/** Indicates an error during resolution of a raw edition. */
|
||||||
|
sealed class EditionResolutionError(message: String, cause: Throwable = null)
|
||||||
|
extends RuntimeException(message, cause)
|
||||||
|
|
||||||
|
object EditionResolutionError {
|
||||||
|
|
||||||
|
/** Indicates that a parent edition referenced in one of the editions that are
|
||||||
|
* being loaded cannot be loaded.
|
||||||
|
*/
|
||||||
|
case class CannotLoadEdition(name: String, cause: Throwable)
|
||||||
|
extends EditionResolutionError(
|
||||||
|
s"Cannot load the edition: ${cause.getMessage}",
|
||||||
|
cause
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Indicates that the edition cannot be parsed. */
|
||||||
|
case class EditionParseError(message: String, cause: Throwable)
|
||||||
|
extends EditionResolutionError(message, cause)
|
||||||
|
|
||||||
|
/** Indicates that a library defined in an edition references a repository
|
||||||
|
* that is not defined in that edition or any of its parents, and so such a
|
||||||
|
* reference is invalid.
|
||||||
|
*/
|
||||||
|
case class LibraryReferencesUndefinedRepository(
|
||||||
|
libraryName: String,
|
||||||
|
repositoryName: String
|
||||||
|
) extends EditionResolutionError(
|
||||||
|
s"A library `$libraryName` references a repository `$repositoryName` " +
|
||||||
|
s"that is not defined in the edition or its parents."
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Indicates that the chain of parent editions forms a cycle which is not
|
||||||
|
* allowed.
|
||||||
|
*/
|
||||||
|
case class EditionResolutionCycle(editions: List[String])
|
||||||
|
extends EditionResolutionError(
|
||||||
|
s"Edition resolution encountered a cycle: ${editions.mkString(" -> ")}"
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Wraps a Circe's decoding error into a more user-friendly error. */
|
||||||
|
def wrapDecodingError(decodingError: io.circe.Error): EditionParseError = {
|
||||||
|
val errorMessage =
|
||||||
|
implicitly[Show[io.circe.Error]].show(decodingError)
|
||||||
|
EditionParseError(
|
||||||
|
s"Could not parse the edition: $errorMessage",
|
||||||
|
decodingError
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Wraps a general error thrown when loading a parsing an edition into a more
|
||||||
|
* specific error type.
|
||||||
|
*/
|
||||||
|
def wrapLoadingError(
|
||||||
|
editionName: String,
|
||||||
|
throwable: Throwable
|
||||||
|
): EditionResolutionError =
|
||||||
|
throwable match {
|
||||||
|
case decodingError: io.circe.Error => wrapDecodingError(decodingError)
|
||||||
|
case other => CannotLoadEdition(editionName, other)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,155 @@
|
|||||||
|
package org.enso.editions
|
||||||
|
|
||||||
|
import cats.implicits._
|
||||||
|
import org.enso.editions.EditionResolutionError.{
|
||||||
|
EditionResolutionCycle,
|
||||||
|
LibraryReferencesUndefinedRepository
|
||||||
|
}
|
||||||
|
import org.enso.editions.Editions.{RawEdition, ResolvedEdition}
|
||||||
|
import org.enso.editions.provider.EditionProvider
|
||||||
|
|
||||||
|
import scala.annotation.tailrec
|
||||||
|
|
||||||
|
/** A helper class that allows to resolve a [[RawEdition]] into a
|
||||||
|
* [[ResolvedEdition]] by loading its parents and resolving the repository
|
||||||
|
* references.
|
||||||
|
*
|
||||||
|
* @param provider an [[EditionProvider]] that is used for loading the
|
||||||
|
* referenced parent editions
|
||||||
|
*/
|
||||||
|
case class EditionResolver(provider: EditionProvider) {
|
||||||
|
|
||||||
|
/** Runs the edition resolution.
|
||||||
|
*
|
||||||
|
* @param edition the raw edition to resolve
|
||||||
|
* @return either a resolution error or the resolved edition
|
||||||
|
*/
|
||||||
|
def resolve(
|
||||||
|
edition: RawEdition
|
||||||
|
): Either[EditionResolutionError, ResolvedEdition] =
|
||||||
|
resolveEdition(edition, Nil)
|
||||||
|
|
||||||
|
/** A helper method that resolves an edition and keeps a list of already
|
||||||
|
* visited edition names to avoid cycles.
|
||||||
|
*/
|
||||||
|
private def resolveEdition(
|
||||||
|
edition: RawEdition,
|
||||||
|
visitedEditions: List[String]
|
||||||
|
): Either[EditionResolutionError, ResolvedEdition] =
|
||||||
|
for {
|
||||||
|
parent <- resolveParent(edition.parent, visitedEditions)
|
||||||
|
libraries <- resolveLibraries(
|
||||||
|
edition.libraries,
|
||||||
|
edition.repositories,
|
||||||
|
parent
|
||||||
|
)
|
||||||
|
} yield Editions.Resolved.Edition(
|
||||||
|
parent = parent,
|
||||||
|
engineVersion = edition.engineVersion,
|
||||||
|
repositories = edition.repositories,
|
||||||
|
libraries = libraries
|
||||||
|
)
|
||||||
|
|
||||||
|
/** A helper method for resolving libraries.
|
||||||
|
*
|
||||||
|
* @param libraries the raw mapping of libraries
|
||||||
|
* @param currentRepositories the mapping of repositories defined in the
|
||||||
|
* current edition
|
||||||
|
* @param parent an optional reference to an (already resolved) parent
|
||||||
|
* edition, which is used if the library references a
|
||||||
|
* repository that was not defined in `currentRepositories`
|
||||||
|
* @return either an error indicating a reference to an unknown repository or
|
||||||
|
* a mapping of resolved libraries
|
||||||
|
*/
|
||||||
|
private def resolveLibraries(
|
||||||
|
libraries: Map[String, Editions.Raw.Library],
|
||||||
|
currentRepositories: Map[String, Editions.Repository],
|
||||||
|
parent: Option[ResolvedEdition]
|
||||||
|
): Either[
|
||||||
|
LibraryReferencesUndefinedRepository,
|
||||||
|
Map[String, Editions.Resolved.Library]
|
||||||
|
] = {
|
||||||
|
val resolvedPairs: Either[
|
||||||
|
LibraryReferencesUndefinedRepository,
|
||||||
|
List[(String, Editions.Resolved.Library)]
|
||||||
|
] =
|
||||||
|
libraries.toList.traverse { case (name, library) =>
|
||||||
|
val resolved = resolveLibrary(library, currentRepositories, parent)
|
||||||
|
resolved.map((name, _))
|
||||||
|
}
|
||||||
|
|
||||||
|
resolvedPairs.map(Map.from)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A helper method to resolve a single library.
|
||||||
|
*
|
||||||
|
* @param library the library to resolve
|
||||||
|
* @param currentRepositories the mapping of repositories defined in the
|
||||||
|
* current edition
|
||||||
|
* @param parent an optional reference to an (already resolved) parent
|
||||||
|
* edition, which is used if the library references a
|
||||||
|
* repository that was not defined in `currentRepositories`
|
||||||
|
* @return either an error or the resolved library
|
||||||
|
*/
|
||||||
|
@tailrec
|
||||||
|
private def resolveLibrary(
|
||||||
|
library: Editions.Raw.Library,
|
||||||
|
currentRepositories: Map[String, Editions.Repository],
|
||||||
|
parent: Option[ResolvedEdition]
|
||||||
|
): Either[
|
||||||
|
LibraryReferencesUndefinedRepository,
|
||||||
|
Editions.Resolved.Library
|
||||||
|
] = library match {
|
||||||
|
case Editions.Raw.LocalLibrary(qualifiedName) =>
|
||||||
|
Right(Editions.Resolved.LocalLibrary(qualifiedName))
|
||||||
|
case Editions.Raw.PublishedLibrary(
|
||||||
|
qualifiedName,
|
||||||
|
version,
|
||||||
|
repositoryName
|
||||||
|
) =>
|
||||||
|
(currentRepositories.get(repositoryName), parent) match {
|
||||||
|
case (Some(repository), _) =>
|
||||||
|
Right(
|
||||||
|
Editions.Resolved
|
||||||
|
.PublishedLibrary(qualifiedName, version, repository)
|
||||||
|
)
|
||||||
|
case (None, Some(parentEdition)) =>
|
||||||
|
resolveLibrary(
|
||||||
|
library,
|
||||||
|
parentEdition.repositories,
|
||||||
|
parentEdition.parent
|
||||||
|
)
|
||||||
|
case (None, None) =>
|
||||||
|
Left(
|
||||||
|
LibraryReferencesUndefinedRepository(
|
||||||
|
libraryName = library.qualifiedName,
|
||||||
|
repositoryName = repositoryName
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A helper function that takes an optional parent name and list of already
|
||||||
|
* seen editions and tries to load the parent (if present), but avoiding
|
||||||
|
* cycles (returning an error if a cycle was encountered).
|
||||||
|
*/
|
||||||
|
private def resolveParent(
|
||||||
|
parent: Option[String],
|
||||||
|
visitedEditions: List[String]
|
||||||
|
): Either[EditionResolutionError, Option[ResolvedEdition]] = parent match {
|
||||||
|
case Some(parentName) =>
|
||||||
|
if (visitedEditions.contains(parentName)) {
|
||||||
|
val cycle = parentName :: visitedEditions
|
||||||
|
Left(EditionResolutionCycle(cycle.reverse))
|
||||||
|
} else
|
||||||
|
for {
|
||||||
|
rawParent <- provider
|
||||||
|
.findEditionForName(parentName)
|
||||||
|
.toEither
|
||||||
|
.left
|
||||||
|
.map(EditionResolutionError.CannotLoadEdition(parentName, _))
|
||||||
|
res <- resolveEdition(rawParent, parentName :: visitedEditions)
|
||||||
|
} yield Some(res)
|
||||||
|
case None => Right(None)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,259 @@
|
|||||||
|
package org.enso.editions
|
||||||
|
|
||||||
|
import cats.Show
|
||||||
|
import io.circe.syntax.EncoderOps
|
||||||
|
import io.circe._
|
||||||
|
import nl.gn0s1s.bump.SemVer
|
||||||
|
import org.enso.editions.Editions.{Raw, Repository}
|
||||||
|
import org.enso.editions.SemVerJson._
|
||||||
|
|
||||||
|
import java.io.FileReader
|
||||||
|
import java.net.URL
|
||||||
|
import java.nio.file.Path
|
||||||
|
import scala.util.{Failure, Try, Using}
|
||||||
|
|
||||||
|
/** Gathers methods for decoding and encoding of Raw editions. */
|
||||||
|
object EditionSerialization {
|
||||||
|
|
||||||
|
/** Tries to parse an edition definition from a string in the YAML format. */
|
||||||
|
def parseYamlString(yamlString: String): Try[Raw.Edition] =
|
||||||
|
yaml.parser
|
||||||
|
.parse(yamlString)
|
||||||
|
.flatMap(_.as[Raw.Edition])
|
||||||
|
.left
|
||||||
|
.map(EditionResolutionError.wrapDecodingError)
|
||||||
|
.toTry
|
||||||
|
|
||||||
|
/** Tries to load an edition definition from a YAML file. */
|
||||||
|
def loadEdition(path: Path): Try[Raw.Edition] =
|
||||||
|
Using(new FileReader(path.toFile)) { reader =>
|
||||||
|
yaml.parser
|
||||||
|
.parse(reader)
|
||||||
|
.flatMap(_.as[Raw.Edition])
|
||||||
|
.toTry
|
||||||
|
}.flatten
|
||||||
|
.recoverWith { error =>
|
||||||
|
Failure(
|
||||||
|
EditionResolutionError.wrapLoadingError(
|
||||||
|
path.getFileName.toString,
|
||||||
|
error
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A [[Decoder]] instance for [[Raw.Edition]].
|
||||||
|
*
|
||||||
|
* It can be used to decode nested editions in other kinds of configurations
|
||||||
|
* files, for example in `package.yaml`.
|
||||||
|
*/
|
||||||
|
implicit val editionDecoder: Decoder[Raw.Edition] = { json =>
|
||||||
|
for {
|
||||||
|
parent <- json.get[Option[EditionName]](Fields.parent)
|
||||||
|
engineVersion <- json.get[Option[EnsoVersion]](Fields.engineVersion)
|
||||||
|
_ <-
|
||||||
|
if (parent.isEmpty && engineVersion.isEmpty)
|
||||||
|
Left(
|
||||||
|
DecodingFailure(
|
||||||
|
s"The edition must specify at least one of " +
|
||||||
|
s"${Fields.engineVersion} or ${Fields.parent}.",
|
||||||
|
json.history
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else Right(())
|
||||||
|
repositories <-
|
||||||
|
json.getOrElse[Seq[Repository]](Fields.repositories)(Seq())
|
||||||
|
libraries <- json.getOrElse[Seq[Raw.Library]](Fields.libraries)(Seq())
|
||||||
|
res <- {
|
||||||
|
val repositoryMap = Map.from(repositories.map(r => (r.name, r)))
|
||||||
|
val libraryMap = Map.from(libraries.map(l => (l.qualifiedName, l)))
|
||||||
|
if (libraryMap.size != libraries.size)
|
||||||
|
Left(
|
||||||
|
DecodingFailure(
|
||||||
|
"Names of libraries defined within a single edition file must be unique.",
|
||||||
|
json.downField(Fields.libraries).history
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else if (repositoryMap.size != repositories.size)
|
||||||
|
Left(
|
||||||
|
DecodingFailure(
|
||||||
|
"Names of repositories defined within a single edition file must be unique.",
|
||||||
|
json.downField(Fields.libraries).history
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Right(
|
||||||
|
Raw.Edition(
|
||||||
|
parent = parent.map(_.name),
|
||||||
|
engineVersion = engineVersion,
|
||||||
|
repositories = repositoryMap,
|
||||||
|
libraries = libraryMap
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} yield res
|
||||||
|
}
|
||||||
|
|
||||||
|
/** An [[Encoder]] instance for [[Raw.Edition]]. */
|
||||||
|
implicit val editionEncoder: Encoder[Raw.Edition] = { edition =>
|
||||||
|
val parent = edition.parent.map { parent => Fields.parent -> parent.asJson }
|
||||||
|
val engineVersion = edition.engineVersion.map { version =>
|
||||||
|
Fields.engineVersion -> version.asJson
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent.isEmpty && engineVersion.isEmpty) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Internal error: An edition must specify at least the engine version or extends clause"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val repositories =
|
||||||
|
if (edition.repositories.isEmpty) None
|
||||||
|
else Some(Fields.repositories -> edition.repositories.values.asJson)
|
||||||
|
val libraries =
|
||||||
|
if (edition.libraries.isEmpty) None
|
||||||
|
else Some(Fields.libraries -> edition.libraries.values.asJson)
|
||||||
|
Json.obj(
|
||||||
|
parent.toSeq ++ engineVersion.toSeq ++ repositories.toSeq ++ libraries.toSeq: _*
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
object Fields {
|
||||||
|
val name = "name"
|
||||||
|
val version = "version"
|
||||||
|
val repository = "repository"
|
||||||
|
val url = "url"
|
||||||
|
val parent = "extends"
|
||||||
|
val engineVersion = "engine-version"
|
||||||
|
val repositories = "repositories"
|
||||||
|
val libraries = "libraries"
|
||||||
|
val localRepositoryName = "local"
|
||||||
|
}
|
||||||
|
|
||||||
|
case class EditionLoadingError(message: String, cause: Throwable)
|
||||||
|
extends RuntimeException(message, cause) {
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
override def toString: String = message
|
||||||
|
}
|
||||||
|
|
||||||
|
object EditionLoadingError {
|
||||||
|
|
||||||
|
/** Creates a [[EditionLoadingError]] by wrapping another [[Throwable]].
|
||||||
|
*
|
||||||
|
* Special logic is used for [[io.circe.Error]] to display the error
|
||||||
|
* summary in a human-readable way.
|
||||||
|
*/
|
||||||
|
def fromThrowable(throwable: Throwable): EditionLoadingError =
|
||||||
|
throwable match {
|
||||||
|
case decodingError: io.circe.Error =>
|
||||||
|
val errorMessage =
|
||||||
|
implicitly[Show[io.circe.Error]].show(decodingError)
|
||||||
|
EditionLoadingError(
|
||||||
|
s"Could not parse the edition file: $errorMessage",
|
||||||
|
decodingError
|
||||||
|
)
|
||||||
|
case other =>
|
||||||
|
EditionLoadingError(s"Could not load the edition file: $other", other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A helper opaque type to handle special parsing logic of edition names.
|
||||||
|
*
|
||||||
|
* The issue is that if an edition is called `2021.4` and it is written
|
||||||
|
* unquoted inside of a YAML file, that is treated as a floating point
|
||||||
|
* number, so special care must be taken to correctly parse it.
|
||||||
|
*/
|
||||||
|
private case class EditionName(name: String)
|
||||||
|
|
||||||
|
implicit private val editionNameDecoder: Decoder[EditionName] = { json =>
|
||||||
|
json
|
||||||
|
.as[String]
|
||||||
|
.orElse(json.as[Int].map(_.toString))
|
||||||
|
.orElse(json.as[Float].map(_.toString))
|
||||||
|
.map(EditionName)
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit private val libraryDecoder: Decoder[Raw.Library] = { json =>
|
||||||
|
def makeLibrary(name: String, repository: String, version: Option[SemVer]) =
|
||||||
|
if (repository == Fields.localRepositoryName)
|
||||||
|
if (version.isDefined)
|
||||||
|
Left(
|
||||||
|
DecodingFailure(
|
||||||
|
"Version field must not be set for libraries associated with the local repository.",
|
||||||
|
json.history
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else Right(Raw.LocalLibrary(name))
|
||||||
|
else {
|
||||||
|
version match {
|
||||||
|
case Some(versionValue) =>
|
||||||
|
Right(Raw.PublishedLibrary(name, versionValue, repository))
|
||||||
|
case None =>
|
||||||
|
Left(
|
||||||
|
DecodingFailure(
|
||||||
|
"Version field is mandatory for non-local libraries.",
|
||||||
|
json.history
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
name <- json.get[String](Fields.name)
|
||||||
|
repository <- json.get[String](Fields.repository)
|
||||||
|
version <- json.get[Option[SemVer]](Fields.version)
|
||||||
|
res <- makeLibrary(name, repository, version)
|
||||||
|
} yield res
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit private val libraryEncoder: Encoder[Raw.Library] = {
|
||||||
|
case Raw.LocalLibrary(name) =>
|
||||||
|
Json.obj(
|
||||||
|
Fields.name -> name.asJson,
|
||||||
|
Fields.repository -> Fields.localRepositoryName.asJson
|
||||||
|
)
|
||||||
|
case Raw.PublishedLibrary(name, version, repository) =>
|
||||||
|
Json.obj(
|
||||||
|
Fields.name -> name.asJson,
|
||||||
|
Fields.version -> version.asJson,
|
||||||
|
Fields.repository -> repository.asJson
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit private val urlEncoder: Encoder[URL] = { url => url.toString.asJson }
|
||||||
|
|
||||||
|
implicit private val urlDecoder: Decoder[URL] = { json =>
|
||||||
|
json.as[String].flatMap { str =>
|
||||||
|
Try(new URL(str)).toEither.left.map(throwable =>
|
||||||
|
DecodingFailure(
|
||||||
|
s"Cannot parse an URL: ${throwable.getMessage}",
|
||||||
|
json.history
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit private val repositoryEncoder: Encoder[Repository] = { repo =>
|
||||||
|
Json.obj(
|
||||||
|
Fields.name -> repo.name.asJson,
|
||||||
|
Fields.url -> repo.url.asJson
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit private val repositoryDecoder: Decoder[Repository] = { json =>
|
||||||
|
val nameField = json.downField(Fields.name)
|
||||||
|
for {
|
||||||
|
name <- nameField.as[String]
|
||||||
|
url <- json.get[URL](Fields.url)
|
||||||
|
res <-
|
||||||
|
if (name == Fields.localRepositoryName)
|
||||||
|
Left(
|
||||||
|
DecodingFailure(
|
||||||
|
s"A defined repository cannot be called " +
|
||||||
|
s"`${Fields.localRepositoryName}` which is a reserved keyword.",
|
||||||
|
nameField.history
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else Right(Repository(name, url))
|
||||||
|
} yield res
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,147 @@
|
|||||||
|
package org.enso.editions
|
||||||
|
|
||||||
|
import nl.gn0s1s.bump.SemVer
|
||||||
|
|
||||||
|
import java.net.URL
|
||||||
|
import scala.util.Try
|
||||||
|
|
||||||
|
/** Defines the general edition structure.
|
||||||
|
*
|
||||||
|
* We split the data type into two categories: Raw and Resolved editions.
|
||||||
|
*
|
||||||
|
* Raw editions are directly parsed from a YAML structure and can be serialized
|
||||||
|
* back to it. They are just an object model of the underlying YAML format.
|
||||||
|
*
|
||||||
|
* The raw edition may reference a parent edition or repositories by name.
|
||||||
|
* The [[EditionResolver]] takes care of resolving a Raw edition into a
|
||||||
|
* Resolved instance by loading and parsing any of its parents and replacing
|
||||||
|
* the repository by-name references by actual references to the repository
|
||||||
|
* instances.
|
||||||
|
*/
|
||||||
|
trait Editions {
|
||||||
|
|
||||||
|
/** The type of nested editions.
|
||||||
|
*
|
||||||
|
* Raw editions will refer to parents by their name and the Resolved edition
|
||||||
|
* will contain a reference to an actual object.
|
||||||
|
*/
|
||||||
|
type NestedEditionType
|
||||||
|
|
||||||
|
/** The type of repository reference in listed libraries.
|
||||||
|
*
|
||||||
|
* Libraries in Raw editions will refer to repositories by their name and the
|
||||||
|
* Resolved variant will contain a reference to the actual repository object
|
||||||
|
* loaded from the edition or one of its parents.
|
||||||
|
*/
|
||||||
|
type LibraryRepositoryType
|
||||||
|
|
||||||
|
/** The library description included in the edition. */
|
||||||
|
sealed trait Library {
|
||||||
|
|
||||||
|
/** The qualified name of the library.
|
||||||
|
*
|
||||||
|
* It should consist of a prefix followed by a dot an the library name, for
|
||||||
|
* example `Prefix.Library_Name`.
|
||||||
|
*/
|
||||||
|
def qualifiedName: String
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Represents a local library. */
|
||||||
|
case class LocalLibrary(override val qualifiedName: String) extends Library
|
||||||
|
|
||||||
|
/** Represents a specific version of the library that is published in a
|
||||||
|
* repository.
|
||||||
|
*
|
||||||
|
* @param qualifiedName the qualified name of the library
|
||||||
|
* @param version the exact version of the library that should be used
|
||||||
|
* @param repository the recommended repository to download the library from,
|
||||||
|
* if it is not yet cached
|
||||||
|
*/
|
||||||
|
case class PublishedLibrary(
|
||||||
|
override val qualifiedName: String,
|
||||||
|
version: SemVer,
|
||||||
|
repository: LibraryRepositoryType
|
||||||
|
) extends Library
|
||||||
|
|
||||||
|
/** An Edition describing the library resolution configuration.
|
||||||
|
*
|
||||||
|
* @param parent a parent edition (if applicable)
|
||||||
|
* @param engineVersion an engine version; it should be defined if the
|
||||||
|
* edition wants to override the setting from the parent
|
||||||
|
* or if it has no parents
|
||||||
|
* @param repositories a mapping of repositories directly defined in the
|
||||||
|
* edition (does not include ones defined in the parents)
|
||||||
|
* @param libraries a mapping of libraries directly defined in the edition
|
||||||
|
* (does not include ones defined in the parents)
|
||||||
|
*/
|
||||||
|
case class Edition(
|
||||||
|
parent: Option[NestedEditionType],
|
||||||
|
engineVersion: Option[EnsoVersion],
|
||||||
|
repositories: Map[String, Editions.Repository],
|
||||||
|
libraries: Map[String, Library]
|
||||||
|
) {
|
||||||
|
if (parent.isEmpty && engineVersion.isEmpty)
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"The edition must specify the engine version or a parent edition " +
|
||||||
|
"that will imply it."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object Editions {
|
||||||
|
|
||||||
|
/** Represents a repository that provides libraries. */
|
||||||
|
case class Repository(name: String, url: URL)
|
||||||
|
|
||||||
|
object Repository {
|
||||||
|
|
||||||
|
/** A helper function that creates a Repository instance from a raw string
|
||||||
|
* URL.
|
||||||
|
*/
|
||||||
|
def make(name: String, url: String): Try[Repository] = Try {
|
||||||
|
Repository(name, new URL(url))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Implements the Raw editions that can be directly parsed from a YAML
|
||||||
|
* configuration.
|
||||||
|
*
|
||||||
|
* All references are typed as String, because this is what is directly read
|
||||||
|
* from the configuration, without any postprocessing.
|
||||||
|
*/
|
||||||
|
object Raw extends Editions {
|
||||||
|
override type NestedEditionType = String
|
||||||
|
override type LibraryRepositoryType = String
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Implements the Resolved editions which are obtained by analyzing the Raw
|
||||||
|
* edition and loading any of its parents.
|
||||||
|
*/
|
||||||
|
object Resolved extends Editions {
|
||||||
|
override type NestedEditionType = this.Edition
|
||||||
|
override type LibraryRepositoryType = Repository
|
||||||
|
}
|
||||||
|
|
||||||
|
/** An alias for Raw editions. */
|
||||||
|
type RawEdition = Raw.Edition
|
||||||
|
|
||||||
|
/** An alias for Resolved editions. */
|
||||||
|
type ResolvedEdition = Resolved.Edition
|
||||||
|
|
||||||
|
/** Syntax helpers for resolved edition. */
|
||||||
|
implicit class ResolvedEditionOps(edition: ResolvedEdition) {
|
||||||
|
|
||||||
|
/** Resolves the engine version that is implied by this resolved edition. It
|
||||||
|
* is either the version override directly specified in the edition or the
|
||||||
|
* version implied by its parent.
|
||||||
|
*/
|
||||||
|
def getEngineVersion: EnsoVersion = edition.engineVersion.getOrElse {
|
||||||
|
val parent = edition.parent.getOrElse {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Internal error: Resolved edition does not imply an engine version."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
parent.getEngineVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package org.enso.editions
|
||||||
|
|
||||||
|
import org.enso.editions.Editions.RawEdition
|
||||||
|
import org.enso.editions.provider.EditionProvider
|
||||||
|
|
||||||
|
/** A helper class which resolves the engine version that is entailed by the
|
||||||
|
* edition configuration.
|
||||||
|
*
|
||||||
|
* It requires an [[EditionProvider]] instance, because the edition may not
|
||||||
|
* specify the engine version directly, but it can just rely on what is
|
||||||
|
* specified in a parent edition, so it needs a way to load the parent
|
||||||
|
* editions.
|
||||||
|
*/
|
||||||
|
case class EngineVersionResolver(editionProvider: EditionProvider) {
|
||||||
|
private val editionResolver = EditionResolver(editionProvider)
|
||||||
|
|
||||||
|
/** Returns the [[EnsoVersion]] that is entailed by the provided edition (or a
|
||||||
|
* resolution error if the edition cannot be resolved).
|
||||||
|
*/
|
||||||
|
def resolveEnsoVersion(
|
||||||
|
edition: RawEdition
|
||||||
|
): Either[EditionResolutionError, EnsoVersion] = {
|
||||||
|
for {
|
||||||
|
edition <- editionResolver.resolve(edition)
|
||||||
|
} yield edition.getEngineVersion
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package org.enso.pkg
|
package org.enso.editions
|
||||||
|
|
||||||
import io.circe.syntax._
|
import io.circe.syntax._
|
||||||
import io.circe.{Decoder, DecodingFailure, Encoder}
|
import io.circe.{Decoder, DecodingFailure, Encoder}
|
@ -0,0 +1,16 @@
|
|||||||
|
package org.enso.editions
|
||||||
|
|
||||||
|
/** Represents a library name that should uniquely identify the library.
|
||||||
|
*
|
||||||
|
* The prefix is either a special prefix or a username.
|
||||||
|
*/
|
||||||
|
case class LibraryName(prefix: String, name: String) {
|
||||||
|
|
||||||
|
/** The qualified name of the library consists of its prefix and name
|
||||||
|
* separated with a dot.
|
||||||
|
*/
|
||||||
|
def qualifiedName: String = s"$prefix.$name"
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
override def toString: String = qualifiedName
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package org.enso.pkg
|
package org.enso.editions
|
||||||
|
|
||||||
import io.circe.{Decoder, DecodingFailure, Encoder}
|
|
||||||
import io.circe.syntax._
|
import io.circe.syntax._
|
||||||
|
import io.circe.{Decoder, DecodingFailure, Encoder}
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
|
|
||||||
object SemVerJson {
|
object SemVerJson {
|
@ -0,0 +1,16 @@
|
|||||||
|
package org.enso.editions.provider
|
||||||
|
|
||||||
|
import org.enso.editions.Editions
|
||||||
|
|
||||||
|
import scala.util.Try
|
||||||
|
|
||||||
|
/** Interface for a provider of editions which is able to load a raw edition
|
||||||
|
* based on its name.
|
||||||
|
*
|
||||||
|
* It is used when resolving parent edition references.
|
||||||
|
*/
|
||||||
|
trait EditionProvider {
|
||||||
|
|
||||||
|
/** Tries to load an edition with the given name. */
|
||||||
|
def findEditionForName(name: String): Try[Editions.Raw.Edition]
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
package org.enso.editions.provider
|
||||||
|
|
||||||
|
import org.enso.editions.{EditionSerialization, Editions}
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException
|
||||||
|
import java.nio.file.{Files, Path}
|
||||||
|
import scala.annotation.tailrec
|
||||||
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
||||||
|
/** An implementation of [[EditionProvider]] that looks for the edition files in
|
||||||
|
* a list of filesystem paths.
|
||||||
|
*/
|
||||||
|
case class FileSystemEditionProvider(searchPaths: List[Path])
|
||||||
|
extends EditionProvider {
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
override def findEditionForName(name: String): Try[Editions.Raw.Edition] = {
|
||||||
|
val result = findEdition(name, searchPaths)
|
||||||
|
result match {
|
||||||
|
case Left(EditionNotFound) =>
|
||||||
|
Failure(new FileNotFoundException(s"Could not find edition `$name`."))
|
||||||
|
case Left(EditionReadError(error)) => Failure(error)
|
||||||
|
case Right(value) => Success(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val editionSuffix = ".yaml"
|
||||||
|
|
||||||
|
@tailrec
|
||||||
|
private def findEdition(
|
||||||
|
name: String,
|
||||||
|
paths: List[Path]
|
||||||
|
): Either[EditionLoadingError, Editions.Raw.Edition] = paths match {
|
||||||
|
case head :: tail =>
|
||||||
|
val headResult = loadEdition(name, head)
|
||||||
|
headResult match {
|
||||||
|
case Left(EditionNotFound) =>
|
||||||
|
findEdition(name, tail)
|
||||||
|
case _ => headResult
|
||||||
|
}
|
||||||
|
case Nil => Left(EditionNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed private trait EditionLoadingError
|
||||||
|
private case object EditionNotFound extends EditionLoadingError
|
||||||
|
private case class EditionReadError(error: Throwable)
|
||||||
|
extends EditionLoadingError
|
||||||
|
|
||||||
|
private def loadEdition(
|
||||||
|
name: String,
|
||||||
|
path: Path
|
||||||
|
): Either[EditionLoadingError, Editions.Raw.Edition] = {
|
||||||
|
val fileName = name + editionSuffix
|
||||||
|
val editionPath = path.resolve(fileName)
|
||||||
|
if (Files.exists(editionPath)) {
|
||||||
|
EditionSerialization
|
||||||
|
.loadEdition(editionPath)
|
||||||
|
.toEither
|
||||||
|
.left
|
||||||
|
.map(EditionReadError)
|
||||||
|
} else Left(EditionNotFound)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,157 @@
|
|||||||
|
package org.enso.editions
|
||||||
|
|
||||||
|
import nl.gn0s1s.bump.SemVer
|
||||||
|
import org.enso.editions.EditionResolutionError.EditionResolutionCycle
|
||||||
|
import org.enso.editions.Editions.Repository
|
||||||
|
import org.enso.editions.provider.EditionProvider
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import org.scalatest.wordspec.AnyWordSpec
|
||||||
|
import org.scalatest.{Inside, OptionValues}
|
||||||
|
|
||||||
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
||||||
|
class EditionResolverSpec
|
||||||
|
extends AnyWordSpec
|
||||||
|
with Matchers
|
||||||
|
with Inside
|
||||||
|
with OptionValues {
|
||||||
|
object FakeEditionProvider extends EditionProvider {
|
||||||
|
val mainRepo = Repository.make("main", "https://example.com/main").get
|
||||||
|
val editions: Map[String, Editions.RawEdition] = Map(
|
||||||
|
"2021.0" -> Editions.Raw.Edition(
|
||||||
|
parent = None,
|
||||||
|
engineVersion = Some(SemVerEnsoVersion(SemVer(1, 2, 3))),
|
||||||
|
repositories = Map(
|
||||||
|
"main" -> mainRepo
|
||||||
|
),
|
||||||
|
libraries = Map(
|
||||||
|
"Standard.Base" -> Editions.Raw
|
||||||
|
.PublishedLibrary("Standard.Base", SemVer(1, 2, 3), "main")
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"cycleA" -> Editions.Raw.Edition(
|
||||||
|
parent = Some("cycleB"),
|
||||||
|
engineVersion = Some(SemVerEnsoVersion(SemVer(1, 2, 3))),
|
||||||
|
repositories = Map(),
|
||||||
|
libraries = Map()
|
||||||
|
),
|
||||||
|
"cycleB" -> Editions.Raw.Edition(
|
||||||
|
parent = Some("cycleA"),
|
||||||
|
engineVersion = Some(SemVerEnsoVersion(SemVer(1, 2, 3))),
|
||||||
|
repositories = Map(),
|
||||||
|
libraries = Map()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
override def findEditionForName(name: String): Try[Editions.Raw.Edition] =
|
||||||
|
editions
|
||||||
|
.get(name)
|
||||||
|
.map(Success(_))
|
||||||
|
.getOrElse(
|
||||||
|
Failure(new RuntimeException(s"Could not load edition `$name`."))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val resolver = EditionResolver(FakeEditionProvider)
|
||||||
|
|
||||||
|
"EditionResolver" should {
|
||||||
|
"resolve a simple edition" in {
|
||||||
|
val repo = Repository.make("foo", "http://example.com").get
|
||||||
|
val edition = Editions.Raw.Edition(
|
||||||
|
parent = None,
|
||||||
|
engineVersion = Some(SemVerEnsoVersion(SemVer(1, 2, 3))),
|
||||||
|
repositories = Map("foo" -> repo),
|
||||||
|
libraries = Map(
|
||||||
|
"bar.baz" -> Editions.Raw.LocalLibrary("bar.baz"),
|
||||||
|
"foo.bar" -> Editions.Raw
|
||||||
|
.PublishedLibrary("foo.bar", SemVer(1, 2, 3), "foo")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
inside(resolver.resolve(edition)) { case Right(resolved) =>
|
||||||
|
resolved.engineVersion shouldEqual edition.engineVersion
|
||||||
|
resolved.parent should be(empty)
|
||||||
|
resolved.repositories shouldEqual edition.repositories
|
||||||
|
resolved.libraries should have size 2
|
||||||
|
resolved.libraries("bar.baz") shouldEqual Editions.Resolved
|
||||||
|
.LocalLibrary("bar.baz")
|
||||||
|
resolved.libraries("foo.bar") shouldEqual Editions.Resolved
|
||||||
|
.PublishedLibrary("foo.bar", SemVer(1, 2, 3), repo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"resolve a nested edition" in {
|
||||||
|
val edition = Editions.Raw.Edition(
|
||||||
|
parent = Some("2021.0"),
|
||||||
|
engineVersion = Some(SemVerEnsoVersion(SemVer(1, 2, 3))),
|
||||||
|
repositories = Map(),
|
||||||
|
libraries = Map(
|
||||||
|
"bar.baz" -> Editions.Raw.LocalLibrary("bar.baz"),
|
||||||
|
"foo.bar" -> Editions.Raw
|
||||||
|
.PublishedLibrary("foo.bar", SemVer(1, 2, 3), "main")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
inside(resolver.resolve(edition)) { case Right(resolved) =>
|
||||||
|
resolved.parent should be(defined)
|
||||||
|
resolved.libraries should have size 2
|
||||||
|
resolved.libraries("bar.baz") shouldEqual Editions.Resolved
|
||||||
|
.LocalLibrary("bar.baz")
|
||||||
|
resolved.libraries("foo.bar") shouldEqual Editions.Resolved
|
||||||
|
.PublishedLibrary(
|
||||||
|
"foo.bar",
|
||||||
|
SemVer(1, 2, 3),
|
||||||
|
FakeEditionProvider.mainRepo
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"correctly handle repository shadowing when resolving libraries" in {
|
||||||
|
val localRepo = Repository.make("main", "http://example.com/local").get
|
||||||
|
|
||||||
|
localRepo should not equal FakeEditionProvider.mainRepo
|
||||||
|
|
||||||
|
val edition = Editions.Raw.Edition(
|
||||||
|
parent = Some("2021.0"),
|
||||||
|
engineVersion = None,
|
||||||
|
repositories = Map("main" -> localRepo),
|
||||||
|
libraries = Map(
|
||||||
|
"foo.bar" -> Editions.Raw
|
||||||
|
.PublishedLibrary("foo.bar", SemVer(1, 2, 3), "main")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
inside(resolver.resolve(edition)) { case Right(resolved) =>
|
||||||
|
resolved.parent should be(defined)
|
||||||
|
resolved.libraries should have size 1
|
||||||
|
resolved.libraries("foo.bar") shouldEqual
|
||||||
|
Editions.Resolved.PublishedLibrary(
|
||||||
|
"foo.bar",
|
||||||
|
SemVer(1, 2, 3),
|
||||||
|
localRepo
|
||||||
|
)
|
||||||
|
|
||||||
|
resolved.parent.value.libraries("Standard.Base") shouldEqual
|
||||||
|
Editions.Resolved.PublishedLibrary(
|
||||||
|
"Standard.Base",
|
||||||
|
SemVer(1, 2, 3),
|
||||||
|
FakeEditionProvider.mainRepo
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"avoid cycles in the resolution" in {
|
||||||
|
val edition = Editions.Raw.Edition(
|
||||||
|
parent = Some("cycleA"),
|
||||||
|
engineVersion = Some(SemVerEnsoVersion(SemVer(1, 2, 3))),
|
||||||
|
repositories = Map(),
|
||||||
|
libraries = Map()
|
||||||
|
)
|
||||||
|
|
||||||
|
val failure = resolver.resolve(edition)
|
||||||
|
inside(failure) { case Left(EditionResolutionCycle(editions)) =>
|
||||||
|
editions shouldEqual Seq("cycleA", "cycleB", "cycleA")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,121 @@
|
|||||||
|
package org.enso.editions
|
||||||
|
|
||||||
|
import nl.gn0s1s.bump.SemVer
|
||||||
|
import org.scalatest.Inside
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import org.scalatest.wordspec.AnyWordSpec
|
||||||
|
|
||||||
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
|
class EditionSerializationSpec extends AnyWordSpec with Matchers with Inside {
|
||||||
|
"EditionSerialization" should {
|
||||||
|
"parse an edition that just derives another one" in {
|
||||||
|
val parsed = EditionSerialization.parseYamlString(
|
||||||
|
"""extends: 2021.4
|
||||||
|
|""".stripMargin
|
||||||
|
)
|
||||||
|
inside(parsed) { case Success(edition) =>
|
||||||
|
edition.parent should contain("2021.4")
|
||||||
|
edition.repositories should be(empty)
|
||||||
|
edition.libraries should be(empty)
|
||||||
|
edition.engineVersion should be(empty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"parse a standalone edition" in {
|
||||||
|
val parsed = EditionSerialization.parseYamlString(
|
||||||
|
"""engine-version: 1.2.3-SNAPSHOT
|
||||||
|
|repositories:
|
||||||
|
| - name: example
|
||||||
|
| url: https://example.com/
|
||||||
|
| - name: foo
|
||||||
|
| url: http://127.0.0.1:8080/root
|
||||||
|
|libraries:
|
||||||
|
| - name: Foo.Local
|
||||||
|
| repository: local
|
||||||
|
| - name: Bar.Baz
|
||||||
|
| repository: example
|
||||||
|
| version: 0.0.0
|
||||||
|
| - name: A.B
|
||||||
|
| repository: bar
|
||||||
|
| version: 1.0.1
|
||||||
|
|""".stripMargin
|
||||||
|
)
|
||||||
|
inside(parsed) { case Success(edition) =>
|
||||||
|
edition.parent should be(empty)
|
||||||
|
edition.repositories.size shouldEqual 2
|
||||||
|
edition.repositories("example").name shouldEqual "example"
|
||||||
|
edition
|
||||||
|
.repositories("example")
|
||||||
|
.url
|
||||||
|
.toString shouldEqual "https://example.com/"
|
||||||
|
edition.repositories("foo").name shouldEqual "foo"
|
||||||
|
edition
|
||||||
|
.repositories("foo")
|
||||||
|
.url
|
||||||
|
.toString shouldEqual "http://127.0.0.1:8080/root"
|
||||||
|
|
||||||
|
edition.libraries.values should contain theSameElementsAs Seq(
|
||||||
|
Editions.Raw.LocalLibrary("Foo.Local"),
|
||||||
|
Editions.Raw.PublishedLibrary("Bar.Baz", SemVer(0, 0, 0), "example"),
|
||||||
|
Editions.Raw.PublishedLibrary("A.B", SemVer(1, 0, 1), "bar")
|
||||||
|
)
|
||||||
|
edition.engineVersion should contain(
|
||||||
|
SemVerEnsoVersion(SemVer(1, 2, 3, Some("SNAPSHOT")))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"not parse an edition that does not define the engine version nor attempt to derive it" in {
|
||||||
|
val parsed = EditionSerialization.parseYamlString(
|
||||||
|
"""libraries:
|
||||||
|
|- name: foo
|
||||||
|
| repository: local
|
||||||
|
|""".stripMargin
|
||||||
|
)
|
||||||
|
inside(parsed) { case Failure(exception) =>
|
||||||
|
exception.getMessage should include(
|
||||||
|
"The edition must specify at least one of"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"not allow invalid urls for repositories" in {
|
||||||
|
val parsed = EditionSerialization.parseYamlString(
|
||||||
|
"""extends: foo
|
||||||
|
|repositories:
|
||||||
|
|- name: bar
|
||||||
|
| url: baz
|
||||||
|
|""".stripMargin
|
||||||
|
)
|
||||||
|
inside(parsed) { case Failure(exception) =>
|
||||||
|
exception.getMessage should include("Cannot parse an URL")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"not allow invalid version combinations for libraries" in {
|
||||||
|
val parsed = EditionSerialization.parseYamlString(
|
||||||
|
"""extends: foo
|
||||||
|
|libraries:
|
||||||
|
|- name: bar
|
||||||
|
| repository: local
|
||||||
|
| version: 1.2.3-SHOULD-NOT-BE-HERE
|
||||||
|
|""".stripMargin
|
||||||
|
)
|
||||||
|
inside(parsed) { case Failure(exception) =>
|
||||||
|
exception.getMessage should include("Version field must not be set")
|
||||||
|
}
|
||||||
|
|
||||||
|
val parsed2 = EditionSerialization.parseYamlString(
|
||||||
|
"""extends: foo
|
||||||
|
|libraries:
|
||||||
|
|- name: bar
|
||||||
|
| repository: something
|
||||||
|
|""".stripMargin
|
||||||
|
)
|
||||||
|
inside(parsed2) { case Failure(exception) =>
|
||||||
|
exception.getMessage should include("Version field is mandatory")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
package org.enso.librarymanager
|
||||||
|
|
||||||
|
import org.enso.distribution.DistributionManager
|
||||||
|
import org.enso.editions.{Editions, LibraryName}
|
||||||
|
import org.enso.librarymanager.local.LocalLibraryProvider
|
||||||
|
import org.enso.librarymanager.published.{
|
||||||
|
DefaultPublishedLibraryProvider,
|
||||||
|
PublishedLibraryProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A helper class for loading libraries. */
|
||||||
|
case class LibraryLoader(distributionManager: DistributionManager) {
|
||||||
|
private val localLibraryProvider =
|
||||||
|
LocalLibraryProvider.make(distributionManager)
|
||||||
|
private val resolver = LibraryResolver(localLibraryProvider)
|
||||||
|
private val publishedLibraryProvider: PublishedLibraryProvider =
|
||||||
|
new DefaultPublishedLibraryProvider(distributionManager)
|
||||||
|
|
||||||
|
/** Resolves the library version that should be used based on the
|
||||||
|
* configuration and returns its location on the filesystem.
|
||||||
|
*
|
||||||
|
* If the library is not available, this operation may download it.
|
||||||
|
*/
|
||||||
|
def findLibrary(
|
||||||
|
libraryName: LibraryName,
|
||||||
|
edition: Editions.ResolvedEdition,
|
||||||
|
preferLocalLibraries: Boolean
|
||||||
|
): LibraryResolutionResult = {
|
||||||
|
val resolvedVersion = resolver
|
||||||
|
.resolveLibraryVersion(libraryName, edition, preferLocalLibraries)
|
||||||
|
resolvedVersion match {
|
||||||
|
case Left(error) =>
|
||||||
|
LibraryResolutionResult.ResolutionFailure(error)
|
||||||
|
case Right(LibraryVersion.Local) =>
|
||||||
|
localLibraryProvider
|
||||||
|
.findLibrary(libraryName)
|
||||||
|
.map(LibraryResolutionResult.ResolvedImmediately)
|
||||||
|
.getOrElse {
|
||||||
|
LibraryResolutionResult.ResolutionFailure(
|
||||||
|
LibraryResolutionError(
|
||||||
|
s"Edition configuration forces to use the local version, but " +
|
||||||
|
s"the `$libraryName` library is not present among local " +
|
||||||
|
s"libraries."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case Right(LibraryVersion.Published(version, repository)) =>
|
||||||
|
val dependencyResolver = { name: LibraryName =>
|
||||||
|
resolver
|
||||||
|
.resolveLibraryVersion(name, edition, preferLocalLibraries)
|
||||||
|
.toOption
|
||||||
|
}
|
||||||
|
publishedLibraryProvider.findLibrary(
|
||||||
|
libraryName,
|
||||||
|
version,
|
||||||
|
repository,
|
||||||
|
dependencyResolver
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package org.enso.librarymanager
|
||||||
|
|
||||||
|
/** Indicates an error of library resolution. */
|
||||||
|
case class LibraryResolutionError(message: String)
|
||||||
|
extends RuntimeException(message)
|
@ -0,0 +1,32 @@
|
|||||||
|
package org.enso.librarymanager
|
||||||
|
|
||||||
|
import org.enso.cli.task.TaskProgress
|
||||||
|
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
/** Encapsulates possible results of library resolution. */
|
||||||
|
sealed trait LibraryResolutionResult
|
||||||
|
object LibraryResolutionResult {
|
||||||
|
|
||||||
|
/** Indicates that the resolution has failed immediately with an error. */
|
||||||
|
case class ResolutionFailure(error: Throwable) extends LibraryResolutionResult
|
||||||
|
|
||||||
|
/** Indicates that the resolution has succeeded immediately.
|
||||||
|
*
|
||||||
|
* The library was already available on disk and the path to it is returned.
|
||||||
|
*/
|
||||||
|
case class ResolvedImmediately(path: Path) extends LibraryResolutionResult
|
||||||
|
|
||||||
|
/** Indicates that the initial library resolution has succeeded (that the
|
||||||
|
* corrrect version could be inferred from the edition) but the library was
|
||||||
|
* not installed, so it must be downloaded.
|
||||||
|
*
|
||||||
|
* It contains a [[TaskProgress]] instance that can track the progress of the
|
||||||
|
* download and will be completed once the library has been installed.
|
||||||
|
*
|
||||||
|
* The download and installation may of course fail as well, which is
|
||||||
|
* indicated by failure of the [[TaskProgress]].
|
||||||
|
*/
|
||||||
|
case class ResolutionPending(result: TaskProgress[Path])
|
||||||
|
extends LibraryResolutionResult
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
package org.enso.librarymanager
|
||||||
|
|
||||||
|
import org.enso.editions.{Editions, LibraryName}
|
||||||
|
import org.enso.librarymanager.local.LocalLibraryProvider
|
||||||
|
|
||||||
|
/** A helper class that figures out which library version should be used in a
|
||||||
|
* given configuration.
|
||||||
|
*
|
||||||
|
* It needs a [[LocalLibraryProvider]] instance, because if
|
||||||
|
* `preferLocalLibraries` is set to true, the resulting library version depends
|
||||||
|
* on whether a local library with a given name exists.
|
||||||
|
*/
|
||||||
|
case class LibraryResolver(
|
||||||
|
localLibraryProvider: LocalLibraryProvider
|
||||||
|
) {
|
||||||
|
|
||||||
|
/** Resolves the library version that entails from the provided configuration.
|
||||||
|
*
|
||||||
|
* The configuration consists of the edition and the flag specifying if local
|
||||||
|
* libraries should override the edition settings.
|
||||||
|
*
|
||||||
|
* @param libraryName the name of the library to resolve
|
||||||
|
* @param edition the edition configuration
|
||||||
|
* @param preferLocalLibraries the flag indicating whether to prefer local
|
||||||
|
* libraries that are present over what the
|
||||||
|
* edition defines
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
def resolveLibraryVersion(
|
||||||
|
libraryName: LibraryName,
|
||||||
|
edition: Editions.ResolvedEdition,
|
||||||
|
preferLocalLibraries: Boolean
|
||||||
|
): Either[LibraryResolutionError, LibraryVersion] = {
|
||||||
|
if (preferLocalLibraries) {
|
||||||
|
localLibraryProvider
|
||||||
|
.findLibrary(libraryName)
|
||||||
|
.map(_ => Right(LibraryVersion.Local))
|
||||||
|
.getOrElse {
|
||||||
|
resolveLibraryFromEdition(libraryName, edition)
|
||||||
|
}
|
||||||
|
} else resolveLibraryFromEdition(libraryName, edition)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Resolves the library version that entails from the edition.
|
||||||
|
*
|
||||||
|
* @param libraryName the name of the library to resolve
|
||||||
|
* @param edition the edition configuration
|
||||||
|
* @return either an error (if the library was not found in the edition) or
|
||||||
|
* the entailed version
|
||||||
|
*/
|
||||||
|
def resolveLibraryFromEdition(
|
||||||
|
libraryName: LibraryName,
|
||||||
|
edition: Editions.ResolvedEdition
|
||||||
|
): Either[LibraryResolutionError, LibraryVersion] = {
|
||||||
|
import Editions.Resolved._
|
||||||
|
val immediateResult =
|
||||||
|
edition.libraries.get(libraryName.qualifiedName).map {
|
||||||
|
case LocalLibrary(_) =>
|
||||||
|
Right(LibraryVersion.Local)
|
||||||
|
case PublishedLibrary(_, version, repository) =>
|
||||||
|
Right(LibraryVersion.Published(version, repository))
|
||||||
|
}
|
||||||
|
|
||||||
|
immediateResult.getOrElse {
|
||||||
|
edition.parent match {
|
||||||
|
case Some(parentEdition) =>
|
||||||
|
resolveLibraryFromEdition(libraryName, parentEdition)
|
||||||
|
case None =>
|
||||||
|
Left(
|
||||||
|
LibraryResolutionError(
|
||||||
|
s"The library `$libraryName` is not defined within " +
|
||||||
|
s"the edition."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package org.enso.librarymanager
|
||||||
|
|
||||||
|
import nl.gn0s1s.bump.SemVer
|
||||||
|
import org.enso.editions.Editions.Repository
|
||||||
|
|
||||||
|
/** A resolved version of the library. */
|
||||||
|
sealed trait LibraryVersion
|
||||||
|
object LibraryVersion {
|
||||||
|
|
||||||
|
/** Indicates that the version from the local library path should be used. */
|
||||||
|
case object Local extends LibraryVersion
|
||||||
|
|
||||||
|
/** Indicates that a particular published version should be used.
|
||||||
|
*
|
||||||
|
* It also indicates the repository to download the library from if it is not
|
||||||
|
* already cached.
|
||||||
|
*/
|
||||||
|
case class Published(version: SemVer, repository: Repository)
|
||||||
|
extends LibraryVersion
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package org.enso.librarymanager.local
|
||||||
|
|
||||||
|
import org.enso.distribution.DistributionManager
|
||||||
|
import org.enso.editions.LibraryName
|
||||||
|
import org.enso.distribution.FileSystem.PathSyntax
|
||||||
|
|
||||||
|
import java.nio.file.{Files, Path}
|
||||||
|
import scala.annotation.tailrec
|
||||||
|
|
||||||
|
/** A default implementation of [[LocalLibraryProvider]]. */
|
||||||
|
class DefaultLocalLibraryProvider(distributionManager: DistributionManager)
|
||||||
|
extends LocalLibraryProvider {
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
override def findLibrary(libraryName: LibraryName): Option[Path] =
|
||||||
|
findLibraryHelper(
|
||||||
|
libraryName,
|
||||||
|
distributionManager.paths.localLibrariesSearchPaths.toList
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Searches through the available library paths, checking if any one of them contains the requested library.
|
||||||
|
*
|
||||||
|
* The first path on the list takes precedence.
|
||||||
|
*/
|
||||||
|
@tailrec
|
||||||
|
private def findLibraryHelper(
|
||||||
|
libraryName: LibraryName,
|
||||||
|
searchPaths: List[Path]
|
||||||
|
): Option[Path] = searchPaths match {
|
||||||
|
case head :: tail =>
|
||||||
|
val potentialPath = head / libraryName.prefix / libraryName.name
|
||||||
|
if (Files.exists(potentialPath) && Files.isDirectory(potentialPath))
|
||||||
|
Some(potentialPath)
|
||||||
|
else findLibraryHelper(libraryName, tail)
|
||||||
|
case Nil => None
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package org.enso.librarymanager.local
|
||||||
|
|
||||||
|
import org.enso.distribution.DistributionManager
|
||||||
|
import org.enso.editions.LibraryName
|
||||||
|
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
/** A provider for local libraries. */
|
||||||
|
trait LocalLibraryProvider {
|
||||||
|
|
||||||
|
/** Returns the path to a local instance of the requested library, if it is
|
||||||
|
* available.
|
||||||
|
*/
|
||||||
|
def findLibrary(libraryName: LibraryName): Option[Path]
|
||||||
|
}
|
||||||
|
|
||||||
|
object LocalLibraryProvider {
|
||||||
|
|
||||||
|
/** Creates a default [[LocalLibraryProvider]] from a [[DistributionManager]].
|
||||||
|
*/
|
||||||
|
def make(distributionManager: DistributionManager): LocalLibraryProvider =
|
||||||
|
new DefaultLocalLibraryProvider(distributionManager)
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package org.enso.librarymanager.published
|
||||||
|
|
||||||
|
import nl.gn0s1s.bump.SemVer
|
||||||
|
import org.enso.distribution.DistributionManager
|
||||||
|
import org.enso.editions.{Editions, LibraryName}
|
||||||
|
import org.enso.librarymanager.{LibraryResolutionResult, LibraryVersion}
|
||||||
|
|
||||||
|
import scala.annotation.nowarn
|
||||||
|
|
||||||
|
/** A default implementation of [[PublishedLibraryProvider]]. */
|
||||||
|
class DefaultPublishedLibraryProvider(
|
||||||
|
@nowarn("msg=never used") distributionManager: DistributionManager
|
||||||
|
) extends PublishedLibraryProvider {
|
||||||
|
|
||||||
|
// TODO [RW] This is just a stub and will be properly implemented in #1772.
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
override def findLibrary(
|
||||||
|
libraryName: LibraryName,
|
||||||
|
version: SemVer,
|
||||||
|
recommendedRepository: Editions.Repository,
|
||||||
|
dependencyResolver: LibraryName => Option[LibraryVersion]
|
||||||
|
): LibraryResolutionResult = LibraryResolutionResult.ResolutionFailure(
|
||||||
|
new NotImplementedError(
|
||||||
|
"TODO library management is not yet implemented"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package org.enso.librarymanager.published
|
||||||
|
|
||||||
|
import nl.gn0s1s.bump.SemVer
|
||||||
|
import org.enso.editions.Editions.Repository
|
||||||
|
import org.enso.editions.LibraryName
|
||||||
|
import org.enso.librarymanager.{LibraryResolutionResult, LibraryVersion}
|
||||||
|
|
||||||
|
/** A provider of published libraries.
|
||||||
|
*
|
||||||
|
* It usually should use some kind of a cache to keep already downloaded
|
||||||
|
* libraries and download new libraries on demand.
|
||||||
|
*/
|
||||||
|
trait PublishedLibraryProvider {
|
||||||
|
|
||||||
|
/** Tries to locate the requested library at a specific version.
|
||||||
|
*
|
||||||
|
* If the library is not present, a download may be attempted - this is where
|
||||||
|
* the `recommendedRepository` is used to guide which repository should be
|
||||||
|
* used for the download.
|
||||||
|
*/
|
||||||
|
def findLibrary(
|
||||||
|
libraryName: LibraryName,
|
||||||
|
version: SemVer,
|
||||||
|
recommendedRepository: Repository,
|
||||||
|
dependencyResolver: LibraryName => Option[LibraryVersion]
|
||||||
|
): LibraryResolutionResult
|
||||||
|
}
|
@ -0,0 +1,146 @@
|
|||||||
|
package org.enso.librarymanager
|
||||||
|
|
||||||
|
import nl.gn0s1s.bump.SemVer
|
||||||
|
import org.enso.editions.Editions.Repository
|
||||||
|
import org.enso.editions.{DefaultEnsoVersion, Editions, LibraryName}
|
||||||
|
import org.enso.librarymanager.local.LocalLibraryProvider
|
||||||
|
import org.enso.testkit.EitherValue
|
||||||
|
import org.scalatest.Inside
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import org.scalatest.wordspec.AnyWordSpec
|
||||||
|
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
class LibraryResolverSpec
|
||||||
|
extends AnyWordSpec
|
||||||
|
with Matchers
|
||||||
|
with EitherValue
|
||||||
|
with Inside {
|
||||||
|
"LibraryResolver" should {
|
||||||
|
val mainRepo = Repository.make("main", "https://example.com/main").get
|
||||||
|
val parentEdition = Editions.Resolved.Edition(
|
||||||
|
parent = None,
|
||||||
|
engineVersion = Some(DefaultEnsoVersion),
|
||||||
|
repositories = Map("main" -> mainRepo),
|
||||||
|
libraries = Map(
|
||||||
|
"Standard.Base" -> Editions.Resolved
|
||||||
|
.PublishedLibrary("Standard.Base", SemVer(4, 5, 6), mainRepo)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val customRepo = Repository.make("custom", "https://example.com/custom").get
|
||||||
|
val currentEdition = Editions.Resolved.Edition(
|
||||||
|
parent = Some(parentEdition),
|
||||||
|
engineVersion = None,
|
||||||
|
repositories = Map("custom" -> customRepo),
|
||||||
|
libraries = Map(
|
||||||
|
"Foo.Main" -> Editions.Resolved
|
||||||
|
.PublishedLibrary("Foo.Main", SemVer(1, 0, 0), mainRepo),
|
||||||
|
"Foo.My" -> Editions.Resolved
|
||||||
|
.PublishedLibrary("Foo.My", SemVer(2, 0, 0), customRepo),
|
||||||
|
"Foo.Local" -> Editions.Resolved.LocalLibrary("Foo.Local")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
case class FakeLocalLibraryProvider(fixtures: Map[String, Path])
|
||||||
|
extends LocalLibraryProvider {
|
||||||
|
override def findLibrary(libraryName: LibraryName): Option[Path] =
|
||||||
|
fixtures.get(libraryName.qualifiedName)
|
||||||
|
}
|
||||||
|
|
||||||
|
val localLibraries = Map(
|
||||||
|
"Foo.My" -> Path.of("./Foo/My"),
|
||||||
|
"Foo.Local" -> Path.of("./Foo/Local"),
|
||||||
|
"Standard.Base" -> Path.of("./Standard/Base")
|
||||||
|
)
|
||||||
|
|
||||||
|
val resolver = LibraryResolver(FakeLocalLibraryProvider(localLibraries))
|
||||||
|
|
||||||
|
"correctly resolve libraries based on the edition" in {
|
||||||
|
resolver
|
||||||
|
.resolveLibraryVersion(
|
||||||
|
libraryName = LibraryName("Standard", "Base"),
|
||||||
|
edition = currentEdition,
|
||||||
|
preferLocalLibraries = false
|
||||||
|
)
|
||||||
|
.rightValue shouldEqual
|
||||||
|
LibraryVersion.Published(SemVer(4, 5, 6), mainRepo)
|
||||||
|
|
||||||
|
resolver
|
||||||
|
.resolveLibraryVersion(
|
||||||
|
libraryName = LibraryName("Foo", "Main"),
|
||||||
|
edition = currentEdition,
|
||||||
|
preferLocalLibraries = false
|
||||||
|
)
|
||||||
|
.rightValue shouldEqual
|
||||||
|
LibraryVersion.Published(SemVer(1, 0, 0), mainRepo)
|
||||||
|
|
||||||
|
resolver
|
||||||
|
.resolveLibraryVersion(
|
||||||
|
libraryName = LibraryName("Foo", "My"),
|
||||||
|
edition = currentEdition,
|
||||||
|
preferLocalLibraries = false
|
||||||
|
)
|
||||||
|
.rightValue shouldEqual
|
||||||
|
LibraryVersion.Published(SemVer(2, 0, 0), customRepo)
|
||||||
|
|
||||||
|
resolver
|
||||||
|
.resolveLibraryVersion(
|
||||||
|
libraryName = LibraryName("Foo", "Local"),
|
||||||
|
edition = currentEdition,
|
||||||
|
preferLocalLibraries = false
|
||||||
|
)
|
||||||
|
.rightValue shouldEqual
|
||||||
|
LibraryVersion.Local
|
||||||
|
}
|
||||||
|
|
||||||
|
"correctly handle preference of local libraries" in {
|
||||||
|
resolver
|
||||||
|
.resolveLibraryVersion(
|
||||||
|
libraryName = LibraryName("Standard", "Base"),
|
||||||
|
edition = currentEdition,
|
||||||
|
preferLocalLibraries = true
|
||||||
|
)
|
||||||
|
.rightValue shouldEqual
|
||||||
|
LibraryVersion.Local
|
||||||
|
|
||||||
|
resolver
|
||||||
|
.resolveLibraryVersion(
|
||||||
|
libraryName = LibraryName("Foo", "Main"),
|
||||||
|
edition = currentEdition,
|
||||||
|
preferLocalLibraries = true
|
||||||
|
)
|
||||||
|
.rightValue shouldEqual
|
||||||
|
LibraryVersion.Published(SemVer(1, 0, 0), mainRepo)
|
||||||
|
|
||||||
|
resolver
|
||||||
|
.resolveLibraryVersion(
|
||||||
|
libraryName = LibraryName("Foo", "My"),
|
||||||
|
edition = currentEdition,
|
||||||
|
preferLocalLibraries = true
|
||||||
|
)
|
||||||
|
.rightValue shouldEqual
|
||||||
|
LibraryVersion.Local
|
||||||
|
|
||||||
|
resolver
|
||||||
|
.resolveLibraryVersion(
|
||||||
|
libraryName = LibraryName("Foo", "Local"),
|
||||||
|
edition = currentEdition,
|
||||||
|
preferLocalLibraries = true
|
||||||
|
)
|
||||||
|
.rightValue shouldEqual
|
||||||
|
LibraryVersion.Local
|
||||||
|
}
|
||||||
|
|
||||||
|
"not fall back to a local library if it was not defined as such " +
|
||||||
|
"explicitly nor `prefer-local-libraries` is set" in {
|
||||||
|
val result = resolver.resolveLibraryVersion(
|
||||||
|
libraryName = LibraryName("Foo", "Local"),
|
||||||
|
edition = parentEdition,
|
||||||
|
preferLocalLibraries = false
|
||||||
|
)
|
||||||
|
inside(result) { case Left(error) =>
|
||||||
|
error.getMessage should include("not defined")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +1,13 @@
|
|||||||
package org.enso.pkg
|
package org.enso.pkg
|
||||||
|
|
||||||
import io.circe.syntax._
|
import io.circe.syntax._
|
||||||
import io.circe.generic.auto._
|
import io.circe.{yaml, Decoder, DecodingFailure, Encoder, Json, JsonObject}
|
||||||
import io.circe.{yaml, Decoder, DecodingFailure, Encoder, Json}
|
|
||||||
import io.circe.yaml.Printer
|
import io.circe.yaml.Printer
|
||||||
|
import org.enso.editions.{DefaultEnsoVersion, Editions, EnsoVersion}
|
||||||
|
import org.enso.editions.EditionSerialization._
|
||||||
|
|
||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
|
|
||||||
/** An extra project dependency.
|
|
||||||
* @param name name of the package
|
|
||||||
* @param version package version
|
|
||||||
*/
|
|
||||||
case class Dependency(name: String, version: String)
|
|
||||||
|
|
||||||
/** Contact information to a user.
|
/** Contact information to a user.
|
||||||
*
|
*
|
||||||
* Used for defining authors and maintainers.
|
* Used for defining authors and maintainers.
|
||||||
@ -26,8 +21,7 @@ case class Contact(name: Option[String], email: Option[String]) {
|
|||||||
"At least one of fields `name` or `email` must be defined."
|
"At least one of fields `name` or `email` must be defined."
|
||||||
)
|
)
|
||||||
|
|
||||||
/** @inheritdoc
|
/** @inheritdoc */
|
||||||
*/
|
|
||||||
override def toString: String = {
|
override def toString: String = {
|
||||||
val space = if (name.isDefined && email.isDefined) " " else ""
|
val space = if (name.isDefined && email.isDefined) " " else ""
|
||||||
name.getOrElse("") + space + email.map(email => s"<$email>").getOrElse("")
|
name.getOrElse("") + space + email.map(email => s"<$email>").getOrElse("")
|
||||||
@ -35,15 +29,13 @@ case class Contact(name: Option[String], email: Option[String]) {
|
|||||||
}
|
}
|
||||||
object Contact {
|
object Contact {
|
||||||
|
|
||||||
/** Fields for use when serializing the [[Contact]].
|
/** Fields for use when serializing the [[Contact]]. */
|
||||||
*/
|
|
||||||
object Fields {
|
object Fields {
|
||||||
val Name = "name"
|
val Name = "name"
|
||||||
val Email = "email"
|
val Email = "email"
|
||||||
}
|
}
|
||||||
|
|
||||||
/** [[Encoder]] instance for the [[Contact]].
|
/** [[Encoder]] instance for the [[Contact]]. */
|
||||||
*/
|
|
||||||
implicit val encoder: Encoder[Contact] = { contact =>
|
implicit val encoder: Encoder[Contact] = { contact =>
|
||||||
val name = contact.name.map(Fields.Name -> _.asJson)
|
val name = contact.name.map(Fields.Name -> _.asJson)
|
||||||
val email = contact.email.map(Fields.Email -> _.asJson)
|
val email = contact.email.map(Fields.Email -> _.asJson)
|
||||||
@ -78,14 +70,15 @@ object Contact {
|
|||||||
*
|
*
|
||||||
* @param name package name
|
* @param name package name
|
||||||
* @param version package version
|
* @param version package version
|
||||||
* @param ensoVersion version of the Enso engine associated with the package,
|
|
||||||
* can be set to `default` which defaults to the locally
|
|
||||||
* installed version
|
|
||||||
* @param license package license
|
* @param license package license
|
||||||
* @param authors name and contact information of the package author(s)
|
* @param authors name and contact information of the package author(s)
|
||||||
* @param maintainers name and contact information of current package
|
* @param maintainers name and contact information of current package
|
||||||
* maintainer(s)
|
* maintainer(s)
|
||||||
* @param dependencies a list of package dependencies
|
* @param edition the Edition associated with the project; it implies the
|
||||||
|
* engine version and dependency configuration to be used
|
||||||
|
* @param preferLocalLibraries specifies if library resolution should prefer
|
||||||
|
* local libraries over what is defined in the
|
||||||
|
* edition
|
||||||
* @param originalJson a Json object holding the original values that this
|
* @param originalJson a Json object holding the original values that this
|
||||||
* Config was created from, used to preserve configuration
|
* Config was created from, used to preserve configuration
|
||||||
* keys that are not known
|
* keys that are not known
|
||||||
@ -93,16 +86,15 @@ object Contact {
|
|||||||
case class Config(
|
case class Config(
|
||||||
name: String,
|
name: String,
|
||||||
version: String,
|
version: String,
|
||||||
ensoVersion: EnsoVersion,
|
|
||||||
license: String,
|
license: String,
|
||||||
authors: List[Contact],
|
authors: List[Contact],
|
||||||
maintainers: List[Contact],
|
maintainers: List[Contact],
|
||||||
dependencies: List[Dependency],
|
edition: Editions.RawEdition,
|
||||||
originalJson: Json = Json.obj()
|
preferLocalLibraries: Boolean,
|
||||||
|
originalJson: JsonObject = JsonObject()
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/** Converts the configuration into a YAML representation.
|
/** Converts the configuration into a YAML representation. */
|
||||||
*/
|
|
||||||
def toYaml: String =
|
def toYaml: String =
|
||||||
Printer.spaces2.copy(preserveOrder = true).pretty(Config.encoder(this))
|
Printer.spaces2.copy(preserveOrder = true).pretty(Config.encoder(this))
|
||||||
}
|
}
|
||||||
@ -115,56 +107,107 @@ object Config {
|
|||||||
val license: String = "license"
|
val license: String = "license"
|
||||||
val author: String = "authors"
|
val author: String = "authors"
|
||||||
val maintainer: String = "maintainers"
|
val maintainer: String = "maintainers"
|
||||||
val dependencies: String = "dependencies"
|
val edition: String = "edition"
|
||||||
|
val preferLocalLibraries = "prefer-local-libraries"
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit val decoder: Decoder[Config] = { json =>
|
implicit val decoder: Decoder[Config] = { json =>
|
||||||
for {
|
for {
|
||||||
name <- json.get[String](JsonFields.name)
|
name <- json.get[String](JsonFields.name)
|
||||||
version <- json.getOrElse[String](JsonFields.version)("dev")
|
version <- json.getOrElse[String](JsonFields.version)("dev")
|
||||||
ensoVersion <-
|
ensoVersion <- json.get[Option[EnsoVersion]](JsonFields.ensoVersion)
|
||||||
json.getOrElse[EnsoVersion](JsonFields.ensoVersion)(DefaultEnsoVersion)
|
edition <- json.get[Option[Editions.RawEdition]](JsonFields.edition)
|
||||||
license <- json.getOrElse(JsonFields.license)("")
|
license <- json.getOrElse(JsonFields.license)("")
|
||||||
author <- json.getOrElse[List[Contact]](JsonFields.author)(List())
|
author <- json.getOrElse[List[Contact]](JsonFields.author)(List())
|
||||||
maintainer <- json.getOrElse[List[Contact]](JsonFields.maintainer)(List())
|
maintainer <- json.getOrElse[List[Contact]](JsonFields.maintainer)(List())
|
||||||
dependencies <- json.getOrElse[List[Dependency]](JsonFields.dependencies)(
|
preferLocal <-
|
||||||
List()
|
json.getOrElse[Boolean](JsonFields.preferLocalLibraries)(false)
|
||||||
)
|
finalEdition <-
|
||||||
|
editionOrVersionBackwardsCompatibility(edition, ensoVersion).left.map {
|
||||||
|
error => DecodingFailure(error, json.history)
|
||||||
|
}
|
||||||
|
originals <- json.as[JsonObject]
|
||||||
} yield Config(
|
} yield Config(
|
||||||
name,
|
name = name,
|
||||||
version,
|
version = version,
|
||||||
ensoVersion,
|
license = license,
|
||||||
license,
|
authors = author,
|
||||||
author,
|
maintainers = maintainer,
|
||||||
maintainer,
|
edition = finalEdition,
|
||||||
dependencies,
|
preferLocalLibraries = preferLocal,
|
||||||
json.value
|
originalJson = originals
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit val encoder: Encoder[Config] = { config =>
|
implicit val encoder: Encoder[Config] = { config =>
|
||||||
val originals = config.originalJson
|
val originals = config.originalJson
|
||||||
val overrides = Json.obj(
|
val overrides = Seq(
|
||||||
JsonFields.name -> config.name.asJson,
|
JsonFields.name -> config.name.asJson,
|
||||||
JsonFields.version -> config.version.asJson,
|
JsonFields.version -> config.version.asJson,
|
||||||
JsonFields.ensoVersion -> config.ensoVersion.asJson,
|
JsonFields.edition -> config.edition.asJson,
|
||||||
JsonFields.license -> config.license.asJson,
|
JsonFields.license -> config.license.asJson,
|
||||||
JsonFields.author -> config.authors.asJson,
|
JsonFields.author -> config.authors.asJson,
|
||||||
JsonFields.maintainer -> config.maintainers.asJson
|
JsonFields.maintainer -> config.maintainers.asJson
|
||||||
)
|
)
|
||||||
val base = originals.deepMerge(overrides)
|
val preferLocalOverride =
|
||||||
val withDeps =
|
if (config.preferLocalLibraries)
|
||||||
if (config.dependencies.nonEmpty)
|
Seq(JsonFields.preferLocalLibraries -> true.asJson)
|
||||||
base.deepMerge(
|
else Seq()
|
||||||
Json.obj(JsonFields.dependencies -> config.dependencies.asJson)
|
val overridesObject = JsonObject(
|
||||||
)
|
overrides ++ preferLocalOverride: _*
|
||||||
else base
|
)
|
||||||
withDeps
|
originals.remove(JsonFields.ensoVersion).deepMerge(overridesObject).asJson
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Tries to parse the [[Config]] from a YAML string.
|
/** Tries to parse the [[Config]] from a YAML string. */
|
||||||
*/
|
|
||||||
def fromYaml(yamlString: String): Try[Config] = {
|
def fromYaml(yamlString: String): Try[Config] = {
|
||||||
yaml.parser.parse(yamlString).flatMap(_.as[Config]).toTry
|
yaml.parser.parse(yamlString).flatMap(_.as[Config]).toTry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Creates a simple edition that just defines the provided engine version.
|
||||||
|
*
|
||||||
|
* A compatibility layer for migrating from just specifying the engine
|
||||||
|
* version to the edition system.
|
||||||
|
*
|
||||||
|
* TODO [RW] once the edition is actually used for loading libraries, this
|
||||||
|
* may need to be revisited, because an edition created in this way will not
|
||||||
|
* have any libraries present which is highly undesirable. We may either
|
||||||
|
* remove the compatibility layer and return errors for the old format or
|
||||||
|
* need to use the latest/default edition or some hardcoded edition for
|
||||||
|
* compatibility.
|
||||||
|
*/
|
||||||
|
def makeCompatibilityEditionFromVersion(
|
||||||
|
ensoVersion: EnsoVersion
|
||||||
|
): Editions.RawEdition = Editions.Raw.Edition(
|
||||||
|
parent = None,
|
||||||
|
engineVersion = Some(ensoVersion),
|
||||||
|
repositories = Map(),
|
||||||
|
libraries = Map()
|
||||||
|
)
|
||||||
|
|
||||||
|
/** A helper method that reconciles the old and new fields of the config
|
||||||
|
* related to the edition.
|
||||||
|
*
|
||||||
|
* If an edition is present, it is just returned as-is. If the engine version
|
||||||
|
* is specified, a special edition is created that specifies this particular
|
||||||
|
* engine version and nothing else.
|
||||||
|
*
|
||||||
|
* If both fields are defined, an error is raised as the configuration may be
|
||||||
|
* inconsistent - the `engine-version` field should only be present in old
|
||||||
|
* configs and after migration to the edition format it should be removed.
|
||||||
|
*/
|
||||||
|
private def editionOrVersionBackwardsCompatibility(
|
||||||
|
edition: Option[Editions.RawEdition],
|
||||||
|
ensoVersion: Option[EnsoVersion]
|
||||||
|
): Either[String, Editions.RawEdition] = (edition, ensoVersion) match {
|
||||||
|
case (Some(_), Some(_)) =>
|
||||||
|
Left(
|
||||||
|
s"The deprecated `${JsonFields.ensoVersion}` should not be defined " +
|
||||||
|
s"if the `${JsonFields.edition}` that replaces it is already defined."
|
||||||
|
)
|
||||||
|
case (Some(edition), _) => Right(edition)
|
||||||
|
case _ =>
|
||||||
|
val version = ensoVersion.getOrElse(DefaultEnsoVersion)
|
||||||
|
Right(makeCompatibilityEditionFromVersion(version))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
package org.enso.pkg
|
package org.enso.pkg
|
||||||
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
import cats.Show
|
import cats.Show
|
||||||
|
import org.enso.editions.{DefaultEnsoVersion, EnsoVersion}
|
||||||
import scala.jdk.CollectionConverters._
|
|
||||||
import org.enso.filesystem.FileSystem
|
import org.enso.filesystem.FileSystem
|
||||||
import org.enso.pkg.validation.NameValidation
|
import org.enso.pkg.validation.NameValidation
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
import scala.io.Source
|
import scala.io.Source
|
||||||
|
import scala.jdk.CollectionConverters._
|
||||||
import scala.util.{Failure, Try, Using}
|
import scala.util.{Failure, Try, Using}
|
||||||
|
|
||||||
object CouldNotCreateDirectory extends Exception
|
object CouldNotCreateDirectory extends Exception
|
||||||
@ -202,14 +201,15 @@ class PackageManager[F](implicit val fileSystem: FileSystem[F]) {
|
|||||||
authors: List[Contact] = List(),
|
authors: List[Contact] = List(),
|
||||||
maintainers: List[Contact] = List()
|
maintainers: List[Contact] = List()
|
||||||
): Package[F] = {
|
): Package[F] = {
|
||||||
|
val edition = Config.makeCompatibilityEditionFromVersion(ensoVersion)
|
||||||
val config = Config(
|
val config = Config(
|
||||||
name = NameValidation.normalizeName(name),
|
name = NameValidation.normalizeName(name),
|
||||||
version = version,
|
version = version,
|
||||||
ensoVersion = ensoVersion,
|
license = "",
|
||||||
license = "",
|
authors = authors,
|
||||||
authors = authors,
|
edition = edition,
|
||||||
maintainers = maintainers,
|
preferLocalLibraries = true,
|
||||||
dependencies = List()
|
maintainers = maintainers
|
||||||
)
|
)
|
||||||
create(root, config)
|
create(root, config)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package org.enso.pkg
|
package org.enso.pkg
|
||||||
|
|
||||||
import io.circe.Json
|
import io.circe.{Json, JsonObject}
|
||||||
|
import nl.gn0s1s.bump.SemVer
|
||||||
|
import org.enso.editions.{DefaultEnsoVersion, SemVerEnsoVersion}
|
||||||
import org.scalatest.{Inside, OptionValues}
|
import org.scalatest.{Inside, OptionValues}
|
||||||
import org.scalatest.matchers.should.Matchers
|
import org.scalatest.matchers.should.Matchers
|
||||||
import org.scalatest.wordspec.AnyWordSpec
|
import org.scalatest.wordspec.AnyWordSpec
|
||||||
@ -29,21 +31,52 @@ class ConfigSpec
|
|||||||
|
|
||||||
"deserialize the serialized representation to the original value" in {
|
"deserialize the serialized representation to the original value" in {
|
||||||
val config = Config(
|
val config = Config(
|
||||||
name = "placeholder",
|
name = "placeholder",
|
||||||
version = "dev",
|
version = "dev",
|
||||||
ensoVersion = DefaultEnsoVersion,
|
edition = Config.makeCompatibilityEditionFromVersion(
|
||||||
license = "none",
|
SemVerEnsoVersion(SemVer(4, 5, 6))
|
||||||
authors = List(),
|
),
|
||||||
|
license = "none",
|
||||||
|
authors = List(),
|
||||||
maintainers = List(
|
maintainers = List(
|
||||||
Contact(Some("A"), Some("a@example.com")),
|
Contact(Some("A"), Some("a@example.com")),
|
||||||
Contact(Some("B"), None),
|
Contact(Some("B"), None),
|
||||||
Contact(None, Some("c@example.com"))
|
Contact(None, Some("c@example.com"))
|
||||||
),
|
),
|
||||||
dependencies = List(Dependency("dependency", "0.0.0"))
|
preferLocalLibraries = true
|
||||||
)
|
)
|
||||||
val deserialized = Config.fromYaml(config.toYaml).get
|
val deserialized = Config.fromYaml(config.toYaml).get
|
||||||
val withoutJson = deserialized.copy(originalJson = Json.obj())
|
val withoutJson = deserialized.copy(originalJson = JsonObject())
|
||||||
withoutJson shouldEqual config
|
withoutJson shouldEqual config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"only require the name and use defaults for everything else" in {
|
||||||
|
val parsed = Config.fromYaml("name: FooBar").get
|
||||||
|
parsed.name shouldEqual "FooBar"
|
||||||
|
parsed.edition.engineVersion should contain(DefaultEnsoVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
"be backwards compatible but correctly migrate to new format on save" in {
|
||||||
|
val oldFormat =
|
||||||
|
"""name: FooBar
|
||||||
|
|enso-version: 1.2.3
|
||||||
|
|extra-key: extra-value
|
||||||
|
|""".stripMargin
|
||||||
|
val parsed = Config.fromYaml(oldFormat).get
|
||||||
|
|
||||||
|
parsed.edition.engineVersion should contain(
|
||||||
|
SemVerEnsoVersion(SemVer(1, 2, 3))
|
||||||
|
)
|
||||||
|
|
||||||
|
val serialized = parsed.toYaml
|
||||||
|
val parsedAgain = Config.fromYaml(serialized).get
|
||||||
|
|
||||||
|
parsedAgain.copy(originalJson = JsonObject()) shouldEqual
|
||||||
|
parsed.copy(originalJson = JsonObject())
|
||||||
|
|
||||||
|
parsedAgain.originalJson("extra-key").flatMap(_.asString) should contain(
|
||||||
|
"extra-value"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,13 @@ package org.enso.projectmanager.infrastructure.languageserver
|
|||||||
import java.io.PrintWriter
|
import java.io.PrintWriter
|
||||||
import java.lang.ProcessBuilder.Redirect
|
import java.lang.ProcessBuilder.Redirect
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
import akka.actor.ActorRef
|
import akka.actor.ActorRef
|
||||||
import com.typesafe.scalalogging.Logger
|
import com.typesafe.scalalogging.Logger
|
||||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory
|
import org.apache.commons.lang3.concurrent.BasicThreadFactory
|
||||||
import org.enso.logger.masking.Masking
|
import org.enso.logger.masking.Masking
|
||||||
import org.enso.loggingservice.LoggingServiceManager
|
import org.enso.loggingservice.LoggingServiceManager
|
||||||
import org.enso.projectmanager.service.versionmanagement.RuntimeVersionManagerFactory
|
import org.enso.projectmanager.service.versionmanagement.RuntimeVersionManagerFactory
|
||||||
|
import org.enso.runtimeversionmanager.config.GlobalConfigurationManager
|
||||||
import org.enso.runtimeversionmanager.runner.{LanguageServerOptions, Runner}
|
import org.enso.runtimeversionmanager.runner.{LanguageServerOptions, Runner}
|
||||||
|
|
||||||
import scala.util.Using
|
import scala.util.Using
|
||||||
@ -80,11 +80,16 @@ object ExecutorWithUnlimitedPool extends LanguageServerExecutor {
|
|||||||
rpcPort = rpcPort,
|
rpcPort = rpcPort,
|
||||||
dataPort = dataPort
|
dataPort = dataPort
|
||||||
)
|
)
|
||||||
|
val configurationManager = new GlobalConfigurationManager(
|
||||||
val runner = new Runner(
|
|
||||||
versionManager,
|
versionManager,
|
||||||
distributionConfiguration.environment,
|
distributionConfiguration.distributionManager
|
||||||
descriptor.deferredLoggingServiceEndpoint
|
)
|
||||||
|
val runner = new Runner(
|
||||||
|
runtimeVersionManager = versionManager,
|
||||||
|
globalConfigurationManager = configurationManager,
|
||||||
|
editionManager = distributionConfiguration.editionManager,
|
||||||
|
environment = distributionConfiguration.environment,
|
||||||
|
loggerConnection = descriptor.deferredLoggingServiceEndpoint
|
||||||
)
|
)
|
||||||
val runSettings = runner
|
val runSettings = runner
|
||||||
.startLanguageServer(
|
.startLanguageServer(
|
||||||
|
@ -108,7 +108,7 @@ class ProjectFileRepository[
|
|||||||
name = pkg.name,
|
name = pkg.name,
|
||||||
kind = meta.kind,
|
kind = meta.kind,
|
||||||
created = meta.created,
|
created = meta.created,
|
||||||
engineVersion = pkg.config.ensoVersion,
|
edition = pkg.config.edition,
|
||||||
lastOpened = meta.lastOpened,
|
lastOpened = meta.lastOpened,
|
||||||
path = Some(directory.toString),
|
path = Some(directory.toString),
|
||||||
directoryCreationTime = directoryCreationTime
|
directoryCreationTime = directoryCreationTime
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
package org.enso.projectmanager.model
|
package org.enso.projectmanager.model
|
||||||
|
|
||||||
|
import org.enso.editions.Editions
|
||||||
|
|
||||||
import java.nio.file.attribute.FileTime
|
import java.nio.file.attribute.FileTime
|
||||||
import java.time.OffsetDateTime
|
import java.time.OffsetDateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
import org.enso.pkg.{DefaultEnsoVersion, EnsoVersion}
|
|
||||||
|
|
||||||
/** Project entity.
|
/** Project entity.
|
||||||
*
|
*
|
||||||
* @param id a project id
|
* @param id a project id
|
||||||
* @param name a project name
|
* @param name a project name
|
||||||
* @param kind a project kind
|
* @param kind a project kind
|
||||||
* @param created a project creation time
|
* @param created a project creation time
|
||||||
* @param engineVersion version of the engine associated with the project
|
* @param edition the edition configuration associated with the project
|
||||||
* @param lastOpened a project last open time
|
* @param lastOpened a project last open time
|
||||||
* @param path a path to the project structure
|
* @param path a path to the project structure
|
||||||
*/
|
*/
|
||||||
@ -21,7 +21,7 @@ case class Project(
|
|||||||
name: String,
|
name: String,
|
||||||
kind: ProjectKind,
|
kind: ProjectKind,
|
||||||
created: OffsetDateTime,
|
created: OffsetDateTime,
|
||||||
engineVersion: EnsoVersion = DefaultEnsoVersion,
|
edition: Editions.RawEdition,
|
||||||
lastOpened: Option[OffsetDateTime] = None,
|
lastOpened: Option[OffsetDateTime] = None,
|
||||||
path: Option[String] = None,
|
path: Option[String] = None,
|
||||||
directoryCreationTime: Option[FileTime] = None
|
directoryCreationTime: Option[FileTime] = None
|
||||||
|
@ -8,7 +8,7 @@ import org.enso.projectmanager.protocol.ProjectManagementApi._
|
|||||||
*
|
*
|
||||||
* Do not remove this import.
|
* Do not remove this import.
|
||||||
*/
|
*/
|
||||||
import org.enso.pkg.SemVerJson._
|
import org.enso.editions.SemVerJson._
|
||||||
|
|
||||||
object JsonRpc {
|
object JsonRpc {
|
||||||
|
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
package org.enso.projectmanager.protocol
|
package org.enso.projectmanager.protocol
|
||||||
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
import io.circe.Json
|
import io.circe.Json
|
||||||
import io.circe.syntax._
|
import io.circe.syntax._
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
|
import org.enso.editions.EnsoVersion
|
||||||
import org.enso.jsonrpc.{Error, HasParams, HasResult, Method, Unused}
|
import org.enso.jsonrpc.{Error, HasParams, HasResult, Method, Unused}
|
||||||
import org.enso.pkg.EnsoVersion
|
|
||||||
import org.enso.projectmanager.data.{
|
import org.enso.projectmanager.data.{
|
||||||
EngineVersion,
|
EngineVersion,
|
||||||
MissingComponentAction,
|
MissingComponentAction,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package org.enso.projectmanager.requesthandler
|
package org.enso.projectmanager.requesthandler
|
||||||
|
|
||||||
import akka.actor._
|
import akka.actor._
|
||||||
import org.enso.pkg.DefaultEnsoVersion
|
import org.enso.editions.DefaultEnsoVersion
|
||||||
import org.enso.projectmanager.control.core.CovariantFlatMap
|
import org.enso.projectmanager.control.core.CovariantFlatMap
|
||||||
import org.enso.projectmanager.control.core.syntax._
|
import org.enso.projectmanager.control.core.syntax._
|
||||||
import org.enso.projectmanager.control.effect.syntax._
|
import org.enso.projectmanager.control.effect.syntax._
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package org.enso.projectmanager.service
|
package org.enso.projectmanager.service
|
||||||
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
import akka.actor.ActorRef
|
import akka.actor.ActorRef
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
import org.enso.projectmanager.control.core.CovariantFlatMap
|
import org.enso.projectmanager.control.core.CovariantFlatMap
|
||||||
@ -12,8 +10,11 @@ import org.enso.projectmanager.service.ProjectServiceFailure.ProjectCreateFailed
|
|||||||
import org.enso.projectmanager.service.versionmanagement.RuntimeVersionManagerErrorRecoverySyntax._
|
import org.enso.projectmanager.service.versionmanagement.RuntimeVersionManagerErrorRecoverySyntax._
|
||||||
import org.enso.projectmanager.service.versionmanagement.RuntimeVersionManagerFactory
|
import org.enso.projectmanager.service.versionmanagement.RuntimeVersionManagerFactory
|
||||||
import org.enso.projectmanager.versionmanagement.DistributionConfiguration
|
import org.enso.projectmanager.versionmanagement.DistributionConfiguration
|
||||||
|
import org.enso.runtimeversionmanager.config.GlobalConfigurationManager
|
||||||
import org.enso.runtimeversionmanager.runner.Runner
|
import org.enso.runtimeversionmanager.runner.Runner
|
||||||
|
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
/** A service for creating new project structures using the runner of the
|
/** A service for creating new project structures using the runner of the
|
||||||
* specific engine version selected for the project.
|
* specific engine version selected for the project.
|
||||||
*/
|
*/
|
||||||
@ -36,11 +37,17 @@ class ProjectCreationService[
|
|||||||
val versionManager = RuntimeVersionManagerFactory(
|
val versionManager = RuntimeVersionManagerFactory(
|
||||||
distributionConfiguration
|
distributionConfiguration
|
||||||
).makeRuntimeVersionManager(progressTracker, missingComponentAction)
|
).makeRuntimeVersionManager(progressTracker, missingComponentAction)
|
||||||
|
val configurationManager = new GlobalConfigurationManager(
|
||||||
|
versionManager,
|
||||||
|
distributionConfiguration.distributionManager
|
||||||
|
)
|
||||||
val runner =
|
val runner =
|
||||||
new Runner(
|
new Runner(
|
||||||
versionManager,
|
runtimeVersionManager = versionManager,
|
||||||
distributionConfiguration.environment,
|
globalConfigurationManager = configurationManager,
|
||||||
loggingServiceDescriptor.getEndpoint
|
editionManager = distributionConfiguration.editionManager,
|
||||||
|
environment = distributionConfiguration.environment,
|
||||||
|
loggerConnection = loggingServiceDescriptor.getEndpoint
|
||||||
)
|
)
|
||||||
|
|
||||||
val settings =
|
val settings =
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
package org.enso.projectmanager.service
|
package org.enso.projectmanager.service
|
||||||
|
|
||||||
import java.util.UUID
|
|
||||||
|
|
||||||
import akka.actor.ActorRef
|
import akka.actor.ActorRef
|
||||||
import cats.MonadError
|
import cats.MonadError
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
|
import org.enso.editions.{DefaultEnsoVersion, EnsoVersion}
|
||||||
|
import org.enso.pkg.Config
|
||||||
|
import org.enso.projectmanager.control.core.syntax._
|
||||||
import org.enso.projectmanager.control.core.{
|
import org.enso.projectmanager.control.core.{
|
||||||
Applicative,
|
Applicative,
|
||||||
CovariantFlatMap,
|
CovariantFlatMap,
|
||||||
Traverse
|
Traverse
|
||||||
}
|
}
|
||||||
import org.enso.projectmanager.control.core.syntax._
|
|
||||||
import org.enso.projectmanager.control.effect.syntax._
|
import org.enso.projectmanager.control.effect.syntax._
|
||||||
import org.enso.projectmanager.control.effect.{ErrorChannel, Sync}
|
import org.enso.projectmanager.control.effect.{ErrorChannel, Sync}
|
||||||
import org.enso.projectmanager.data.{
|
import org.enso.projectmanager.data.{
|
||||||
@ -48,6 +48,8 @@ import org.enso.projectmanager.service.versionmanagement.RuntimeVersionManagerEr
|
|||||||
import org.enso.projectmanager.service.versionmanagement.RuntimeVersionManagerFactory
|
import org.enso.projectmanager.service.versionmanagement.RuntimeVersionManagerFactory
|
||||||
import org.enso.projectmanager.versionmanagement.DistributionConfiguration
|
import org.enso.projectmanager.versionmanagement.DistributionConfiguration
|
||||||
|
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
/** Implementation of business logic for project management.
|
/** Implementation of business logic for project management.
|
||||||
*
|
*
|
||||||
* @param validator a project validator
|
* @param validator a project validator
|
||||||
@ -87,7 +89,16 @@ class ProjectService[
|
|||||||
_ <- validateName(name)
|
_ <- validateName(name)
|
||||||
_ <- checkIfNameExists(name)
|
_ <- checkIfNameExists(name)
|
||||||
creationTime <- clock.nowInUtc()
|
creationTime <- clock.nowInUtc()
|
||||||
project = Project(projectId, name, UserProject, creationTime)
|
defaultEdition = Config.makeCompatibilityEditionFromVersion(
|
||||||
|
DefaultEnsoVersion
|
||||||
|
)
|
||||||
|
project = Project(
|
||||||
|
id = projectId,
|
||||||
|
name = name,
|
||||||
|
kind = UserProject,
|
||||||
|
created = creationTime,
|
||||||
|
edition = defaultEdition
|
||||||
|
)
|
||||||
path <- repo.findPathForNewProject(project).mapError(toServiceFailure)
|
path <- repo.findPathForNewProject(project).mapError(toServiceFailure)
|
||||||
_ <- log.debug(
|
_ <- log.debug(
|
||||||
"Found a path [{}] for a new project [{}, {}].",
|
"Found a path [{}] for a new project [{}, {}].",
|
||||||
@ -275,7 +286,7 @@ class ProjectService[
|
|||||||
}
|
}
|
||||||
.mapRuntimeManagerErrors(th =>
|
.mapRuntimeManagerErrors(th =>
|
||||||
ProjectOpenFailed(
|
ProjectOpenFailed(
|
||||||
s"Cannot install the required engine. ${th.getMessage}"
|
s"Cannot install the required engine. $th"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -285,8 +296,9 @@ class ProjectService[
|
|||||||
project: Project,
|
project: Project,
|
||||||
missingComponentAction: MissingComponentAction
|
missingComponentAction: MissingComponentAction
|
||||||
): F[ProjectServiceFailure, RunningLanguageServerInfo] = for {
|
): F[ProjectServiceFailure, RunningLanguageServerInfo] = for {
|
||||||
|
version <- resolveProjectVersion(project)
|
||||||
version <- configurationService
|
version <- configurationService
|
||||||
.resolveEnsoVersion(project.engineVersion)
|
.resolveEnsoVersion(version)
|
||||||
.mapError { case ConfigurationFileAccessFailure(message) =>
|
.mapError { case ConfigurationFileAccessFailure(message) =>
|
||||||
ProjectOpenFailed(
|
ProjectOpenFailed(
|
||||||
"Could not deduce the default version to use for the project: " +
|
"Could not deduce the default version to use for the project: " +
|
||||||
@ -345,15 +357,17 @@ class ProjectService[
|
|||||||
private def resolveProjectMetadata(
|
private def resolveProjectMetadata(
|
||||||
project: Project
|
project: Project
|
||||||
): F[ProjectServiceFailure, ProjectMetadata] =
|
): F[ProjectServiceFailure, ProjectMetadata] =
|
||||||
configurationService
|
for {
|
||||||
.resolveEnsoVersion(project.engineVersion)
|
version <- resolveProjectVersion(project)
|
||||||
.mapError { case ConfigurationFileAccessFailure(message) =>
|
version <- configurationService
|
||||||
GlobalConfigurationAccessFailure(
|
.resolveEnsoVersion(version)
|
||||||
"Could not deduce the default version to use for the project: " +
|
.mapError { case ConfigurationFileAccessFailure(message) =>
|
||||||
message
|
GlobalConfigurationAccessFailure(
|
||||||
)
|
"Could not deduce the default version to use for the project: " +
|
||||||
}
|
message
|
||||||
.map(toProjectMetadata(_, project))
|
)
|
||||||
|
}
|
||||||
|
} yield toProjectMetadata(version, project)
|
||||||
|
|
||||||
private def toProjectMetadata(
|
private def toProjectMetadata(
|
||||||
engineVersion: SemVer,
|
engineVersion: SemVer,
|
||||||
@ -439,4 +453,19 @@ class ProjectService[
|
|||||||
log.debug("Project name [{}] validated by [{}].", name, validator)
|
log.debug("Project name [{}] validated by [{}].", name, validator)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def resolveProjectVersion(
|
||||||
|
project: Project
|
||||||
|
): F[ProjectServiceFailure, EnsoVersion] =
|
||||||
|
Sync[F]
|
||||||
|
.blockingOp {
|
||||||
|
distributionConfiguration.editionManager
|
||||||
|
.resolveEngineVersion(project.edition)
|
||||||
|
.get
|
||||||
|
}
|
||||||
|
.mapError { error =>
|
||||||
|
ProjectServiceFailure.GlobalConfigurationAccessFailure(
|
||||||
|
s"Could not resolve project engine version: ${error.getMessage}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package org.enso.projectmanager.service.config
|
|||||||
|
|
||||||
import io.circe.Json
|
import io.circe.Json
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
import org.enso.pkg.{DefaultEnsoVersion, EnsoVersion, SemVerEnsoVersion}
|
import org.enso.editions.{DefaultEnsoVersion, EnsoVersion, SemVerEnsoVersion}
|
||||||
import org.enso.projectmanager.control.core.CovariantFlatMap
|
import org.enso.projectmanager.control.core.CovariantFlatMap
|
||||||
import org.enso.projectmanager.control.effect.{ErrorChannel, Sync}
|
import org.enso.projectmanager.control.effect.{ErrorChannel, Sync}
|
||||||
import org.enso.projectmanager.service.config.GlobalConfigServiceFailure.ConfigurationFileAccessFailure
|
import org.enso.projectmanager.service.config.GlobalConfigServiceFailure.ConfigurationFileAccessFailure
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package org.enso.projectmanager.service.config
|
package org.enso.projectmanager.service.config
|
||||||
|
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
import org.enso.pkg.EnsoVersion
|
import org.enso.editions.EnsoVersion
|
||||||
|
|
||||||
/** A contract for the Global Config Service.
|
/** A contract for the Global Config Service.
|
||||||
*
|
*
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
package org.enso.projectmanager.service.versionmanagement
|
package org.enso.projectmanager.service.versionmanagement
|
||||||
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
import akka.actor.ActorRef
|
import akka.actor.ActorRef
|
||||||
import com.typesafe.scalalogging.Logger
|
import com.typesafe.scalalogging.Logger
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
import org.enso.cli.task.{ProgressListener, TaskProgress}
|
import org.enso.cli.task.{ProgressListener, TaskProgress}
|
||||||
|
import org.enso.distribution.locking.Resource
|
||||||
import org.enso.projectmanager.data.ProgressUnit
|
import org.enso.projectmanager.data.ProgressUnit
|
||||||
import org.enso.runtimeversionmanager.components.{
|
import org.enso.runtimeversionmanager.components.{
|
||||||
GraalVMVersion,
|
GraalVMVersion,
|
||||||
RuntimeVersionManagementUserInterface
|
RuntimeVersionManagementUserInterface
|
||||||
}
|
}
|
||||||
import org.enso.runtimeversionmanager.locking.Resource
|
|
||||||
|
|
||||||
import scala.util.{Failure, Success, Try}
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
||||||
|
@ -2,11 +2,11 @@ package org.enso.projectmanager.service.versionmanagement
|
|||||||
|
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
import org.enso.cli.task.TaskProgress
|
import org.enso.cli.task.TaskProgress
|
||||||
|
import org.enso.distribution.locking.Resource
|
||||||
import org.enso.runtimeversionmanager.components.{
|
import org.enso.runtimeversionmanager.components.{
|
||||||
GraalVMVersion,
|
GraalVMVersion,
|
||||||
RuntimeVersionManagementUserInterface
|
RuntimeVersionManagementUserInterface
|
||||||
}
|
}
|
||||||
import org.enso.runtimeversionmanager.locking.Resource
|
|
||||||
|
|
||||||
/** A simple [[RuntimeVersionManagementUserInterface]] that does not allow to
|
/** A simple [[RuntimeVersionManagementUserInterface]] that does not allow to
|
||||||
* install any versions and does not track any progress.
|
* install any versions and does not track any progress.
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
package org.enso.projectmanager.versionmanagement
|
package org.enso.projectmanager.versionmanagement
|
||||||
|
|
||||||
import com.typesafe.scalalogging.LazyLogging
|
import com.typesafe.scalalogging.LazyLogging
|
||||||
import org.enso.runtimeversionmanager.Environment
|
import org.enso.distribution.{DistributionManager, EditionManager, Environment}
|
||||||
|
import org.enso.distribution.locking.{
|
||||||
|
ResourceManager,
|
||||||
|
ThreadSafeFileLockManager
|
||||||
|
}
|
||||||
import org.enso.runtimeversionmanager.components.{
|
import org.enso.runtimeversionmanager.components.{
|
||||||
GraalVMComponentConfiguration,
|
GraalVMComponentConfiguration,
|
||||||
InstallerKind,
|
InstallerKind,
|
||||||
@ -10,14 +14,7 @@ import org.enso.runtimeversionmanager.components.{
|
|||||||
RuntimeVersionManagementUserInterface,
|
RuntimeVersionManagementUserInterface,
|
||||||
RuntimeVersionManager
|
RuntimeVersionManager
|
||||||
}
|
}
|
||||||
import org.enso.runtimeversionmanager.distribution.{
|
import org.enso.runtimeversionmanager.distribution.TemporaryDirectoryManager
|
||||||
DistributionManager,
|
|
||||||
TemporaryDirectoryManager
|
|
||||||
}
|
|
||||||
import org.enso.runtimeversionmanager.locking.{
|
|
||||||
ResourceManager,
|
|
||||||
ThreadSafeFileLockManager
|
|
||||||
}
|
|
||||||
import org.enso.runtimeversionmanager.releases.ReleaseProvider
|
import org.enso.runtimeversionmanager.releases.ReleaseProvider
|
||||||
import org.enso.runtimeversionmanager.releases.engine.{
|
import org.enso.runtimeversionmanager.releases.engine.{
|
||||||
EngineRelease,
|
EngineRelease,
|
||||||
@ -51,6 +48,9 @@ object DefaultDistributionConfiguration
|
|||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
lazy val resourceManager = new ResourceManager(lockManager)
|
lazy val resourceManager = new ResourceManager(lockManager)
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
lazy val editionManager = EditionManager(distributionManager)
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
lazy val temporaryDirectoryManager =
|
lazy val temporaryDirectoryManager =
|
||||||
new TemporaryDirectoryManager(distributionManager, resourceManager)
|
new TemporaryDirectoryManager(distributionManager, resourceManager)
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
package org.enso.projectmanager.versionmanagement
|
package org.enso.projectmanager.versionmanagement
|
||||||
|
|
||||||
import org.enso.runtimeversionmanager.Environment
|
import org.enso.distribution.locking.ResourceManager
|
||||||
|
import org.enso.distribution.{DistributionManager, EditionManager, Environment}
|
||||||
import org.enso.runtimeversionmanager.components.{
|
import org.enso.runtimeversionmanager.components.{
|
||||||
RuntimeVersionManagementUserInterface,
|
RuntimeVersionManagementUserInterface,
|
||||||
RuntimeVersionManager
|
RuntimeVersionManager
|
||||||
}
|
}
|
||||||
import org.enso.runtimeversionmanager.distribution.{
|
import org.enso.runtimeversionmanager.distribution.TemporaryDirectoryManager
|
||||||
DistributionManager,
|
|
||||||
TemporaryDirectoryManager
|
|
||||||
}
|
|
||||||
import org.enso.runtimeversionmanager.locking.ResourceManager
|
|
||||||
import org.enso.runtimeversionmanager.releases.ReleaseProvider
|
import org.enso.runtimeversionmanager.releases.ReleaseProvider
|
||||||
import org.enso.runtimeversionmanager.releases.engine.EngineRelease
|
import org.enso.runtimeversionmanager.releases.engine.EngineRelease
|
||||||
import org.enso.runtimeversionmanager.runner.JVMSettings
|
import org.enso.runtimeversionmanager.runner.JVMSettings
|
||||||
@ -31,6 +28,9 @@ trait DistributionConfiguration {
|
|||||||
/** A [[ResourceManager]] instance. */
|
/** A [[ResourceManager]] instance. */
|
||||||
def resourceManager: ResourceManager
|
def resourceManager: ResourceManager
|
||||||
|
|
||||||
|
/** An [[EditionManager]] instance. */
|
||||||
|
def editionManager: EditionManager
|
||||||
|
|
||||||
/** A [[TemporaryDirectoryManager]] instance. */
|
/** A [[TemporaryDirectoryManager]] instance. */
|
||||||
def temporaryDirectoryManager: TemporaryDirectoryManager
|
def temporaryDirectoryManager: TemporaryDirectoryManager
|
||||||
|
|
||||||
|
@ -4,13 +4,13 @@ import java.io.File
|
|||||||
import java.nio.file.{Files, Path}
|
import java.nio.file.{Files, Path}
|
||||||
import java.time.{OffsetDateTime, ZoneOffset}
|
import java.time.{OffsetDateTime, ZoneOffset}
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
import akka.testkit.TestActors.blackholeProps
|
import akka.testkit.TestActors.blackholeProps
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import io.circe.Json
|
import io.circe.Json
|
||||||
import io.circe.parser.parse
|
import io.circe.parser.parse
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
|
import org.enso.distribution.OS
|
||||||
import org.enso.jsonrpc.test.JsonRpcServerTestKit
|
import org.enso.jsonrpc.test.JsonRpcServerTestKit
|
||||||
import org.enso.jsonrpc.{ClientControllerFactory, Protocol}
|
import org.enso.jsonrpc.{ClientControllerFactory, Protocol}
|
||||||
import org.enso.loggingservice.printers.StderrPrinterWithColors
|
import org.enso.loggingservice.printers.StderrPrinterWithColors
|
||||||
@ -39,7 +39,6 @@ import org.enso.projectmanager.service.{
|
|||||||
ProjectService
|
ProjectService
|
||||||
}
|
}
|
||||||
import org.enso.projectmanager.test.{ObservableGenerator, ProgrammableClock}
|
import org.enso.projectmanager.test.{ObservableGenerator, ProgrammableClock}
|
||||||
import org.enso.runtimeversionmanager.OS
|
|
||||||
import org.enso.runtimeversionmanager.test.FakeReleases
|
import org.enso.runtimeversionmanager.test.FakeReleases
|
||||||
import org.scalatest.BeforeAndAfterAll
|
import org.scalatest.BeforeAndAfterAll
|
||||||
import pureconfig.ConfigSource
|
import pureconfig.ConfigSource
|
||||||
|
@ -5,7 +5,7 @@ import akka.testkit.TestDuration
|
|||||||
import io.circe.Json
|
import io.circe.Json
|
||||||
import io.circe.syntax._
|
import io.circe.syntax._
|
||||||
import io.circe.literal._
|
import io.circe.literal._
|
||||||
import org.enso.pkg.SemVerJson._
|
import org.enso.editions.SemVerJson._
|
||||||
import io.circe.parser.parse
|
import io.circe.parser.parse
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
import org.enso.projectmanager.data.{MissingComponentAction, Socket}
|
import org.enso.projectmanager.data.{MissingComponentAction, Socket}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package org.enso.projectmanager
|
package org.enso.projectmanager
|
||||||
|
|
||||||
import java.nio.file.Path
|
import org.enso.distribution.{DistributionManager, EditionManager}
|
||||||
|
import org.enso.distribution.locking.ResourceManager
|
||||||
|
|
||||||
|
import java.nio.file.Path
|
||||||
import org.enso.projectmanager.versionmanagement.DistributionConfiguration
|
import org.enso.projectmanager.versionmanagement.DistributionConfiguration
|
||||||
import org.enso.runtimeversionmanager.components.{
|
import org.enso.runtimeversionmanager.components.{
|
||||||
GraalVMComponentConfiguration,
|
GraalVMComponentConfiguration,
|
||||||
@ -9,11 +11,7 @@ import org.enso.runtimeversionmanager.components.{
|
|||||||
RuntimeVersionManagementUserInterface,
|
RuntimeVersionManagementUserInterface,
|
||||||
RuntimeVersionManager
|
RuntimeVersionManager
|
||||||
}
|
}
|
||||||
import org.enso.runtimeversionmanager.distribution.{
|
import org.enso.runtimeversionmanager.distribution.TemporaryDirectoryManager
|
||||||
DistributionManager,
|
|
||||||
TemporaryDirectoryManager
|
|
||||||
}
|
|
||||||
import org.enso.runtimeversionmanager.locking.ResourceManager
|
|
||||||
import org.enso.runtimeversionmanager.releases.engine.{
|
import org.enso.runtimeversionmanager.releases.engine.{
|
||||||
EngineRelease,
|
EngineRelease,
|
||||||
EngineReleaseProvider
|
EngineReleaseProvider
|
||||||
@ -66,6 +64,8 @@ class TestDistributionConfiguration(
|
|||||||
|
|
||||||
lazy val resourceManager = new ResourceManager(lockManager)
|
lazy val resourceManager = new ResourceManager(lockManager)
|
||||||
|
|
||||||
|
lazy val editionManager: EditionManager = EditionManager(distributionManager)
|
||||||
|
|
||||||
lazy val temporaryDirectoryManager =
|
lazy val temporaryDirectoryManager =
|
||||||
new TemporaryDirectoryManager(distributionManager, resourceManager)
|
new TemporaryDirectoryManager(distributionManager, resourceManager)
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import nl.gn0s1s.bump.SemVer
|
|||||||
import org.enso.projectmanager.data.MissingComponentAction
|
import org.enso.projectmanager.data.MissingComponentAction
|
||||||
import org.enso.projectmanager.{BaseServerSpec, ProjectManagementOps}
|
import org.enso.projectmanager.{BaseServerSpec, ProjectManagementOps}
|
||||||
import org.enso.testkit.RetrySpec
|
import org.enso.testkit.RetrySpec
|
||||||
import org.enso.pkg.SemVerJson._
|
import org.enso.editions.SemVerJson._
|
||||||
|
|
||||||
class ProjectCreateMissingComponentsSpec
|
class ProjectCreateMissingComponentsSpec
|
||||||
extends BaseServerSpec
|
extends BaseServerSpec
|
||||||
|
@ -7,7 +7,7 @@ import java.util.UUID
|
|||||||
import io.circe.literal._
|
import io.circe.literal._
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
import org.enso.pkg.SemVerJson._
|
import org.enso.editions.SemVerJson._
|
||||||
import org.enso.projectmanager.{BaseServerSpec, ProjectManagementOps}
|
import org.enso.projectmanager.{BaseServerSpec, ProjectManagementOps}
|
||||||
import org.enso.testkit.{FlakySpec, RetrySpec}
|
import org.enso.testkit.{FlakySpec, RetrySpec}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user