mirror of
https://github.com/enso-org/enso.git
synced 2024-11-22 11:52:59 +03:00
Dynamically Loading Libraries (#1826)
This commit is contained in:
parent
8d71145d57
commit
e58b5eb81d
@ -7,6 +7,10 @@
|
||||
project ([#1806](https://github.com/enso-org/enso/pull/1806)).
|
||||
- Fixed a bug where unresolved imports would crash the compiler
|
||||
([#1822](https://github.com/enso-org/enso/pull/1822)).
|
||||
- Implemented the ability to dynamically load local libraries
|
||||
([#1826](https://github.com/enso-org/enso/pull/1826)). Currently, it only
|
||||
supports the loading of local libraries, but will be integrated with the
|
||||
editions system soon.
|
||||
|
||||
## Tooling
|
||||
|
||||
|
14
build.sbt
14
build.sbt
@ -652,6 +652,18 @@ lazy val `logging-service` = project
|
||||
.dependsOn(`akka-native`)
|
||||
.dependsOn(`logging-utils`)
|
||||
|
||||
lazy val `logging-truffle-connector` = project
|
||||
.in(file("lib/scala/logging-truffle-connector"))
|
||||
.settings(
|
||||
version := "0.1",
|
||||
libraryDependencies ++= Seq(
|
||||
"org.slf4j" % "slf4j-api" % slf4jVersion,
|
||||
"org.graalvm.truffle" % "truffle-api" % graalVersion % "provided"
|
||||
)
|
||||
)
|
||||
.dependsOn(`logging-utils`)
|
||||
.dependsOn(`polyglot-api`)
|
||||
|
||||
lazy val cli = project
|
||||
.in(file("lib/scala/cli"))
|
||||
.configs(Test)
|
||||
@ -1084,8 +1096,10 @@ lazy val runtime = (project in file("engine/runtime"))
|
||||
.dependsOn(`polyglot-api`)
|
||||
.dependsOn(`text-buffer`)
|
||||
.dependsOn(searcher)
|
||||
.dependsOn(`library-manager`)
|
||||
.dependsOn(testkit % Test)
|
||||
.dependsOn(`logging-utils`)
|
||||
.dependsOn(`logging-truffle-connector`)
|
||||
.dependsOn(`docs-generator`)
|
||||
|
||||
/* Note [Unmanaged Classpath]
|
||||
|
@ -26,11 +26,6 @@ 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`.
|
||||
|
||||
|
||||
'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.
|
||||
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`.
|
||||
|
@ -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
|
||||
*
|
||||
@ -43,3 +27,19 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* 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");
|
||||
* 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");
|
||||
* 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");
|
||||
* 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) 2009-2020 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
@ -15,8 +15,6 @@
|
||||
* 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-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
|
||||
* 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");
|
||||
* 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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -1,24 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2004-2011 QOS.ch
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
@ -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) 2009-2020 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
@ -15,8 +15,6 @@
|
||||
* 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-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
|
||||
* 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");
|
||||
* 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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -14,3 +14,4 @@ Documents in this section describe Enso's library ecosystem.
|
||||
organizes library versioning.
|
||||
- [**Repositories:**](./repositories.md) Information on the structure of
|
||||
repositories providing Enso libraries and Editions.
|
||||
- [**Sharing Libraries:**](./sharing.md) Information on how to share libraries.
|
||||
|
70
docs/libraries/sharing.md
Normal file
70
docs/libraries/sharing.md
Normal file
@ -0,0 +1,70 @@
|
||||
---
|
||||
layout: developer-doc
|
||||
title: Sharing Libraries
|
||||
category: libraries
|
||||
tags: [libraries, editions, sharing]
|
||||
order: 3
|
||||
---
|
||||
|
||||
# Sharing Libraries
|
||||
|
||||
This document explains how users can share Enso libraries.
|
||||
|
||||
<!-- MarkdownTOC levels="2,3" autolink="true" -->
|
||||
|
||||
- [Sharing Privately](#sharing-privately)
|
||||
- [Publishing](#publishing)
|
||||
|
||||
<!-- /MarkdownTOC -->
|
||||
|
||||
## Sharing Privately
|
||||
|
||||
To prepare the project for sharing, make sure that it has a proper `namespace`
|
||||
field set in `package.yaml`. It should be set to something unique, like your
|
||||
username.
|
||||
|
||||
> **NOTE**: The field `namespace` is a temporary workaround and in the near
|
||||
> future it will be deprecated and handled mostly automatically. For now however
|
||||
> you need to set it properly.
|
||||
|
||||
To share an Enso library, all you need to do is to package the project into an
|
||||
archive (for example ZIP) and share it (through e-mail, cloud drive services
|
||||
etc.) with your peers. Now to be able to use the library that was shared with
|
||||
you, you need to extract it to the directory
|
||||
`~/enso/libraries/<namespace>/<Project_Name>` (where on Windows `~` should be
|
||||
interpreted as your user home directory). To make sure that the library is
|
||||
extracted correctly, make sure that under the path
|
||||
`~/enso/libraries/<namespace>/<Project_Name>/package.yaml` and that its
|
||||
`namespace` field has the same value as the name of the `<namespace>` directory.
|
||||
|
||||
> The below step is not necessary yet, but it will be needed once the editions
|
||||
> system is fully integrated, so it is better to perform it for forwards
|
||||
> compatibility.
|
||||
|
||||
Now you need to set up your project properly to be able to use this unpublished
|
||||
library. The simplest way to do that is to set `prefer-local-libraries` in your
|
||||
project's `package.yaml` to `true`. This will make all libraries from
|
||||
`~/enso/libraries` take precedence over published libraries set-up in the
|
||||
edition. Alternatively, if you do not want to override all libraries, but only
|
||||
some of them, you can add a local library override, by adding a proper entry in
|
||||
the `libraries` section of the `edition` in your project's `package.yaml`, like
|
||||
shown below:
|
||||
|
||||
```yaml
|
||||
edition:
|
||||
(...)
|
||||
libraries:
|
||||
- name: <namespace>.<Project_Name>
|
||||
repository: local
|
||||
```
|
||||
|
||||
Now, you can use your library by adding a proper import to your project:
|
||||
|
||||
```
|
||||
import <namespace>.<Project_Name>
|
||||
```
|
||||
|
||||
## Publishing
|
||||
|
||||
> Soon it will be possible to share the libraries through the Marketplace, but
|
||||
> it is still a work in progress.
|
@ -200,7 +200,8 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: LogLevel) {
|
||||
.allowAllAccess(true)
|
||||
.allowExperimentalOptions(true)
|
||||
.option(RuntimeServerInfo.ENABLE_OPTION, "true")
|
||||
.option(RuntimeOptions.PACKAGES_PATH, serverConfig.contentRootPath)
|
||||
.option(RuntimeOptions.INTERACTIVE_MODE, "true")
|
||||
.option(RuntimeOptions.PROJECT_ROOT, serverConfig.contentRootPath)
|
||||
.option(
|
||||
RuntimeOptions.LOG_LEVEL,
|
||||
JavaLoggingLogHandler.getJavaLogLevelFor(logLevel).getName
|
||||
|
@ -7,6 +7,7 @@ import org.enso.languageserver.filemanager.ContentRootManagerActor.ContentRoots
|
||||
import org.enso.languageserver.filemanager.ContentRootManagerProtocol._
|
||||
import org.enso.languageserver.monitoring.MonitoringProtocol.{Ping, Pong}
|
||||
import org.enso.languageserver.util.UnhandledLogging
|
||||
import org.enso.logger.masking.MaskedPath
|
||||
import org.enso.polyglot.runtime.Runtime.Api
|
||||
|
||||
import java.io.File
|
||||
@ -57,17 +58,22 @@ class ContentRootManagerActor(config: Config)
|
||||
sender() ! ContentRootsAddedNotification(contentRoots.toList)
|
||||
context.become(mainStage(contentRoots, subscribers + sender()))
|
||||
|
||||
case Api.LibraryLoaded(libraryName, libraryVersion, rootPath) =>
|
||||
case Api.LibraryLoaded(namespace, name, version, rootPath) =>
|
||||
val libraryRoot = ContentRootWithFile(
|
||||
ContentRoot.Library(
|
||||
id = UUID.randomUUID(),
|
||||
namespace = libraryName.namespace,
|
||||
name = libraryName.name,
|
||||
version = libraryVersion.toString
|
||||
namespace = namespace,
|
||||
name = name,
|
||||
version = version.toString
|
||||
),
|
||||
file = rootPath.getCanonicalFile
|
||||
)
|
||||
|
||||
logger.trace(
|
||||
s"Library root for [$namespace.$name:$version] added at " +
|
||||
s"[${MaskedPath(rootPath.toPath).applyMasking()}]."
|
||||
)
|
||||
|
||||
subscribers.foreach { subscriber =>
|
||||
subscriber ! ContentRootsAddedNotification(List(libraryRoot))
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package org.enso.languageserver.filemanager
|
||||
import akka.actor.{ActorRef, ActorSystem}
|
||||
import akka.testkit.{TestDuration, TestKit, TestProbe}
|
||||
import org.apache.commons.lang3.SystemUtils
|
||||
import org.enso.editions.{LibraryName, LibraryVersion}
|
||||
import org.enso.languageserver.data._
|
||||
import org.enso.languageserver.filemanager.ContentRootManagerProtocol.{
|
||||
ContentRootsAddedNotification,
|
||||
@ -11,11 +10,11 @@ import org.enso.languageserver.filemanager.ContentRootManagerProtocol.{
|
||||
}
|
||||
import org.enso.polyglot.runtime.Runtime.Api
|
||||
import org.enso.testkit.EitherValue
|
||||
import org.scalatest.{Inside, OptionValues}
|
||||
import org.scalatest.concurrent.Futures
|
||||
import org.scalatest.concurrent.ScalaFutures.convertScalaFuture
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.wordspec.AnyWordSpecLike
|
||||
import org.scalatest.{Inside, OptionValues}
|
||||
|
||||
import java.io.File
|
||||
import java.nio.file.{Path => JPath}
|
||||
@ -87,12 +86,10 @@ class ContentRootManagerSpec
|
||||
fsRoots should not be empty
|
||||
}
|
||||
|
||||
val libraryName = LibraryName("Foo", "Bar")
|
||||
val libraryVersion = LibraryVersion.Local
|
||||
val rootPath = new File("foobar")
|
||||
val rootPath = new File("foobar")
|
||||
|
||||
system.eventStream.publish(
|
||||
Api.LibraryLoaded(libraryName, libraryVersion, rootPath)
|
||||
Api.LibraryLoaded("Foo", "Bar", "local", rootPath)
|
||||
)
|
||||
|
||||
inside(subscriberProbe.receiveOne(2.seconds.dilated)) {
|
||||
|
@ -2,11 +2,8 @@ package org.enso.languageserver.websocket.json
|
||||
|
||||
import io.circe.literal._
|
||||
import io.circe.parser.parse
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.bouncycastle.util.encoders.Hex
|
||||
import org.enso.editions.Editions.Repository
|
||||
import org.enso.editions.{LibraryName, LibraryVersion}
|
||||
import org.enso.languageserver.data._
|
||||
import org.enso.polyglot.runtime.Runtime.Api
|
||||
import org.enso.testkit.RetrySpec
|
||||
@ -1792,15 +1789,10 @@ class FileManagerTest extends BaseServerTest with RetrySpec {
|
||||
|
||||
"Content root management" must {
|
||||
"notify the IDE when a new root is added" in {
|
||||
val client = getInitialisedWsClient()
|
||||
|
||||
val repo = Repository.make("example", "https://example.com/").get
|
||||
|
||||
val libraryName = LibraryName("Foo", "Bar")
|
||||
val libraryVersion = LibraryVersion.Published(SemVer(1, 2, 3), repo)
|
||||
val rootPath = new File("foobar")
|
||||
val client = getInitialisedWsClient()
|
||||
val rootPath = new File("foobar")
|
||||
system.eventStream.publish(
|
||||
Api.LibraryLoaded(libraryName, libraryVersion, rootPath)
|
||||
Api.LibraryLoaded("Foo", "Bar", "1.2.3", rootPath)
|
||||
)
|
||||
|
||||
val parsed = parse(client.expectMessage())
|
||||
|
@ -51,6 +51,7 @@ case class Launcher(cliOptions: GlobalCLIOptions) {
|
||||
private lazy val runner =
|
||||
new LauncherRunner(
|
||||
projectManager,
|
||||
distributionManager,
|
||||
configurationManager,
|
||||
componentsManager,
|
||||
editionManager,
|
||||
|
@ -2,7 +2,7 @@ package org.enso.launcher.components
|
||||
|
||||
import akka.http.scaladsl.model.Uri
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.distribution.{EditionManager, Environment}
|
||||
import org.enso.distribution.{DistributionManager, EditionManager, Environment}
|
||||
import org.enso.launcher.project.ProjectManager
|
||||
import org.enso.loggingservice.LogLevel
|
||||
import org.enso.runtimeversionmanager.components.RuntimeVersionManager
|
||||
@ -17,6 +17,7 @@ import scala.util.Try
|
||||
*/
|
||||
class LauncherRunner(
|
||||
projectManager: ProjectManager,
|
||||
distributionManager: DistributionManager,
|
||||
configurationManager: GlobalConfigurationManager,
|
||||
componentsManager: RuntimeVersionManager,
|
||||
editionManager: EditionManager,
|
||||
@ -24,6 +25,7 @@ class LauncherRunner(
|
||||
loggerConnection: Future[Option[Uri]]
|
||||
) extends Runner(
|
||||
componentsManager,
|
||||
distributionManager,
|
||||
configurationManager,
|
||||
editionManager,
|
||||
environment,
|
||||
|
@ -133,7 +133,7 @@ class DistributionInstaller(
|
||||
throw InstallationError(
|
||||
s"${installed.configDirectory} already exists but is not a " +
|
||||
s"directory. Please remove it or change the installation " +
|
||||
s"location by setting `${installed.ENSO_CONFIG_DIRECTORY}`."
|
||||
s"location by setting `${manager.ENSO_CONFIG_DIRECTORY}`."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ class DistributionUninstaller(
|
||||
val remaining =
|
||||
FileSystem.listDirectory(manager.paths.config).map(_.getFileName.toString)
|
||||
handleRemainingFiles(
|
||||
manager.LocallyInstalledDirectories.ENSO_CONFIG_DIRECTORY,
|
||||
manager.ENSO_CONFIG_DIRECTORY,
|
||||
manager.paths.config,
|
||||
remaining
|
||||
)
|
||||
@ -244,7 +244,7 @@ class DistributionUninstaller(
|
||||
.toSet -- ignoredFiles
|
||||
if (remainingFiles.nonEmpty) {
|
||||
handleRemainingFiles(
|
||||
manager.LocallyInstalledDirectories.ENSO_DATA_DIRECTORY,
|
||||
manager.ENSO_DATA_DIRECTORY,
|
||||
dataRoot.toAbsolutePath.normalize,
|
||||
remainingFiles.toSeq
|
||||
)
|
||||
|
@ -37,6 +37,7 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest {
|
||||
val runner =
|
||||
new LauncherRunner(
|
||||
projectManager,
|
||||
distributionManager,
|
||||
configurationManager,
|
||||
componentsManager,
|
||||
editionManager,
|
||||
|
@ -8,10 +8,10 @@ import org.graalvm.options.OptionKey;
|
||||
|
||||
/** Class representing runtime options supported by the Enso engine. */
|
||||
public class RuntimeOptions {
|
||||
public static final String PACKAGES_PATH = optionName("packagesPath");
|
||||
public static final OptionKey<String> PACKAGES_PATH_KEY = new OptionKey<>("");
|
||||
private static final OptionDescriptor PACKAGES_PATH_DESCRIPTOR =
|
||||
OptionDescriptor.newBuilder(PACKAGES_PATH_KEY, PACKAGES_PATH).build();
|
||||
public static final String PROJECT_ROOT = optionName("projectRoot");
|
||||
public static final OptionKey<String> PROJECT_ROOT_KEY = new OptionKey<>("");
|
||||
private static final OptionDescriptor PROJECT_ROOT_DESCRIPTOR =
|
||||
OptionDescriptor.newBuilder(PROJECT_ROOT_KEY, PROJECT_ROOT).build();
|
||||
|
||||
public static final String STRICT_ERRORS = optionName("strictErrors");
|
||||
public static final OptionKey<Boolean> STRICT_ERRORS_KEY = new OptionKey<>(false);
|
||||
@ -28,6 +28,11 @@ public class RuntimeOptions {
|
||||
private static final OptionDescriptor LOG_LEVEL_DESCRIPTOR =
|
||||
OptionDescriptor.newBuilder(LOG_LEVEL_KEY, LOG_LEVEL).build();
|
||||
|
||||
public static final String INTERACTIVE_MODE = interpreterOptionName("interactive");
|
||||
public static final OptionKey<Boolean> INTERACTIVE_MODE_KEY = new OptionKey<>(false);
|
||||
public static final OptionDescriptor INTERACTIVE_MODE_DESCRIPTOR =
|
||||
OptionDescriptor.newBuilder(INTERACTIVE_MODE_KEY, INTERACTIVE_MODE).build();
|
||||
|
||||
public static final String INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION =
|
||||
interpreterOptionName("sequentialCommandExecution");
|
||||
public static final OptionKey<Boolean> INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION_KEY =
|
||||
@ -41,22 +46,30 @@ public class RuntimeOptions {
|
||||
public static final String ENABLE_PROJECT_SUGGESTIONS = optionName("enableProjectSuggestions");
|
||||
public static final OptionKey<Boolean> ENABLE_PROJECT_SUGGESTIONS_KEY = new OptionKey<>(true);
|
||||
private static final OptionDescriptor ENABLE_PROJECT_SUGGESTIONS_DESCRIPTOR =
|
||||
OptionDescriptor.newBuilder(ENABLE_PROJECT_SUGGESTIONS_KEY, ENABLE_PROJECT_SUGGESTIONS).build();
|
||||
OptionDescriptor.newBuilder(ENABLE_PROJECT_SUGGESTIONS_KEY, ENABLE_PROJECT_SUGGESTIONS)
|
||||
.build();
|
||||
|
||||
public static final String ENABLE_GLOBAL_SUGGESTIONS = optionName("enableGlobalSuggestions");
|
||||
public static final OptionKey<Boolean> ENABLE_GLOBAL_SUGGESTIONS_KEY = new OptionKey<>(true);
|
||||
private static final OptionDescriptor ENABLE_GLOBAL_SUGGESTIONS_DESCRIPTOR =
|
||||
OptionDescriptor.newBuilder(ENABLE_GLOBAL_SUGGESTIONS_KEY, ENABLE_GLOBAL_SUGGESTIONS).build();
|
||||
|
||||
public static final String PRELOADED_PACKAGES_PATHS = optionName("preloadedPackagesPaths");
|
||||
public static final OptionKey<String> PRELOADED_PACKAGES_PATHS_KEY = new OptionKey<>("");
|
||||
private static final OptionDescriptor PRELOADED_PACKAGES_PATHS_DESCRIPTOR =
|
||||
OptionDescriptor.newBuilder(PRELOADED_PACKAGES_PATHS_KEY, PRELOADED_PACKAGES_PATHS).build();
|
||||
|
||||
public static final OptionDescriptors OPTION_DESCRIPTORS =
|
||||
OptionDescriptors.create(
|
||||
Arrays.asList(
|
||||
PACKAGES_PATH_DESCRIPTOR,
|
||||
PROJECT_ROOT_DESCRIPTOR,
|
||||
STRICT_ERRORS_DESCRIPTOR,
|
||||
LOG_LEVEL_DESCRIPTOR,
|
||||
DISABLE_INLINE_CACHES_DESCRIPTOR,
|
||||
ENABLE_PROJECT_SUGGESTIONS_DESCRIPTOR,
|
||||
ENABLE_GLOBAL_SUGGESTIONS_DESCRIPTOR,
|
||||
INTERACTIVE_MODE_DESCRIPTOR,
|
||||
PRELOADED_PACKAGES_PATHS_DESCRIPTOR,
|
||||
INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION_DESCRIPTOR));
|
||||
|
||||
/**
|
||||
|
@ -7,7 +7,6 @@ import com.fasterxml.jackson.module.scala.{
|
||||
DefaultScalaModule,
|
||||
ScalaObjectMapper
|
||||
}
|
||||
import org.enso.editions.{LibraryName, LibraryVersion}
|
||||
import org.enso.logger.masking.{MaskedPath, MaskedString, ToLogString}
|
||||
import org.enso.polyglot.Suggestion
|
||||
import org.enso.polyglot.data.{Tree, TypeGraph}
|
||||
@ -1341,14 +1340,16 @@ object Runtime {
|
||||
/** Signals that a new library has been imported, which means its content
|
||||
* root should be registered.
|
||||
*
|
||||
* @param namespace namespace of the loaded library
|
||||
* @param name name of the loaded library
|
||||
* @param version library version that was selected
|
||||
* @param location location on disk of the project root belonging to the
|
||||
* loaded library
|
||||
*/
|
||||
case class LibraryLoaded(
|
||||
name: LibraryName,
|
||||
version: LibraryVersion,
|
||||
namespace: String,
|
||||
name: String,
|
||||
version: String,
|
||||
location: File
|
||||
) extends ApiNotification
|
||||
|
||||
|
@ -18,7 +18,7 @@ class ModuleManagementTest extends AnyFlatSpec with Matchers {
|
||||
.newBuilder(LanguageInfo.ID)
|
||||
.allowExperimentalOptions(true)
|
||||
.allowAllAccess(true)
|
||||
.option(RuntimeOptions.PACKAGES_PATH, pkg.root.getAbsolutePath)
|
||||
.option(RuntimeOptions.PROJECT_ROOT, pkg.root.getAbsolutePath)
|
||||
.option(RuntimeOptions.STRICT_ERRORS, "true")
|
||||
.build()
|
||||
)
|
||||
|
@ -17,7 +17,8 @@ class ContextFactory {
|
||||
|
||||
/** Creates a new Graal polyglot context.
|
||||
*
|
||||
* @param packagesPath Enso packages path
|
||||
* @param projectRoot root of the project the interpreter is being run in
|
||||
* (or empty if ran outside of any projects)
|
||||
* @param in the input stream for standard in
|
||||
* @param out the output stream for standard out
|
||||
* @param repl the Repl manager to use for this context
|
||||
@ -26,7 +27,7 @@ class ContextFactory {
|
||||
* @return configured Context instance
|
||||
*/
|
||||
def create(
|
||||
packagesPath: String = "",
|
||||
projectRoot: String = "",
|
||||
in: InputStream,
|
||||
out: OutputStream,
|
||||
repl: Repl,
|
||||
@ -37,7 +38,7 @@ class ContextFactory {
|
||||
.newBuilder()
|
||||
.allowExperimentalOptions(true)
|
||||
.allowAllAccess(true)
|
||||
.option(RuntimeOptions.PACKAGES_PATH, packagesPath)
|
||||
.option(RuntimeOptions.PROJECT_ROOT, projectRoot)
|
||||
.option(RuntimeOptions.STRICT_ERRORS, strictErrors.toString)
|
||||
.option(DebugServerInfo.ENABLE_OPTION, "true")
|
||||
.option("js.foreign-object-prototype", "true")
|
||||
|
@ -284,7 +284,7 @@ object Main {
|
||||
exitFail()
|
||||
}
|
||||
val projectMode = file.isDirectory
|
||||
val packagePath =
|
||||
val projectRoot =
|
||||
if (projectMode) {
|
||||
projectPath match {
|
||||
case Some(inProject) if inProject != path =>
|
||||
@ -299,7 +299,7 @@ object Main {
|
||||
file.getAbsolutePath
|
||||
} else projectPath.getOrElse("")
|
||||
val context = new ContextFactory().create(
|
||||
packagePath,
|
||||
projectRoot,
|
||||
System.in,
|
||||
System.out,
|
||||
Repl(TerminalIO()),
|
||||
@ -426,10 +426,10 @@ object Main {
|
||||
|$mainMethodName = Debug.breakpoint
|
||||
|""".stripMargin
|
||||
val replModuleName = "Internal_Repl_Module___"
|
||||
val packagePath = projectPath.getOrElse("")
|
||||
val projectRoot = projectPath.getOrElse("")
|
||||
val context =
|
||||
new ContextFactory().create(
|
||||
packagePath,
|
||||
projectRoot,
|
||||
System.in,
|
||||
System.out,
|
||||
Repl(TerminalIO()),
|
||||
|
@ -10,6 +10,9 @@ import com.oracle.truffle.api.instrumentation.StandardTags;
|
||||
import com.oracle.truffle.api.nodes.RootNode;
|
||||
import org.enso.interpreter.epb.EpbLanguage;
|
||||
import org.enso.interpreter.instrument.IdExecutionInstrument;
|
||||
import org.enso.interpreter.instrument.NotificationHandler;
|
||||
import org.enso.interpreter.instrument.NotificationHandler.Forwarder;
|
||||
import org.enso.interpreter.instrument.NotificationHandler.TextMode$;
|
||||
import org.enso.interpreter.node.ProgramRootNode;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.tag.IdentifiedTag;
|
||||
@ -60,11 +63,18 @@ public final class Language extends TruffleLanguage<Context> {
|
||||
*/
|
||||
@Override
|
||||
protected Context createContext(Env env) {
|
||||
Context context = new Context(this, getLanguageHome(), env);
|
||||
var notificationHandler = new Forwarder();
|
||||
boolean isInteractiveMode = env.getOptions().get(RuntimeOptions.INTERACTIVE_MODE_KEY);
|
||||
boolean isTextMode = !isInteractiveMode;
|
||||
if (isTextMode) {
|
||||
notificationHandler.addListener(TextMode$.MODULE$);
|
||||
}
|
||||
|
||||
Context context = new Context(this, getLanguageHome(), env, notificationHandler);
|
||||
InstrumentInfo idValueListenerInstrument =
|
||||
env.getInstruments().get(IdExecutionInstrument.INSTRUMENT_ID);
|
||||
idExecutionInstrument = env.lookup(idValueListenerInstrument, IdExecutionInstrument.class);
|
||||
env.registerService(new ExecutionService(context, idExecutionInstrument));
|
||||
env.registerService(new ExecutionService(context, idExecutionInstrument, notificationHandler));
|
||||
return context;
|
||||
}
|
||||
|
||||
|
@ -5,22 +5,42 @@ import com.oracle.truffle.api.TruffleLanguage;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import org.enso.polyglot.RuntimeOptions;
|
||||
|
||||
public class OptionsHelper {
|
||||
/**
|
||||
* Gets the list of locations of known modules that can be imported in the current run.
|
||||
* Gets the location of the project that is the context of the current run.
|
||||
*
|
||||
* @param env the current run environment
|
||||
* @return the list of locations of known modules that can be imported in the current run
|
||||
* @return the project path (can be empty if running outside of the project)
|
||||
*/
|
||||
public static List<TruffleFile> getPackagesPaths(TruffleLanguage.Env env) {
|
||||
if (env.getOptions().get(RuntimeOptions.PACKAGES_PATH_KEY).equals("")) {
|
||||
public static Optional<TruffleFile> getProjectRoot(TruffleLanguage.Env env) {
|
||||
String option = env.getOptions().get(RuntimeOptions.PROJECT_ROOT_KEY);
|
||||
if (option.equals("")) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
return Optional.of(env.getInternalTruffleFile(option));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of locations of packages that should be preloaded.
|
||||
*
|
||||
* <p>This is meant mainly for testing purposes, as normally package locations are handled by
|
||||
* environment variables, see {@link org.enso.distribution.DistributionManager}.
|
||||
*
|
||||
* <p>It will also be slightly repurposed after integrating with editions and once the standard
|
||||
* library directory structure is upgraded to the new format.
|
||||
*/
|
||||
public static List<TruffleFile> getPreloadedPackagesPaths(TruffleLanguage.Env env) {
|
||||
String option = env.getOptions().get(RuntimeOptions.PRELOADED_PACKAGES_PATHS_KEY);
|
||||
if (option.equals("")) {
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
return Arrays.stream(
|
||||
env.getOptions().get(RuntimeOptions.PACKAGES_PATH_KEY).split(env.getPathSeparator()))
|
||||
option.split(env.getPathSeparator()))
|
||||
.map(env::getInternalTruffleFile)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
@ -4,35 +4,34 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
||||
import com.oracle.truffle.api.TruffleFile;
|
||||
import com.oracle.truffle.api.TruffleLanguage;
|
||||
import com.oracle.truffle.api.TruffleLanguage.Env;
|
||||
import com.oracle.truffle.api.TruffleLogger;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import org.enso.compiler.Compiler;
|
||||
import org.enso.compiler.PackageRepository;
|
||||
import org.enso.compiler.data.CompilerConfig;
|
||||
import org.enso.home.HomeManager;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.OptionsHelper;
|
||||
import org.enso.interpreter.instrument.NotificationHandler;
|
||||
import org.enso.interpreter.runtime.builtin.Builtins;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.scope.TopLevelScope;
|
||||
import org.enso.interpreter.runtime.util.ShadowedPackage;
|
||||
import org.enso.interpreter.runtime.util.TruffleFileSystem;
|
||||
import org.enso.interpreter.util.ScalaConversions;
|
||||
import org.enso.pkg.Package;
|
||||
import org.enso.pkg.PackageManager;
|
||||
import org.enso.pkg.QualifiedName;
|
||||
import org.enso.polyglot.LanguageInfo;
|
||||
import org.enso.polyglot.RuntimeOptions;
|
||||
import scala.jdk.javaapi.CollectionConverters;
|
||||
|
||||
/**
|
||||
* The language context is the internal state of the language that is associated with each thread in
|
||||
@ -42,28 +41,32 @@ public class Context {
|
||||
|
||||
private final Language language;
|
||||
private final Env environment;
|
||||
private final Compiler compiler;
|
||||
private @CompilationFinal Compiler compiler;
|
||||
private final PrintStream out;
|
||||
private final PrintStream err;
|
||||
private final InputStream in;
|
||||
private final BufferedReader inReader;
|
||||
private List<Package<TruffleFile>> packages;
|
||||
private @CompilationFinal PackageRepository packageRepository;
|
||||
private @CompilationFinal TopLevelScope topScope;
|
||||
private final ThreadManager threadManager;
|
||||
private final ResourceManager resourceManager;
|
||||
private final boolean isCachingDisabled;
|
||||
private final Builtins builtins;
|
||||
private final String home;
|
||||
private final List<ShadowedPackage> shadowedPackages;
|
||||
private final CompilerConfig compilerConfig;
|
||||
private final NotificationHandler notificationHandler;
|
||||
private final TruffleLogger logger = TruffleLogger.getLogger(LanguageInfo.ID, Context.class);
|
||||
|
||||
/**
|
||||
* Creates a new Enso context.
|
||||
*
|
||||
* @param language the language identifier
|
||||
* @param home language home
|
||||
* @param environment the execution environment of the {@link TruffleLanguage}
|
||||
* @param notificationHandler a handler for notifications
|
||||
*/
|
||||
public Context(Language language, String home, Env environment) {
|
||||
public Context(
|
||||
Language language, String home, Env environment, NotificationHandler notificationHandler) {
|
||||
this.language = language;
|
||||
this.environment = environment;
|
||||
this.out = new PrintStream(environment.out());
|
||||
@ -75,68 +78,59 @@ public class Context {
|
||||
this.isCachingDisabled = environment.getOptions().get(RuntimeOptions.DISABLE_INLINE_CACHES_KEY);
|
||||
this.compilerConfig = new CompilerConfig(false, true);
|
||||
this.home = home;
|
||||
this.shadowedPackages = new ArrayList<>();
|
||||
|
||||
builtins = new Builtins(this);
|
||||
|
||||
this.compiler =
|
||||
new Compiler(this, builtins, new PackageRepository.Default(this), compilerConfig);
|
||||
this.builtins = new Builtins(this);
|
||||
this.notificationHandler = notificationHandler;
|
||||
}
|
||||
|
||||
/** Perform expensive initialization logic for the context. */
|
||||
public void initialize() {
|
||||
TruffleFileSystem fs = new TruffleFileSystem();
|
||||
HashMap<String, Package<TruffleFile>> packageMap = new HashMap<>();
|
||||
packages = new ArrayList<>();
|
||||
|
||||
if (home != null) {
|
||||
HomeManager<TruffleFile> homeManager =
|
||||
new HomeManager<>(environment.getInternalTruffleFile(home), fs);
|
||||
packageMap.putAll(
|
||||
homeManager.loadStdLib().collect(Collectors.toMap((Package::name), Function.identity())));
|
||||
}
|
||||
|
||||
PackageManager<TruffleFile> packageManager = new PackageManager<>(fs);
|
||||
List<TruffleFile> packagePaths = OptionsHelper.getPackagesPaths(environment);
|
||||
|
||||
// Add user packages one-by-one, shadowing previously added packages. It assumes that the
|
||||
// standard library packages will not clash. In the future, we should be able to disambiguate
|
||||
// packages that clash.
|
||||
for (var packagePath : packagePaths) {
|
||||
Optional<Package<TruffleFile>> asPackage =
|
||||
Optional<TruffleFile> projectRoot = OptionsHelper.getProjectRoot(environment);
|
||||
Optional<Package<TruffleFile>> projectPackage =
|
||||
projectRoot.flatMap(
|
||||
file -> {
|
||||
var result = packageManager.fromDirectory(projectRoot.get());
|
||||
if (result.isEmpty()) {
|
||||
logger.warning("Could not load the project root package.");
|
||||
}
|
||||
return ScalaConversions.asJava(result);
|
||||
});
|
||||
|
||||
packageRepository =
|
||||
PackageRepository.makeLegacyRepository(
|
||||
RuntimeDistributionManager$.MODULE$, this, builtins, notificationHandler);
|
||||
topScope = new TopLevelScope(builtins, packageRepository);
|
||||
this.compiler = new Compiler(this, builtins, packageRepository, compilerConfig);
|
||||
|
||||
projectPackage.ifPresent(
|
||||
pkg -> packageRepository.registerMainProjectPackage(pkg.libraryName(), pkg));
|
||||
|
||||
List<Package<TruffleFile>> packagesToPreload = new ArrayList<>();
|
||||
// TODO [RW] This preloading mechanism should be replaced by prepending this special path to the
|
||||
// local libraries search paths when switching to the actual edition-based resolution.
|
||||
List<TruffleFile> preloadedPackagePaths = OptionsHelper.getPreloadedPackagesPaths(environment);
|
||||
for (var packagePath : preloadedPackagePaths) {
|
||||
Optional<Package<TruffleFile>> pkgOpt =
|
||||
ScalaConversions.asJava(packageManager.fromDirectory(packagePath));
|
||||
if (asPackage.isPresent()) {
|
||||
Package<TruffleFile> pkg = asPackage.get();
|
||||
boolean nameExists = packageMap.containsKey(pkg.name());
|
||||
|
||||
if (nameExists) {
|
||||
shadowedPackages.add(
|
||||
new ShadowedPackage(
|
||||
packageMap.get(pkg.name()).root().getPath(), pkg.root().getPath(), pkg.name()));
|
||||
packageMap.remove(pkg.name());
|
||||
}
|
||||
packageMap.put(pkg.name(), pkg);
|
||||
if (pkgOpt.isPresent()) {
|
||||
var pkg = pkgOpt.get();
|
||||
packagesToPreload.add(pkg);
|
||||
} else {
|
||||
logger.warning("Could not preload a package.");
|
||||
}
|
||||
}
|
||||
|
||||
packages.addAll(packageMap.values());
|
||||
// TODO [RW] this is left here temporarily, until the edition system takes over resolving the
|
||||
// std-lib
|
||||
if (home != null) {
|
||||
HomeManager<TruffleFile> homeManager =
|
||||
new HomeManager<>(environment.getInternalTruffleFile(home), fs);
|
||||
homeManager.loadStdLib().forEach(packagesToPreload::add);
|
||||
}
|
||||
|
||||
packages.forEach(
|
||||
pkg -> {
|
||||
List<TruffleFile> classPathItems =
|
||||
ScalaConversions.asJava(pkg.listPolyglotExtensions("java"));
|
||||
classPathItems.forEach(environment::addToHostClassPath);
|
||||
});
|
||||
|
||||
Map<String, Module> knownFiles =
|
||||
packages.stream()
|
||||
.flatMap(
|
||||
p ->
|
||||
ScalaConversions.asJava(p.listSources()).stream()
|
||||
.map(srcFile -> new Module(srcFile.qualifiedName(), p, srcFile.file())))
|
||||
.collect(Collectors.toMap(mod -> mod.getName().toString(), mod -> mod));
|
||||
|
||||
topScope = new TopLevelScope(builtins, knownFiles);
|
||||
packageRepository.registerForPreload(CollectionConverters.asScala(packagesToPreload).toSeq());
|
||||
}
|
||||
|
||||
public TruffleFile getTruffleFile(File file) {
|
||||
@ -217,7 +211,7 @@ public class Context {
|
||||
*/
|
||||
public Optional<QualifiedName> getModuleNameForFile(File path) {
|
||||
TruffleFile p = getTruffleFile(path);
|
||||
return packages.stream()
|
||||
return ScalaConversions.asJava(packageRepository.getLoadedPackages()).stream()
|
||||
.filter(pkg -> p.startsWith(pkg.sourceDir()))
|
||||
.map(pkg -> pkg.moduleNameForFile(p))
|
||||
.findFirst();
|
||||
@ -231,23 +225,7 @@ public class Context {
|
||||
* @param newName the new project name
|
||||
*/
|
||||
public void renameProject(String namespace, String oldName, String newName) {
|
||||
renamePackages(namespace, oldName, newName);
|
||||
topScope.renameProjectInModules(namespace, oldName, newName);
|
||||
}
|
||||
|
||||
private void renamePackages(String namespace, String oldName, String newName) {
|
||||
List<Package<TruffleFile>> toChange =
|
||||
packages.stream()
|
||||
.filter(
|
||||
p -> p.config().namespace().equals(namespace) && p.config().name().equals(oldName))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
packages.removeAll(toChange);
|
||||
|
||||
List<Package<TruffleFile>> renamed =
|
||||
toChange.stream().map(p -> p.setPackageName(newName)).collect(Collectors.toList());
|
||||
|
||||
packages.addAll(renamed);
|
||||
packageRepository.renameProject(namespace, oldName, newName);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -294,7 +272,7 @@ public class Context {
|
||||
if (file == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return packages.stream()
|
||||
return ScalaConversions.asJava(packageRepository.getLoadedPackages()).stream()
|
||||
.filter(pkg -> file.getAbsoluteFile().startsWith(pkg.root().getAbsoluteFile()))
|
||||
.findFirst();
|
||||
}
|
||||
@ -317,7 +295,7 @@ public class Context {
|
||||
* @return an object containing the builtin functions
|
||||
*/
|
||||
public Builtins getBuiltins() {
|
||||
return getTopScope().getBuiltins();
|
||||
return this.builtins;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -386,16 +364,6 @@ public class Context {
|
||||
return isCachingDisabled;
|
||||
}
|
||||
|
||||
/** @return the list of shadowed packages */
|
||||
public List<ShadowedPackage> getShadowedPackages() {
|
||||
return shadowedPackages;
|
||||
}
|
||||
|
||||
/** @return the pre-loaded packages */
|
||||
public List<Package<TruffleFile>> getPackages() {
|
||||
return packages;
|
||||
}
|
||||
|
||||
/** @return the compiler configuration for this language */
|
||||
public CompilerConfig getCompilerConfig() {
|
||||
return compilerConfig;
|
||||
|
@ -14,41 +14,39 @@ import com.oracle.truffle.api.library.ExportLibrary;
|
||||
import com.oracle.truffle.api.library.ExportMessage;
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import org.enso.compiler.PackageRepository;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.Module;
|
||||
import org.enso.interpreter.runtime.builtin.Builtins;
|
||||
import org.enso.interpreter.runtime.data.Array;
|
||||
import org.enso.interpreter.runtime.type.Types;
|
||||
import org.enso.interpreter.util.ScalaConversions;
|
||||
import org.enso.pkg.Package;
|
||||
import org.enso.pkg.QualifiedName;
|
||||
import org.enso.pkg.QualifiedName$;
|
||||
import org.enso.polyglot.MethodNames;
|
||||
|
||||
/** Represents the top scope of Enso execution, containing all the importable modules. */
|
||||
@ExportLibrary(InteropLibrary.class)
|
||||
public class TopLevelScope implements TruffleObject {
|
||||
private final Builtins builtins;
|
||||
private final Map<String, Module> modules;
|
||||
private final PackageRepository packageRepository;
|
||||
|
||||
/**
|
||||
* Creates a new instance of top scope.
|
||||
*
|
||||
* @param builtins the automatically-imported builtin module.
|
||||
* @param modules the initial modules this scope contains.
|
||||
* @param packageRepository the {@link PackageRepository} instance that manages loaded packages
|
||||
*/
|
||||
public TopLevelScope(Builtins builtins, Map<String, Module> modules) {
|
||||
public TopLevelScope(Builtins builtins, PackageRepository packageRepository) {
|
||||
this.builtins = builtins;
|
||||
this.modules = modules;
|
||||
this.packageRepository = packageRepository;
|
||||
}
|
||||
|
||||
/** @return the list of modules in the scope. */
|
||||
public Collection<Module> getModules() {
|
||||
return modules.values();
|
||||
return ScalaConversions.asJava(packageRepository.getLoadedModules());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -58,10 +56,7 @@ public class TopLevelScope implements TruffleObject {
|
||||
* @return empty result if the module does not exist or the requested module.
|
||||
*/
|
||||
public Optional<Module> getModule(String name) {
|
||||
if (name.equals(Builtins.MODULE_NAME)) {
|
||||
return Optional.of(builtins.getModule());
|
||||
}
|
||||
return Optional.ofNullable(modules.get(name));
|
||||
return ScalaConversions.asJava(packageRepository.getLoadedModule(name));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -73,32 +68,10 @@ public class TopLevelScope implements TruffleObject {
|
||||
*/
|
||||
public Module createModule(QualifiedName name, Package<TruffleFile> pkg, TruffleFile sourceFile) {
|
||||
Module module = new Module(name, pkg, sourceFile);
|
||||
modules.put(name.toString(), module);
|
||||
packageRepository.registerModuleCreatedInRuntime(module);
|
||||
return module;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames a project part of the included modules.
|
||||
*
|
||||
* @param oldName the old project name
|
||||
* @param newName the new project name
|
||||
*/
|
||||
public void renameProjectInModules(String namespace, String oldName, String newName) {
|
||||
String separator = QualifiedName$.MODULE$.separator();
|
||||
List<String> keys =
|
||||
modules.keySet().stream()
|
||||
.filter(name -> name.startsWith(namespace + separator + oldName + separator))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
keys.stream()
|
||||
.map(modules::remove)
|
||||
.forEach(
|
||||
module -> {
|
||||
module.renameProject(newName);
|
||||
modules.put(module.getName().toString(), module);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the builtins module.
|
||||
*
|
||||
@ -145,15 +118,12 @@ public class TopLevelScope implements TruffleObject {
|
||||
throws ArityException, UnsupportedTypeException, UnknownIdentifierException {
|
||||
String moduleName = Types.extractArguments(arguments, String.class);
|
||||
|
||||
if (moduleName.equals(Builtins.MODULE_NAME)) {
|
||||
return scope.builtins.getModule();
|
||||
}
|
||||
Module module = scope.modules.get(moduleName);
|
||||
if (module == null) {
|
||||
var module = scope.getModule(moduleName);
|
||||
if (module.isEmpty()) {
|
||||
throw UnknownIdentifierException.create(moduleName);
|
||||
}
|
||||
|
||||
return module;
|
||||
return module.get();
|
||||
}
|
||||
|
||||
private static Module createModule(TopLevelScope scope, Object[] arguments, Context context)
|
||||
@ -169,14 +139,14 @@ public class TopLevelScope implements TruffleObject {
|
||||
QualifiedName qualName = QualifiedName.fromString(args.getFirst());
|
||||
File location = new File(args.getSecond());
|
||||
Module module = new Module(qualName, null, context.getTruffleFile(location));
|
||||
scope.modules.put(qualName.toString(), module);
|
||||
scope.packageRepository.registerModuleCreatedInRuntime(module);
|
||||
return module;
|
||||
}
|
||||
|
||||
private static Object unregisterModule(TopLevelScope scope, Object[] arguments, Context context)
|
||||
throws ArityException, UnsupportedTypeException {
|
||||
String name = Types.extractArguments(arguments, String.class);
|
||||
scope.modules.remove(name);
|
||||
scope.packageRepository.deregisterModule(name);
|
||||
return context.getNothing().newInstance();
|
||||
}
|
||||
|
||||
|
@ -4,11 +4,23 @@ import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.TruffleLogger;
|
||||
import com.oracle.truffle.api.instrumentation.EventBinding;
|
||||
import com.oracle.truffle.api.instrumentation.ExecutionEventListener;
|
||||
import com.oracle.truffle.api.interop.*;
|
||||
import com.oracle.truffle.api.interop.ArityException;
|
||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||
import com.oracle.truffle.api.interop.UnknownIdentifierException;
|
||||
import com.oracle.truffle.api.interop.UnsupportedMessageException;
|
||||
import com.oracle.truffle.api.interop.UnsupportedTypeException;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
import org.enso.compiler.context.ChangesetBuilder;
|
||||
import org.enso.interpreter.instrument.Endpoint;
|
||||
import org.enso.interpreter.instrument.IdExecutionInstrument;
|
||||
import org.enso.interpreter.instrument.MethodCallsCache;
|
||||
import org.enso.interpreter.instrument.NotificationHandler;
|
||||
import org.enso.interpreter.instrument.RuntimeCache;
|
||||
import org.enso.interpreter.instrument.UpdatesSynchronizationState;
|
||||
import org.enso.interpreter.instrument.execution.LocationFilter;
|
||||
@ -21,7 +33,12 @@ import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.error.PanicException;
|
||||
import org.enso.interpreter.runtime.scope.ModuleScope;
|
||||
import org.enso.interpreter.runtime.state.data.EmptyMap;
|
||||
import org.enso.interpreter.service.error.*;
|
||||
import org.enso.interpreter.service.error.ConstructorNotFoundException;
|
||||
import org.enso.interpreter.service.error.FailedToApplyEditsException;
|
||||
import org.enso.interpreter.service.error.MethodNotFoundException;
|
||||
import org.enso.interpreter.service.error.ModuleNotFoundException;
|
||||
import org.enso.interpreter.service.error.ModuleNotFoundForFileException;
|
||||
import org.enso.interpreter.service.error.SourceNotFoundException;
|
||||
import org.enso.polyglot.LanguageInfo;
|
||||
import org.enso.polyglot.MethodNames;
|
||||
import org.enso.text.buffer.Rope;
|
||||
@ -30,13 +47,6 @@ import org.enso.text.editing.JavaEditorAdapter;
|
||||
import org.enso.text.editing.TextEditor;
|
||||
import org.enso.text.editing.model;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* A service allowing externally-triggered code execution, registered by an instance of the
|
||||
* language.
|
||||
@ -44,6 +54,7 @@ import java.util.function.Consumer;
|
||||
public class ExecutionService {
|
||||
private final Context context;
|
||||
private final IdExecutionInstrument idExecutionInstrument;
|
||||
private final NotificationHandler.Forwarder notificationForwarder;
|
||||
private final InteropLibrary interopLibrary = InteropLibrary.getFactory().getUncached();
|
||||
private final TruffleLogger logger = TruffleLogger.getLogger(LanguageInfo.ID);
|
||||
|
||||
@ -54,9 +65,13 @@ public class ExecutionService {
|
||||
* @param idExecutionInstrument an instance of the {@link IdExecutionInstrument} to use in the
|
||||
* course of executions.
|
||||
*/
|
||||
public ExecutionService(Context context, IdExecutionInstrument idExecutionInstrument) {
|
||||
public ExecutionService(
|
||||
Context context,
|
||||
IdExecutionInstrument idExecutionInstrument,
|
||||
NotificationHandler.Forwarder notificationForwarder) {
|
||||
this.idExecutionInstrument = idExecutionInstrument;
|
||||
this.context = context;
|
||||
this.notificationForwarder = notificationForwarder;
|
||||
}
|
||||
|
||||
/** @return the language context. */
|
||||
@ -86,6 +101,11 @@ public class ExecutionService {
|
||||
function, EmptyMap.create(), new Object[] {atomConstructor.newInstance()});
|
||||
}
|
||||
|
||||
public void initializeLanguageServerConnection(Endpoint endpoint) {
|
||||
var notificationHandler = new NotificationHandler.InteractiveMode(endpoint);
|
||||
notificationForwarder.addListener(notificationHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a function with given arguments, represented as runtime language-level objects.
|
||||
*
|
||||
|
@ -23,7 +23,6 @@ import org.enso.syntax.text.Parser.IDMap
|
||||
import org.enso.syntax.text.{AST, Parser}
|
||||
|
||||
import java.io.StringReader
|
||||
import scala.jdk.CollectionConverters._
|
||||
import scala.jdk.OptionConverters._
|
||||
|
||||
/** This class encapsulates the static transformation processes that take place
|
||||
@ -330,12 +329,6 @@ class Compiler(
|
||||
def runErrorHandling(
|
||||
modules: List[Module]
|
||||
): Unit = {
|
||||
val shadowed = context.getShadowedPackages.asScala
|
||||
if (shadowed.nonEmpty) {
|
||||
context.getOut.println("Modules were shadowed during loading:")
|
||||
}
|
||||
shadowed.foreach(s => context.getOut.println(s.toString))
|
||||
|
||||
if (context.isStrictErrors) {
|
||||
val diagnostics = modules.map { module =>
|
||||
val errors = GatherDiagnostics
|
||||
|
@ -1,24 +1,65 @@
|
||||
package org.enso.compiler
|
||||
|
||||
import org.enso.interpreter.runtime.Context
|
||||
import com.oracle.truffle.api.TruffleFile
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import org.enso.distribution.DistributionManager
|
||||
import org.enso.editions.{LibraryName, LibraryVersion}
|
||||
import org.enso.interpreter.instrument.NotificationHandler
|
||||
import org.enso.interpreter.runtime.builtin.Builtins
|
||||
import org.enso.interpreter.runtime.util.TruffleFileSystem
|
||||
import org.enso.interpreter.runtime.{Context, Module}
|
||||
import org.enso.librarymanager.{ResolvedLibrary, ResolvingLibraryProvider}
|
||||
import org.enso.librarymanager.local.DefaultLocalLibraryProvider
|
||||
import org.enso.logger.masking.MaskedPath
|
||||
import org.enso.pkg.{Package, PackageManager, QualifiedName}
|
||||
|
||||
import scala.jdk.CollectionConverters._
|
||||
import java.nio.file.Path
|
||||
import scala.util.Try
|
||||
|
||||
/** Manages loaded packages and modules. */
|
||||
trait PackageRepository {
|
||||
|
||||
/** Informs the repository that it should populate the top scope with modules
|
||||
* belonging to a given package.
|
||||
*
|
||||
* @param namespace the namespace of the package.
|
||||
* @param name the package name.
|
||||
* @param libraryName the name of the library that should be loaded
|
||||
* @return `Right(())` if the package was already loaded or successfully
|
||||
* downloaded. A `Left` containing an error otherwise.
|
||||
*/
|
||||
def ensurePackageIsLoaded(
|
||||
namespace: String,
|
||||
name: String
|
||||
libraryName: LibraryName
|
||||
): Either[PackageRepository.Error, Unit]
|
||||
|
||||
/** Get a sequence of currently loaded packages. */
|
||||
def getLoadedPackages(): Seq[Package[TruffleFile]]
|
||||
|
||||
/** Get a sequence of currently loaded modules. */
|
||||
def getLoadedModules(): Seq[Module]
|
||||
|
||||
/** Get a loaded module by its qualified name. */
|
||||
def getLoadedModule(qualifiedName: String): Option[Module]
|
||||
|
||||
/** Register the main project package. */
|
||||
def registerMainProjectPackage(
|
||||
libraryName: LibraryName,
|
||||
pkg: Package[TruffleFile]
|
||||
): Unit
|
||||
|
||||
/** Register a single module, outside of any packages or part of an already
|
||||
* loaded package, that has been created manually during runtime.
|
||||
*/
|
||||
def registerModuleCreatedInRuntime(module: Module): Unit
|
||||
|
||||
/** Removes a module with the given name from the list of loaded modules. */
|
||||
def deregisterModule(qualifiedName: String): Unit
|
||||
|
||||
/** Modifies package and module names to reflect the project name change. */
|
||||
def renameProject(namespace: String, oldName: String, newName: String): Unit
|
||||
|
||||
/** This is a temporary workaround that should be removed once we get
|
||||
* integrated with the editions.
|
||||
*/
|
||||
def registerForPreload(packages: Seq[Package[TruffleFile]]): Unit
|
||||
}
|
||||
|
||||
object PackageRepository {
|
||||
@ -28,28 +69,298 @@ object PackageRepository {
|
||||
|
||||
object Error {
|
||||
|
||||
/** An error reported when the requested package does not exist.
|
||||
/** Indicates that a resolution error has happened, for example the package
|
||||
* was not defined in the selected edition.
|
||||
*/
|
||||
case object PackageDoesNotExist extends Error
|
||||
case class PackageCouldNotBeResolved(cause: Throwable) extends Error {
|
||||
override def toString: String =
|
||||
s"The package could not be resolved: ${cause.getMessage}"
|
||||
}
|
||||
|
||||
/** Indicates that the package was missing and a download was attempted, but
|
||||
* it failed - for example due to connectivity problems or just because the
|
||||
* package did not exist in the repository.
|
||||
*/
|
||||
case class PackageDownloadFailed(cause: Throwable) extends Error {
|
||||
override def toString: String =
|
||||
s"The package download has failed: ${cause.getMessage}"
|
||||
}
|
||||
|
||||
/** Indicates that the package was already present in the cache (or within
|
||||
* local packages), but it could not be loaded, possibly to a filesystem
|
||||
* error or insufficient permissions.
|
||||
*/
|
||||
case class PackageLoadingError(cause: String) extends Error {
|
||||
override def toString: String =
|
||||
s"The package could not be loaded: $cause"
|
||||
}
|
||||
}
|
||||
|
||||
/** A temporary package repository, only able to resolve packages known
|
||||
* upfront to the language context.
|
||||
/** The default [[PackageRepository]] implementation.
|
||||
*
|
||||
* @param libraryProvider the [[ResolvingLibraryProvider]] which resolves
|
||||
* which library version should be imported and
|
||||
* locates them (or downloads if they are missing)
|
||||
* @param context the language context
|
||||
* @param builtins the builtins module
|
||||
* @param notificationHandler a notification handler
|
||||
*/
|
||||
class Default(context: Context) extends PackageRepository {
|
||||
class Default(
|
||||
libraryProvider: ResolvingLibraryProvider,
|
||||
context: Context,
|
||||
builtins: Builtins,
|
||||
notificationHandler: NotificationHandler
|
||||
) extends PackageRepository {
|
||||
|
||||
private val logger = Logger[Default]
|
||||
|
||||
implicit private val fs: TruffleFileSystem = new TruffleFileSystem
|
||||
private val packageManager = new PackageManager[TruffleFile]
|
||||
|
||||
/** The mapping containing all loaded packages.
|
||||
*
|
||||
* It should be modified only from within synchronized sections, but it may
|
||||
* be always read. Thus elements should be added to this mapping only after
|
||||
* all library loading bookkeeping has been finished - so that if other,
|
||||
* unsynchronized threads read this map, every element it contains is
|
||||
* already fully processed.
|
||||
*/
|
||||
val loadedPackages
|
||||
: collection.concurrent.Map[LibraryName, Option[Package[TruffleFile]]] = {
|
||||
val builtinsName = LibraryName(Builtins.NAMESPACE, Builtins.PACKAGE_NAME)
|
||||
collection.concurrent.TrieMap(builtinsName -> None)
|
||||
}
|
||||
|
||||
/** The mapping containing loaded modules. */
|
||||
val loadedModules: collection.concurrent.Map[String, Module] =
|
||||
collection.concurrent.TrieMap(Builtins.MODULE_NAME -> builtins.getModule)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def registerMainProjectPackage(
|
||||
libraryName: LibraryName,
|
||||
pkg: Package[TruffleFile]
|
||||
): Unit = registerPackageInternal(
|
||||
libraryName = libraryName,
|
||||
pkg = pkg,
|
||||
libraryVersion = LibraryVersion.Local,
|
||||
isLibrary = false
|
||||
)
|
||||
|
||||
private def registerPackageInternal(
|
||||
libraryName: LibraryName,
|
||||
libraryVersion: LibraryVersion,
|
||||
pkg: Package[TruffleFile],
|
||||
isLibrary: Boolean
|
||||
): Unit = {
|
||||
val extensions = pkg.listPolyglotExtensions("java")
|
||||
extensions.foreach(context.getEnvironment.addToHostClassPath)
|
||||
|
||||
pkg.listSources
|
||||
.map { srcFile =>
|
||||
new Module(srcFile.qualifiedName, pkg, srcFile.file)
|
||||
}
|
||||
.foreach(registerModule)
|
||||
|
||||
if (isLibrary) {
|
||||
val root = Path.of(pkg.root.toString)
|
||||
notificationHandler.addedLibrary(libraryName, libraryVersion, root)
|
||||
}
|
||||
|
||||
loadedPackages.put(libraryName, Some(pkg))
|
||||
}
|
||||
|
||||
/** This package modifies the [[loadedPackages]], so it should be only
|
||||
* called from within synchronized sections.
|
||||
*/
|
||||
private def loadPackage(
|
||||
libraryName: LibraryName,
|
||||
libraryVersion: LibraryVersion,
|
||||
root: Path
|
||||
): Either[Error, Unit] = Try {
|
||||
logger.debug(
|
||||
s"Loading library $libraryName from " +
|
||||
s"[${MaskedPath(root).applyMasking()}]."
|
||||
)
|
||||
val rootFile = context.getEnvironment.getInternalTruffleFile(
|
||||
root.toAbsolutePath.normalize.toString
|
||||
)
|
||||
val pkg = packageManager.loadPackage(rootFile).get
|
||||
registerPackageInternal(
|
||||
libraryName = libraryName,
|
||||
libraryVersion = libraryVersion,
|
||||
pkg = pkg,
|
||||
isLibrary = true
|
||||
)
|
||||
}.toEither.left.map { error => Error.PackageLoadingError(error.getMessage) }
|
||||
|
||||
/** @inheritdoc */
|
||||
override def ensurePackageIsLoaded(
|
||||
libraryName: LibraryName
|
||||
): Either[Error, Unit] = {
|
||||
runPreload()
|
||||
if (loadedPackages.contains(libraryName)) Right(())
|
||||
else {
|
||||
logger.trace(s"Resolving library $libraryName.")
|
||||
val resolvedLibrary = libraryProvider.findLibrary(libraryName)
|
||||
this.synchronized {
|
||||
// We check again inside of the monitor, in case that some other
|
||||
// thread has just added this library.
|
||||
if (loadedPackages.contains(libraryName)) Right(())
|
||||
else
|
||||
resolvedLibrary
|
||||
.flatMap { library =>
|
||||
loadPackage(library.name, library.version, library.location)
|
||||
}
|
||||
.left
|
||||
.map {
|
||||
case ResolvingLibraryProvider.Error.NotResolved(details) =>
|
||||
Error.PackageCouldNotBeResolved(details)
|
||||
case ResolvingLibraryProvider.Error.DownloadFailed(reason) =>
|
||||
Error.PackageDownloadFailed(reason)
|
||||
case ResolvingLibraryProvider.Error.RequestedLocalLibraryDoesNotExist =>
|
||||
Error.PackageLoadingError(
|
||||
"The local library has not been found on the local " +
|
||||
"libraries search paths."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def getLoadedModules(): Seq[Module] = {
|
||||
runPreload()
|
||||
loadedModules.values.toSeq
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def getLoadedPackages(): Seq[Package[TruffleFile]] = {
|
||||
runPreload()
|
||||
loadedPackages.values.toSeq.flatten
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def getLoadedModule(qualifiedName: String): Option[Module] = {
|
||||
runPreload()
|
||||
loadedModules.get(qualifiedName)
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def registerModuleCreatedInRuntime(module: Module): Unit =
|
||||
registerModule(module)
|
||||
|
||||
private def registerModule(module: Module): Unit =
|
||||
loadedModules.put(module.getName.toString, module)
|
||||
|
||||
override def deregisterModule(qualifiedName: String): Unit =
|
||||
loadedModules.remove(qualifiedName)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def renameProject(
|
||||
namespace: String,
|
||||
name: String
|
||||
): Either[PackageRepository.Error, Unit] =
|
||||
if (
|
||||
(name == Builtins.PACKAGE_NAME && namespace == Builtins.NAMESPACE) ||
|
||||
context.getPackages.asScala
|
||||
.exists(p => p.name == name && p.namespace == namespace)
|
||||
) Right(())
|
||||
else Left(Error.PackageDoesNotExist)
|
||||
oldName: String,
|
||||
newName: String
|
||||
): Unit = this.synchronized {
|
||||
renamePackages(namespace, oldName, newName)
|
||||
renameModules(namespace, oldName, newName)
|
||||
}
|
||||
|
||||
private def renamePackages(
|
||||
namespace: String,
|
||||
oldName: String,
|
||||
newName: String
|
||||
): Unit = {
|
||||
val toChange = loadedPackages.toSeq.filter { case (name, _) =>
|
||||
name.namespace == namespace && name.name == oldName
|
||||
}
|
||||
|
||||
for ((key, _) <- toChange) {
|
||||
loadedPackages.remove(key)
|
||||
}
|
||||
|
||||
for ((key, pkgOption) <- toChange) {
|
||||
val newPkg = pkgOption.map(_.setPackageName(newName))
|
||||
val newKey = key.copy(name = newName)
|
||||
loadedPackages.put(newKey, newPkg)
|
||||
}
|
||||
}
|
||||
|
||||
private def renameModules(
|
||||
namespace: String,
|
||||
oldName: String,
|
||||
newName: String
|
||||
): Unit = {
|
||||
val separator: String = QualifiedName.separator
|
||||
val keys = loadedModules.keySet.filter(name =>
|
||||
name.startsWith(namespace + separator + oldName + separator)
|
||||
)
|
||||
|
||||
for {
|
||||
key <- keys
|
||||
module <- loadedModules.remove(key)
|
||||
} {
|
||||
module.renameProject(newName)
|
||||
loadedModules.put(module.getName.toString, module)
|
||||
}
|
||||
}
|
||||
|
||||
/** Temporary workaround, will be removed once editions are integrated. */
|
||||
private var toPreload: List[Package[TruffleFile]] = Nil
|
||||
private def runPreload(): Unit = {
|
||||
for (pkg <- toPreload) {
|
||||
registerPackageInternal(
|
||||
libraryName = pkg.libraryName,
|
||||
pkg = pkg,
|
||||
libraryVersion = LibraryVersion.Local,
|
||||
isLibrary = true
|
||||
)
|
||||
}
|
||||
toPreload = Nil
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def registerForPreload(packages: Seq[Package[TruffleFile]]): Unit =
|
||||
toPreload ++= packages
|
||||
}
|
||||
|
||||
/** A temporary [[ResolvingLibraryProvider]] that ignores the edition and just
|
||||
* provides the local libraries.
|
||||
*
|
||||
* TODO [RW] it should be removed once the editions are integrated
|
||||
*/
|
||||
private class TemporaryLocalProvider(searchPaths: List[Path])
|
||||
extends ResolvingLibraryProvider {
|
||||
|
||||
private val localRepo = new DefaultLocalLibraryProvider(searchPaths)
|
||||
|
||||
override def findLibrary(
|
||||
name: LibraryName
|
||||
): Either[ResolvingLibraryProvider.Error, ResolvedLibrary] =
|
||||
localRepo
|
||||
.findLibrary(name)
|
||||
.map(ResolvedLibrary(name, LibraryVersion.Local, _))
|
||||
.toRight {
|
||||
ResolvingLibraryProvider.Error.RequestedLocalLibraryDoesNotExist
|
||||
}
|
||||
}
|
||||
|
||||
/** A temporary helper constructor for [[PackageRepository]] that does not
|
||||
* need any edition configuration.
|
||||
*
|
||||
* TODO [RW] it should be removed once the editions are integrated
|
||||
*/
|
||||
def makeLegacyRepository(
|
||||
distributionManager: DistributionManager,
|
||||
context: Context,
|
||||
builtins: Builtins,
|
||||
notificationHandler: NotificationHandler
|
||||
): PackageRepository = {
|
||||
val searchPaths = distributionManager.paths.localLibrariesSearchPaths.toList
|
||||
new Default(
|
||||
new TemporaryLocalProvider(searchPaths),
|
||||
context,
|
||||
builtins,
|
||||
notificationHandler
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -6631,12 +6631,15 @@ object IR {
|
||||
" but the module does not belong to a project."
|
||||
}
|
||||
|
||||
/** Used when an import statement triggers loading of a package that does not exist.
|
||||
/** Used when an import statement triggers loading of a package that could
|
||||
* not be loaded.
|
||||
*
|
||||
* @param name the module name.
|
||||
*/
|
||||
case class PackageDoesNotExist(name: String) extends Reason {
|
||||
case class PackageCouldNotBeLoaded(name: String, reason: String)
|
||||
extends Reason {
|
||||
override def message: String = s"Package containing the module $name" +
|
||||
s" could not be found."
|
||||
s" could not be loaded: $reason"
|
||||
}
|
||||
|
||||
/** Used when an import statement refers to a module that does not exist.
|
||||
|
@ -3,7 +3,9 @@ package org.enso.compiler.phase
|
||||
import org.enso.compiler.Compiler
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.data.BindingsMap
|
||||
import org.enso.compiler.exception.CompilerError
|
||||
import org.enso.compiler.pass.analyse.BindingAnalysis
|
||||
import org.enso.editions.LibraryName
|
||||
import org.enso.interpreter.runtime.Module
|
||||
|
||||
import scala.collection.mutable
|
||||
@ -60,38 +62,44 @@ class ImportResolver(compiler: Compiler) {
|
||||
val exp = ir.exports
|
||||
.collect { case ex: IR.Module.Scope.Export.Module => ex }
|
||||
.find(_.name.name == impName)
|
||||
val isLoaded = imp.name.parts match {
|
||||
val libraryName = imp.name.parts match {
|
||||
case namespace :: name :: _ =>
|
||||
compiler.packageRepository.ensurePackageIsLoaded(
|
||||
namespace.name,
|
||||
name.name
|
||||
).isRight
|
||||
case _ => false
|
||||
LibraryName(namespace.name, name.name)
|
||||
case _ =>
|
||||
throw new CompilerError(
|
||||
"Imports should containt at least two segments after " +
|
||||
"desugaring."
|
||||
)
|
||||
}
|
||||
if (isLoaded) {
|
||||
compiler.getModule(impName) match {
|
||||
case Some(module) =>
|
||||
(
|
||||
imp,
|
||||
Some(BindingsMap.ResolvedImport(imp, exp, module))
|
||||
)
|
||||
case None =>
|
||||
(
|
||||
IR.Error.ImportExport(
|
||||
compiler.packageRepository
|
||||
.ensurePackageIsLoaded(libraryName) match {
|
||||
case Right(()) =>
|
||||
compiler.getModule(impName) match {
|
||||
case Some(module) =>
|
||||
(
|
||||
imp,
|
||||
IR.Error.ImportExport.ModuleDoesNotExist(impName)
|
||||
),
|
||||
None
|
||||
)
|
||||
}
|
||||
} else {
|
||||
(
|
||||
IR.Error.ImportExport(
|
||||
imp,
|
||||
IR.Error.ImportExport.PackageDoesNotExist(impName)
|
||||
),
|
||||
None
|
||||
)
|
||||
Some(BindingsMap.ResolvedImport(imp, exp, module))
|
||||
)
|
||||
case None =>
|
||||
(
|
||||
IR.Error.ImportExport(
|
||||
imp,
|
||||
IR.Error.ImportExport.ModuleDoesNotExist(impName)
|
||||
),
|
||||
None
|
||||
)
|
||||
}
|
||||
case Left(loadingError) =>
|
||||
(
|
||||
IR.Error.ImportExport(
|
||||
imp,
|
||||
IR.Error.ImportExport.PackageCouldNotBeLoaded(
|
||||
impName,
|
||||
loadingError.toString
|
||||
)
|
||||
),
|
||||
None
|
||||
)
|
||||
}
|
||||
case other => (other, None)
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
package org.enso.interpreter.instrument
|
||||
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
import com.oracle.truffle.api.TruffleContext
|
||||
import org.enso.interpreter.instrument.command.CommandFactory
|
||||
import org.enso.interpreter.instrument.execution.{
|
||||
@ -12,6 +10,8 @@ import org.enso.interpreter.service.ExecutionService
|
||||
import org.enso.polyglot.runtime.Runtime.Api
|
||||
import org.graalvm.polyglot.io.MessageEndpoint
|
||||
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
/** A message endpoint implementation used by the
|
||||
* [[org.enso.interpreter.instrument.RuntimeServerInstrument]].
|
||||
*/
|
||||
@ -75,6 +75,7 @@ final class Handler {
|
||||
truffleContext
|
||||
)
|
||||
commandProcessor = new CommandExecutionEngine(interpreterCtx)
|
||||
executionService.initializeLanguageServerConnection(endpoint)
|
||||
endpoint.sendToClient(Api.Response(Api.InitializedNotification()))
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,111 @@
|
||||
package org.enso.interpreter.instrument
|
||||
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import org.enso.cli.ProgressBar
|
||||
import org.enso.cli.task.{ProgressReporter, TaskProgress}
|
||||
import org.enso.editions.{LibraryName, LibraryVersion}
|
||||
import org.enso.polyglot.runtime.Runtime.{Api, ApiResponse}
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
/** A class that forwards notifications about loaded libraries and long-running
|
||||
* tasks to the user interface.
|
||||
*/
|
||||
trait NotificationHandler extends ProgressReporter {
|
||||
|
||||
/** Called when a library has been loaded.
|
||||
*
|
||||
* @param libraryName name of the added library
|
||||
* @param libraryVersion selected version
|
||||
* @param location path to the location from which the library is loaded
|
||||
*/
|
||||
def addedLibrary(
|
||||
libraryName: LibraryName,
|
||||
libraryVersion: LibraryVersion,
|
||||
location: Path
|
||||
): Unit
|
||||
}
|
||||
|
||||
object NotificationHandler {
|
||||
|
||||
/** A [[NotificationHandler]] for text mode.
|
||||
*
|
||||
* It ignores library notifications and displays progress as a progress bar
|
||||
* in the CLI, as long as the stdout is connected to a terminal.
|
||||
*/
|
||||
object TextMode extends NotificationHandler {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def addedLibrary(
|
||||
libraryName: LibraryName,
|
||||
libraryVersion: LibraryVersion,
|
||||
location: Path
|
||||
): Unit = {
|
||||
// Library notifications are deliberately ignored in text mode.
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def trackProgress(message: String, task: TaskProgress[_]): Unit = {
|
||||
Logger[TextMode.type].info(message)
|
||||
if (System.console() != null) {
|
||||
ProgressBar.waitWithProgress(task)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** A [[NotificationHandler]] that forwards messages to other
|
||||
* NotificationHandlers.
|
||||
*/
|
||||
class Forwarder extends NotificationHandler {
|
||||
private var listeners: List[NotificationHandler] = Nil
|
||||
|
||||
/** @inheritdoc */
|
||||
override def addedLibrary(
|
||||
libraryName: LibraryName,
|
||||
libraryVersion: LibraryVersion,
|
||||
location: Path
|
||||
): Unit = for (listener <- listeners)
|
||||
listener.addedLibrary(libraryName, libraryVersion, location)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def trackProgress(message: String, task: TaskProgress[_]): Unit =
|
||||
for (listener <- listeners) listener.trackProgress(message, task)
|
||||
|
||||
/** Registers a new listener. */
|
||||
def addListener(listener: NotificationHandler): Unit =
|
||||
listeners ::= listener
|
||||
}
|
||||
|
||||
/** A [[NotificationHandler]] for interactive mode, which forwards the
|
||||
* notifications to the Language Server, which then should forward them to
|
||||
* the IDE.
|
||||
*/
|
||||
class InteractiveMode(endpoint: Endpoint) extends NotificationHandler {
|
||||
private val logger = Logger[InteractiveMode]
|
||||
|
||||
private def sendMessage(message: ApiResponse): Unit = {
|
||||
val response = Api.Response(None, message)
|
||||
endpoint.sendToClient(response)
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def addedLibrary(
|
||||
libraryName: LibraryName,
|
||||
libraryVersion: LibraryVersion,
|
||||
location: Path
|
||||
): Unit = sendMessage(
|
||||
Api.LibraryLoaded(
|
||||
namespace = libraryName.namespace,
|
||||
name = libraryName.name,
|
||||
version = libraryVersion.toString,
|
||||
location = location.toFile
|
||||
)
|
||||
)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def trackProgress(message: String, task: TaskProgress[_]): Unit = {
|
||||
logger.info(message)
|
||||
// TODO [RW] this should be implemented once progress tracking is used by downloads
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package org.enso.interpreter.runtime
|
||||
|
||||
import org.enso.distribution.{DistributionManager, Environment}
|
||||
|
||||
/** A [[DistributionManager]] for the runtime.
|
||||
*
|
||||
* It is used to resolve paths to library cache etc.
|
||||
*/
|
||||
object RuntimeDistributionManager
|
||||
extends DistributionManager(new Environment {})
|
@ -21,7 +21,7 @@ trait PackageTest extends AnyFlatSpec with Matchers with ValueEquality {
|
||||
.newBuilder(LanguageInfo.ID)
|
||||
.allowExperimentalOptions(true)
|
||||
.allowAllAccess(true)
|
||||
.option(RuntimeOptions.PACKAGES_PATH, pkgPath.getAbsolutePath)
|
||||
.option(RuntimeOptions.PROJECT_ROOT, pkgPath.getAbsolutePath)
|
||||
.option(RuntimeOptions.STRICT_ERRORS, "true")
|
||||
.out(output)
|
||||
.in(System.in)
|
||||
|
@ -56,12 +56,13 @@ class RuntimeErrorsTest
|
||||
.newBuilder(LanguageInfo.ID)
|
||||
.allowExperimentalOptions(true)
|
||||
.allowAllAccess(true)
|
||||
.option(RuntimeOptions.PACKAGES_PATH, pkg.root.getAbsolutePath)
|
||||
.option(RuntimeOptions.PROJECT_ROOT, pkg.root.getAbsolutePath)
|
||||
.option(RuntimeOptions.LOG_LEVEL, "WARNING")
|
||||
.option(RuntimeOptions.INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION, "true")
|
||||
.option(RuntimeOptions.ENABLE_PROJECT_SUGGESTIONS, "false")
|
||||
.option(RuntimeOptions.ENABLE_GLOBAL_SUGGESTIONS, "false")
|
||||
.option(RuntimeServerInfo.ENABLE_OPTION, "true")
|
||||
.option(RuntimeOptions.INTERACTIVE_MODE, "true")
|
||||
.out(out)
|
||||
.serverTransport { (uri, peer) =>
|
||||
if (uri.toString == RuntimeServerInfo.URI) {
|
||||
|
@ -50,12 +50,13 @@ class RuntimeInstrumentTest
|
||||
.newBuilder(LanguageInfo.ID)
|
||||
.allowExperimentalOptions(true)
|
||||
.allowAllAccess(true)
|
||||
.option(RuntimeOptions.PACKAGES_PATH, pkg.root.getAbsolutePath)
|
||||
.option(RuntimeOptions.PROJECT_ROOT, pkg.root.getAbsolutePath)
|
||||
.option(RuntimeOptions.LOG_LEVEL, "WARNING")
|
||||
.option(RuntimeOptions.INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION, "true")
|
||||
.option(RuntimeOptions.ENABLE_PROJECT_SUGGESTIONS, "false")
|
||||
.option(RuntimeOptions.ENABLE_GLOBAL_SUGGESTIONS, "false")
|
||||
.option(RuntimeServerInfo.ENABLE_OPTION, "true")
|
||||
.option(RuntimeOptions.INTERACTIVE_MODE, "true")
|
||||
.out(out)
|
||||
.serverTransport { (uri, peer) =>
|
||||
if (uri.toString == RuntimeServerInfo.URI) {
|
||||
|
@ -54,12 +54,13 @@ class RuntimeServerTest
|
||||
.newBuilder(LanguageInfo.ID)
|
||||
.allowExperimentalOptions(true)
|
||||
.allowAllAccess(true)
|
||||
.option(RuntimeOptions.PACKAGES_PATH, pkg.root.getAbsolutePath)
|
||||
.option(RuntimeOptions.PROJECT_ROOT, pkg.root.getAbsolutePath)
|
||||
.option(RuntimeOptions.LOG_LEVEL, "WARNING")
|
||||
.option(RuntimeOptions.INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION, "true")
|
||||
.option(RuntimeOptions.ENABLE_PROJECT_SUGGESTIONS, "false")
|
||||
.option(RuntimeOptions.ENABLE_GLOBAL_SUGGESTIONS, "false")
|
||||
.option(RuntimeServerInfo.ENABLE_OPTION, "true")
|
||||
.option(RuntimeOptions.INTERACTIVE_MODE, "true")
|
||||
.logHandler(logOut)
|
||||
.out(out)
|
||||
.serverTransport { (uri, peer) =>
|
||||
|
@ -1,11 +1,5 @@
|
||||
package org.enso.interpreter.test.instrument
|
||||
|
||||
import java.io.{ByteArrayOutputStream, File}
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.file.{Files, Paths}
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.{LinkedBlockingQueue, TimeUnit}
|
||||
|
||||
import org.enso.interpreter.test.Metadata
|
||||
import org.enso.pkg.{Package, PackageManager}
|
||||
import org.enso.polyglot._
|
||||
@ -13,19 +7,25 @@ import org.enso.polyglot.runtime.Runtime.Api
|
||||
import org.enso.testkit.OsSpec
|
||||
import org.graalvm.polyglot.Context
|
||||
import org.graalvm.polyglot.io.MessageEndpoint
|
||||
import org.scalatest.BeforeAndAfterEach
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach}
|
||||
|
||||
import java.io.{ByteArrayOutputStream, File}
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.file.{Files, Paths}
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.{LinkedBlockingQueue, TimeUnit}
|
||||
|
||||
@scala.annotation.nowarn("msg=multiarg infix syntax")
|
||||
class RuntimeStdlibTest
|
||||
extends AnyFlatSpec
|
||||
with Matchers
|
||||
with BeforeAndAfterEach
|
||||
with BeforeAndAfterAll
|
||||
with OsSpec {
|
||||
|
||||
final val ContextPathSeparator: String =
|
||||
if (isWindows) ";" else ":"
|
||||
final val ContextPathSeparator: String = File.pathSeparator
|
||||
|
||||
var context: TestContext = _
|
||||
|
||||
@ -47,13 +47,12 @@ class RuntimeStdlibTest
|
||||
.newBuilder(LanguageInfo.ID)
|
||||
.allowExperimentalOptions(true)
|
||||
.allowAllAccess(true)
|
||||
.option(
|
||||
RuntimeOptions.PACKAGES_PATH,
|
||||
toPackagesPath(pkg.root.getAbsolutePath, stdlib.toString)
|
||||
)
|
||||
.option(RuntimeOptions.PROJECT_ROOT, pkg.root.getAbsolutePath)
|
||||
.option(RuntimeOptions.PRELOADED_PACKAGES_PATHS, stdlib.toString)
|
||||
.option(RuntimeOptions.LOG_LEVEL, "WARNING")
|
||||
.option(RuntimeOptions.INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION, "true")
|
||||
.option(RuntimeServerInfo.ENABLE_OPTION, "true")
|
||||
.option(RuntimeOptions.INTERACTIVE_MODE, "true")
|
||||
.out(out)
|
||||
.serverTransport { (uri, peer) =>
|
||||
if (uri.toString == RuntimeServerInfo.URI) {
|
||||
@ -77,7 +76,7 @@ class RuntimeStdlibTest
|
||||
executionContext.context.initialize(LanguageInfo.ID)
|
||||
|
||||
def toPackagesPath(paths: String*): String =
|
||||
paths.mkString(ContextPathSeparator)
|
||||
paths.mkString(File.pathSeparator)
|
||||
|
||||
def writeMain(contents: String): File =
|
||||
Files.write(pkg.mainFile.toPath, contents.getBytes).toFile
|
||||
@ -92,7 +91,7 @@ class RuntimeStdlibTest
|
||||
|
||||
def send(msg: Api.Request): Unit = endPoint.sendBinary(Api.serialize(msg))
|
||||
|
||||
def receiveNone: Option[Api.Response] = {
|
||||
def receiveOne: Option[Api.Response] = {
|
||||
Option(messageQueue.poll())
|
||||
}
|
||||
|
||||
@ -165,7 +164,7 @@ class RuntimeStdlibTest
|
||||
context.send(
|
||||
Api.Request(Api.OpenFileNotification(mainFile, contents, true))
|
||||
)
|
||||
context.receiveNone shouldEqual None
|
||||
context.receiveOne shouldEqual None
|
||||
|
||||
// push main
|
||||
context.send(
|
||||
@ -181,16 +180,17 @@ class RuntimeStdlibTest
|
||||
)
|
||||
)
|
||||
)
|
||||
val response =
|
||||
val responses =
|
||||
context.receiveAllUntil(
|
||||
context.executionComplete(contextId),
|
||||
timeout = 60
|
||||
)
|
||||
response should contain allOf (
|
||||
responses should contain allOf (
|
||||
Api.Response(requestId, Api.PushContextResponse(contextId)),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
val suggestions = response.collect {
|
||||
|
||||
val suggestions = responses.collect {
|
||||
case Api.Response(
|
||||
None,
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(_, _, as, xs)
|
||||
@ -199,7 +199,7 @@ class RuntimeStdlibTest
|
||||
}
|
||||
suggestions.isEmpty shouldBe false
|
||||
|
||||
val builtinsSuggestions = response.collect {
|
||||
val builtinsSuggestions = responses.collect {
|
||||
case Api.Response(
|
||||
None,
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(file, _, as, xs)
|
||||
@ -208,6 +208,16 @@ class RuntimeStdlibTest
|
||||
}
|
||||
builtinsSuggestions.length shouldBe 1
|
||||
|
||||
val contentRootNotifications = responses.collect {
|
||||
case Api.Response(
|
||||
None,
|
||||
Api.LibraryLoaded(namespace, name, version, _)
|
||||
) =>
|
||||
(namespace, name, version)
|
||||
}
|
||||
|
||||
contentRootNotifications should contain(("Standard", "Base", "local"))
|
||||
|
||||
context.consumeOut shouldEqual List("Hello World!")
|
||||
}
|
||||
|
||||
|
@ -43,11 +43,12 @@ class RuntimeSuggestionUpdatesTest
|
||||
.newBuilder(LanguageInfo.ID)
|
||||
.allowExperimentalOptions(true)
|
||||
.allowAllAccess(true)
|
||||
.option(RuntimeOptions.PACKAGES_PATH, pkg.root.getAbsolutePath)
|
||||
.option(RuntimeOptions.PROJECT_ROOT, pkg.root.getAbsolutePath)
|
||||
.option(RuntimeOptions.LOG_LEVEL, "WARNING")
|
||||
.option(RuntimeOptions.INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION, "true")
|
||||
.option(RuntimeOptions.ENABLE_GLOBAL_SUGGESTIONS, "false")
|
||||
.option(RuntimeServerInfo.ENABLE_OPTION, "true")
|
||||
.option(RuntimeOptions.INTERACTIVE_MODE, "true")
|
||||
.out(out)
|
||||
.serverTransport { (uri, peer) =>
|
||||
if (uri.toString == RuntimeServerInfo.URI) {
|
||||
@ -184,7 +185,13 @@ class RuntimeSuggestionUpdatesTest
|
||||
"main",
|
||||
List(
|
||||
Suggestion
|
||||
.Argument("this", "Enso_Test.Test.Main", false, false, None)
|
||||
.Argument(
|
||||
"this",
|
||||
"Enso_Test.Test.Main",
|
||||
false,
|
||||
false,
|
||||
None
|
||||
)
|
||||
),
|
||||
"Enso_Test.Test.Main",
|
||||
Constants.ANY,
|
||||
@ -240,7 +247,13 @@ class RuntimeSuggestionUpdatesTest
|
||||
"main",
|
||||
List(
|
||||
Suggestion
|
||||
.Argument("this", "Enso_Test.Test.Main", false, false, None)
|
||||
.Argument(
|
||||
"this",
|
||||
"Enso_Test.Test.Main",
|
||||
false,
|
||||
false,
|
||||
None
|
||||
)
|
||||
),
|
||||
"Enso_Test.Test.Main",
|
||||
Constants.ANY,
|
||||
@ -318,7 +331,13 @@ class RuntimeSuggestionUpdatesTest
|
||||
"main",
|
||||
List(
|
||||
Suggestion
|
||||
.Argument("this", "Enso_Test.Test.Main", false, false, None)
|
||||
.Argument(
|
||||
"this",
|
||||
"Enso_Test.Test.Main",
|
||||
false,
|
||||
false,
|
||||
None
|
||||
)
|
||||
),
|
||||
"Enso_Test.Test.Main",
|
||||
Constants.ANY,
|
||||
@ -416,7 +435,13 @@ class RuntimeSuggestionUpdatesTest
|
||||
"main",
|
||||
List(
|
||||
Suggestion
|
||||
.Argument("this", "Enso_Test.Test.Main", false, false, None)
|
||||
.Argument(
|
||||
"this",
|
||||
"Enso_Test.Test.Main",
|
||||
false,
|
||||
false,
|
||||
None
|
||||
)
|
||||
),
|
||||
"Enso_Test.Test.Main",
|
||||
Constants.ANY,
|
||||
@ -524,7 +549,13 @@ class RuntimeSuggestionUpdatesTest
|
||||
"main",
|
||||
List(
|
||||
Suggestion
|
||||
.Argument("this", "Enso_Test.Test.Main", false, false, None)
|
||||
.Argument(
|
||||
"this",
|
||||
"Enso_Test.Test.Main",
|
||||
false,
|
||||
false,
|
||||
None
|
||||
)
|
||||
),
|
||||
"Enso_Test.Test.Main",
|
||||
Constants.ANY,
|
||||
@ -590,7 +621,13 @@ class RuntimeSuggestionUpdatesTest
|
||||
"foo",
|
||||
List(
|
||||
Suggestion
|
||||
.Argument("this", "Enso_Test.Test.Main", false, false, None),
|
||||
.Argument(
|
||||
"this",
|
||||
"Enso_Test.Test.Main",
|
||||
false,
|
||||
false,
|
||||
None
|
||||
),
|
||||
Suggestion
|
||||
.Argument("x", Constants.ANY, false, false, None)
|
||||
),
|
||||
@ -652,7 +689,13 @@ class RuntimeSuggestionUpdatesTest
|
||||
"foo",
|
||||
List(
|
||||
Suggestion
|
||||
.Argument("this", "Enso_Test.Test.Main", false, false, None),
|
||||
.Argument(
|
||||
"this",
|
||||
"Enso_Test.Test.Main",
|
||||
false,
|
||||
false,
|
||||
None
|
||||
),
|
||||
Suggestion
|
||||
.Argument("x", Constants.ANY, false, false, None)
|
||||
),
|
||||
@ -768,7 +811,13 @@ class RuntimeSuggestionUpdatesTest
|
||||
"main",
|
||||
Seq(
|
||||
Suggestion
|
||||
.Argument("this", "Enso_Test.Test.Main", false, false, None)
|
||||
.Argument(
|
||||
"this",
|
||||
"Enso_Test.Test.Main",
|
||||
false,
|
||||
false,
|
||||
None
|
||||
)
|
||||
),
|
||||
"Enso_Test.Test.Main",
|
||||
Constants.ANY,
|
||||
|
@ -53,12 +53,13 @@ class RuntimeVisualisationsTest
|
||||
.newBuilder(LanguageInfo.ID)
|
||||
.allowExperimentalOptions(true)
|
||||
.allowAllAccess(true)
|
||||
.option(RuntimeOptions.PACKAGES_PATH, pkg.root.getAbsolutePath)
|
||||
.option(RuntimeOptions.PROJECT_ROOT, pkg.root.getAbsolutePath)
|
||||
.option(RuntimeOptions.LOG_LEVEL, "WARNING")
|
||||
.option(RuntimeOptions.INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION, "true")
|
||||
.option(RuntimeOptions.ENABLE_PROJECT_SUGGESTIONS, "false")
|
||||
.option(RuntimeOptions.ENABLE_GLOBAL_SUGGESTIONS, "false")
|
||||
.option(RuntimeServerInfo.ENABLE_OPTION, "true")
|
||||
.option(RuntimeOptions.INTERACTIVE_MODE, "true")
|
||||
.logHandler(logOut)
|
||||
.out(out)
|
||||
.serverTransport { (uri, peer) =>
|
||||
@ -180,7 +181,13 @@ class RuntimeVisualisationsTest
|
||||
Api.ExpressionUpdate(
|
||||
Main.idMainY,
|
||||
Some(Constants.INTEGER),
|
||||
Some(Api.MethodPointer("Enso_Test.Test.Main", Constants.NUMBER, "foo")),
|
||||
Some(
|
||||
Api.MethodPointer(
|
||||
"Enso_Test.Test.Main",
|
||||
Constants.NUMBER,
|
||||
"foo"
|
||||
)
|
||||
),
|
||||
Vector(Api.ProfilingInfo.ExecutionTime(0)),
|
||||
fromCache,
|
||||
Api.ExpressionUpdate.Payload.Value()
|
||||
|
@ -23,8 +23,9 @@ class ImportsTest extends PackageTest {
|
||||
) should have message "Compilation aborted due to errors."
|
||||
val outLines = consumeOut
|
||||
outLines(2) should include(
|
||||
"Package containing the module Surely_This.Does_Not_Exist.My_Module could" +
|
||||
" not be found."
|
||||
"Package containing the module Surely_This.Does_Not_Exist.My_Module " +
|
||||
"could not be loaded: The package could not be loaded: The local " +
|
||||
"library has not been found on the local libraries search paths."
|
||||
)
|
||||
outLines(3) should include(
|
||||
"The module Enso_Test.Test_Bad_Imports.Oopsie does not exist."
|
||||
|
@ -2,7 +2,9 @@ package org.enso.distribution
|
||||
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import org.enso.distribution.FileSystem.PathSyntax
|
||||
import org.enso.logger.masking.{MaskedPath, ToLogString}
|
||||
|
||||
import java.io.File
|
||||
import java.nio.file.{Files, Path}
|
||||
import scala.util.Try
|
||||
import scala.util.control.NonFatal
|
||||
@ -19,6 +21,7 @@ import scala.util.control.NonFatal
|
||||
* @param bundle optional bundle description, containing secondary engine and
|
||||
* runtime directories
|
||||
* @param config location of configuration
|
||||
* @param runRoot root for directories that store runtime files, like lockfiles
|
||||
* @param locks a directory for storing lockfiles that are used to synchronize
|
||||
* access to the various components
|
||||
* @param logs a directory for storing logs
|
||||
@ -36,6 +39,7 @@ case class DistributionPaths(
|
||||
engines: Path,
|
||||
bundle: Option[Bundle],
|
||||
config: Path,
|
||||
runRoot: Path,
|
||||
locks: Path,
|
||||
logs: Path,
|
||||
unsafeTemporaryDirectory: Path,
|
||||
@ -47,16 +51,23 @@ case class DistributionPaths(
|
||||
/** @inheritdoc */
|
||||
override def toString: String =
|
||||
s"""DistributionPaths(
|
||||
| dataRoot = $dataRoot,
|
||||
| runtimes = $runtimes,
|
||||
| engines = $engines,
|
||||
| bundle = $bundle,
|
||||
| config = $config,
|
||||
| locks = $locks,
|
||||
| tmp = $unsafeTemporaryDirectory,
|
||||
| ensoHome = $ensoHome
|
||||
| dataRoot = ${mask(dataRoot)},
|
||||
| runtimes = ${mask(runtimes)},
|
||||
| engines = ${mask(engines)},
|
||||
| bundle = ${bundle.map(_.applyMasking())},
|
||||
| config = ${mask(config)},
|
||||
| locks = ${mask(locks)},
|
||||
| logs = ${mask(logs)},
|
||||
| tmp = ${mask(unsafeTemporaryDirectory)},
|
||||
| ensoHome = ${mask(ensoHome)},
|
||||
| customEditions = ${mask(customEditions)},
|
||||
| localLibrariesSearchpaths = ${mask(localLibrariesSearchPaths)}
|
||||
|)""".stripMargin
|
||||
|
||||
private def mask(path: Path): String = MaskedPath(path).applyMasking()
|
||||
private def mask(paths: Seq[Path]): String =
|
||||
paths.map(p => MaskedPath(p).applyMasking()).toString()
|
||||
|
||||
/** Sequence of paths to search for engine installations, in order of
|
||||
* precedence.
|
||||
*/
|
||||
@ -91,7 +102,11 @@ case class DistributionPaths(
|
||||
* For portable distributions, bundled packages are already included in the
|
||||
* primary directory.
|
||||
*/
|
||||
case class Bundle(engines: Path, runtimes: Path)
|
||||
case class Bundle(engines: Path, runtimes: Path) extends ToLogString {
|
||||
override def toLogString(shouldMask: Boolean): String =
|
||||
s"Bundle(engines = ${MaskedPath(engines).toLogString(shouldMask)}, " +
|
||||
s"runtimes = ${MaskedPath(runtimes).toLogString(shouldMask)})"
|
||||
}
|
||||
|
||||
/** A helper class that encapsulates management of paths to components of the
|
||||
* distribution.
|
||||
@ -123,6 +138,7 @@ class DistributionManager(val env: Environment) {
|
||||
engines = dataRoot / ENGINES_DIRECTORY,
|
||||
bundle = detectBundle(),
|
||||
config = configRoot,
|
||||
runRoot = runRoot,
|
||||
locks = runRoot / LOCK_DIRECTORY,
|
||||
logs = LocallyInstalledDirectories.logDirectory,
|
||||
unsafeTemporaryDirectory = dataRoot / TMP_DIRECTORY,
|
||||
@ -132,9 +148,44 @@ class DistributionManager(val env: Environment) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Returns a mapping of environment variables, such that if it is set to
|
||||
* another [[DistributionManager]] instance (but one which is not
|
||||
* [[PortableDistributionManager]], because the portable mark overrides the
|
||||
* environment variable settings), it will lead to the same paths.
|
||||
*/
|
||||
def getEnvironmentToInheritSettings: Map[String, String] = {
|
||||
def canonize(path: Path): String = path.toAbsolutePath.normalize.toString
|
||||
def canonizeSeq(paths: Seq[Path]): String =
|
||||
paths.map(canonize).mkString(File.pathSeparator)
|
||||
Map(
|
||||
ENSO_DATA_DIRECTORY -> canonize(paths.dataRoot),
|
||||
ENSO_CONFIG_DIRECTORY -> canonize(paths.config),
|
||||
ENSO_RUNTIME_DIRECTORY -> canonize(paths.runRoot),
|
||||
ENSO_LOG_DIRECTORY -> canonize(paths.logs),
|
||||
ENSO_HOME -> canonize(paths.ensoHome),
|
||||
ENSO_EDITION_PATH -> canonizeSeq(paths.customEditions),
|
||||
ENSO_LIBRARY_PATH -> canonizeSeq(paths.localLibrariesSearchPaths)
|
||||
)
|
||||
}
|
||||
|
||||
private val ENSO_HOME = "ENSO_HOME"
|
||||
private val ENSO_EDITION_PATH = "ENSO_EDITION_PATH"
|
||||
private val ENSO_LIBRARY_PATH = "ENSO_LIBRARY_PATH"
|
||||
val ENSO_DATA_DIRECTORY = "ENSO_DATA_DIRECTORY"
|
||||
val ENSO_CONFIG_DIRECTORY = "ENSO_CONFIG_DIRECTORY"
|
||||
val ENSO_BIN_DIRECTORY = "ENSO_BIN_DIRECTORY"
|
||||
val ENSO_RUNTIME_DIRECTORY = "ENSO_RUNTIME_DIRECTORY"
|
||||
val ENSO_LOG_DIRECTORY = "ENSO_LOG_DIRECTORY"
|
||||
|
||||
private val ENSO_AUXILIARY_LIBRARY_CACHES = "ENSO_AUXILIARY_LIBRARY_CACHES"
|
||||
|
||||
/** List of paths of additional caches for published libraries.
|
||||
*
|
||||
* These locations can be used to preload published libraries, for example
|
||||
* from a shared network drive, so that they do not need to be downloaded.
|
||||
*/
|
||||
def auxiliaryLibraryCaches(): Seq[Path] =
|
||||
env.getEnvPaths(ENSO_AUXILIARY_LIBRARY_CACHES).getOrElse(Seq())
|
||||
|
||||
/** Finds the path to the ENSO_HOME directory that is used for keeping user's
|
||||
* projects, libraries and other custom artifacts.
|
||||
@ -208,12 +259,6 @@ class DistributionManager(val env: Environment) {
|
||||
* to determine destination for installed files.
|
||||
*/
|
||||
object LocallyInstalledDirectories {
|
||||
val ENSO_DATA_DIRECTORY = "ENSO_DATA_DIRECTORY"
|
||||
val ENSO_CONFIG_DIRECTORY = "ENSO_CONFIG_DIRECTORY"
|
||||
val ENSO_BIN_DIRECTORY = "ENSO_BIN_DIRECTORY"
|
||||
val ENSO_RUNTIME_DIRECTORY = "ENSO_RUNTIME_DIRECTORY"
|
||||
val ENSO_LOG_DIRECTORY = "ENSO_LOG_DIRECTORY"
|
||||
|
||||
private val XDG_DATA_DIRECTORY = "XDG_DATA_HOME"
|
||||
private val XDG_CONFIG_DIRECTORY = "XDG_CONFIG_HOME"
|
||||
private val XDG_BIN_DIRECTORY = "XDG_BIN_HOME"
|
||||
|
@ -76,6 +76,7 @@ class PortableDistributionManager(env: Environment)
|
||||
engines = root / ENGINES_DIRECTORY,
|
||||
bundle = None,
|
||||
config = root / CONFIG_DIRECTORY,
|
||||
runRoot = root,
|
||||
locks = root / LOCK_DIRECTORY,
|
||||
logs = root / LOG_DIRECTORY,
|
||||
unsafeTemporaryDirectory = root / TMP_DIRECTORY,
|
||||
|
@ -0,0 +1,98 @@
|
||||
package org.enso.librarymanager
|
||||
|
||||
import org.enso.distribution.DistributionManager
|
||||
import org.enso.editions.{Editions, LibraryName, LibraryVersion}
|
||||
import org.enso.librarymanager.local.LocalLibraryProvider
|
||||
import org.enso.librarymanager.published.bundles.LocalReadOnlyRepository
|
||||
import org.enso.librarymanager.published.cache.NoOpCache
|
||||
import org.enso.librarymanager.published.{
|
||||
DefaultPublishedLibraryProvider,
|
||||
PublishedLibraryProvider
|
||||
}
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
/** A helper class for loading libraries.
|
||||
*
|
||||
* @param distributionManager a distribution manager
|
||||
* @param engineDistributionRoot the root of the engine distribution that is
|
||||
* being run, if applicable; it is used to make
|
||||
* bundled libraries available
|
||||
* @param edition the edition used in the project
|
||||
* @param preferLocalLibraries project setting whether to use local libraries
|
||||
*/
|
||||
case class DefaultLibraryProvider(
|
||||
distributionManager: DistributionManager,
|
||||
engineDistributionRoot: Option[Path],
|
||||
edition: Editions.ResolvedEdition,
|
||||
preferLocalLibraries: Boolean
|
||||
) extends ResolvingLibraryProvider {
|
||||
private val localLibraryProvider =
|
||||
LocalLibraryProvider.make(distributionManager)
|
||||
private val resolver = LibraryResolver(localLibraryProvider)
|
||||
|
||||
// TODO [RW] actual cache that can download libraries will be implemented in #1772
|
||||
private val primaryCache = new NoOpCache
|
||||
|
||||
private val additionalCaches = {
|
||||
val bundleRoot = engineDistributionRoot.map { root =>
|
||||
// TODO [RW] change this to sth like just `lib`
|
||||
root.resolve("std-lib")
|
||||
}
|
||||
val locations =
|
||||
bundleRoot.toList ++ distributionManager.auxiliaryLibraryCaches()
|
||||
locations.map(new LocalReadOnlyRepository(_))
|
||||
}
|
||||
|
||||
private val publishedLibraryProvider: PublishedLibraryProvider =
|
||||
new DefaultPublishedLibraryProvider(primaryCache, additionalCaches)
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
override def findLibrary(
|
||||
libraryName: LibraryName
|
||||
): Either[ResolvingLibraryProvider.Error, ResolvedLibrary] = {
|
||||
val resolvedVersion = resolver
|
||||
.resolveLibraryVersion(libraryName, edition, preferLocalLibraries)
|
||||
resolvedVersion match {
|
||||
case Left(reason) =>
|
||||
Left(ResolvingLibraryProvider.Error.NotResolved(reason))
|
||||
|
||||
case Right(LibraryVersion.Local) =>
|
||||
localLibraryProvider
|
||||
.findLibrary(libraryName)
|
||||
.map(ResolvedLibrary(libraryName, LibraryVersion.Local, _))
|
||||
.toRight {
|
||||
ResolvingLibraryProvider.Error.NotResolved(
|
||||
LibraryResolutionError(
|
||||
s"Edition configuration forces to use the local version, but " +
|
||||
s"the `$libraryName` library is not present among local " +
|
||||
s"libraries."
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
case Right(version @ LibraryVersion.Published(semver, repository)) =>
|
||||
val dependencyResolver = { name: LibraryName =>
|
||||
resolver
|
||||
.resolveLibraryVersion(name, edition, preferLocalLibraries)
|
||||
.toOption
|
||||
}
|
||||
|
||||
publishedLibraryProvider
|
||||
.findLibrary(
|
||||
libraryName,
|
||||
semver,
|
||||
repository,
|
||||
dependencyResolver
|
||||
)
|
||||
.map(ResolvedLibrary(libraryName, version, _))
|
||||
.toEither
|
||||
.left
|
||||
.map(ResolvingLibraryProvider.Error.DownloadFailed)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
package org.enso.librarymanager
|
||||
|
||||
import org.enso.distribution.DistributionManager
|
||||
import org.enso.editions.{Editions, LibraryName, LibraryVersion}
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
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,12 @@
|
||||
package org.enso.librarymanager
|
||||
|
||||
import org.enso.editions.{LibraryName, LibraryVersion}
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
/** Represents a resolved library that is located somewhere on the filesystem. */
|
||||
case class ResolvedLibrary(
|
||||
name: LibraryName,
|
||||
version: LibraryVersion,
|
||||
location: Path
|
||||
)
|
@ -0,0 +1,42 @@
|
||||
package org.enso.librarymanager
|
||||
|
||||
import org.enso.editions.LibraryName
|
||||
|
||||
/** A helper class for resolving libraries. */
|
||||
trait ResolvingLibraryProvider {
|
||||
|
||||
/** Resolves which library version should be used and finds its path within
|
||||
* local libraries or the cache.
|
||||
*
|
||||
* If the library is not cached, it attempts to download it.
|
||||
*
|
||||
* @param name name of the library
|
||||
* @return the resolved library containing the resulting version and path
|
||||
*/
|
||||
def findLibrary(
|
||||
name: LibraryName
|
||||
): Either[ResolvingLibraryProvider.Error, ResolvedLibrary]
|
||||
}
|
||||
|
||||
object ResolvingLibraryProvider {
|
||||
|
||||
/** Indicates a failure to find a library. */
|
||||
sealed trait Error
|
||||
object Error {
|
||||
|
||||
/** Indicates that the library could not be resolved in the edition
|
||||
* (for example it is not defined in that edition).
|
||||
*/
|
||||
case class NotResolved(details: LibraryResolutionError) extends Error
|
||||
|
||||
/** Indicates that a local library version was requested, but it did not
|
||||
* exist on the library path.
|
||||
*/
|
||||
case object RequestedLocalLibraryDoesNotExist extends Error
|
||||
|
||||
/** Indicates that the library version was missing and had to be downloaded,
|
||||
* but the download has failed.
|
||||
*/
|
||||
case class DownloadFailed(reason: Throwable) extends Error
|
||||
}
|
||||
}
|
@ -1,21 +1,24 @@
|
||||
package org.enso.librarymanager.local
|
||||
|
||||
import org.enso.distribution.DistributionManager
|
||||
import org.enso.editions.LibraryName
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import org.enso.distribution.FileSystem.PathSyntax
|
||||
import org.enso.editions.LibraryName
|
||||
import org.enso.logger.masking.MaskedPath
|
||||
|
||||
import java.nio.file.{Files, Path}
|
||||
import scala.annotation.tailrec
|
||||
|
||||
/** A default implementation of [[LocalLibraryProvider]]. */
|
||||
class DefaultLocalLibraryProvider(distributionManager: DistributionManager)
|
||||
class DefaultLocalLibraryProvider(searchPaths: List[Path])
|
||||
extends LocalLibraryProvider {
|
||||
|
||||
private val logger = Logger[DefaultLocalLibraryProvider]
|
||||
|
||||
/** @inheritdoc */
|
||||
override def findLibrary(libraryName: LibraryName): Option[Path] =
|
||||
findLibraryHelper(
|
||||
libraryName,
|
||||
distributionManager.paths.localLibrariesSearchPaths.toList
|
||||
searchPaths
|
||||
)
|
||||
|
||||
/** Searches through the available library paths, checking if any one of them contains the requested library.
|
||||
@ -29,9 +32,19 @@ class DefaultLocalLibraryProvider(distributionManager: DistributionManager)
|
||||
): Option[Path] = searchPaths match {
|
||||
case head :: tail =>
|
||||
val potentialPath = head / libraryName.namespace / libraryName.name
|
||||
if (Files.exists(potentialPath) && Files.isDirectory(potentialPath))
|
||||
if (Files.exists(potentialPath) && Files.isDirectory(potentialPath)) {
|
||||
logger.trace(
|
||||
s"Found a local $libraryName at " +
|
||||
s"[${MaskedPath(potentialPath).applyMasking()}]."
|
||||
)
|
||||
Some(potentialPath)
|
||||
else findLibraryHelper(libraryName, tail)
|
||||
} else {
|
||||
logger.trace(
|
||||
s"Local library $libraryName not found at " +
|
||||
s"[${MaskedPath(potentialPath).applyMasking()}]."
|
||||
)
|
||||
findLibraryHelper(libraryName, tail)
|
||||
}
|
||||
case Nil => None
|
||||
}
|
||||
}
|
||||
|
@ -19,5 +19,7 @@ object LocalLibraryProvider {
|
||||
/** Creates a default [[LocalLibraryProvider]] from a [[DistributionManager]].
|
||||
*/
|
||||
def make(distributionManager: DistributionManager): LocalLibraryProvider =
|
||||
new DefaultLocalLibraryProvider(distributionManager)
|
||||
new DefaultLocalLibraryProvider(
|
||||
distributionManager.paths.localLibrariesSearchPaths.toList
|
||||
)
|
||||
}
|
||||
|
@ -1,18 +1,41 @@
|
||||
package org.enso.librarymanager.published
|
||||
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.distribution.DistributionManager
|
||||
import org.enso.editions.{Editions, LibraryName, LibraryVersion}
|
||||
import org.enso.librarymanager.LibraryResolutionResult
|
||||
import org.enso.librarymanager.published.cache.{
|
||||
LibraryCache,
|
||||
ReadOnlyLibraryCache
|
||||
}
|
||||
|
||||
import scala.annotation.nowarn
|
||||
import java.nio.file.Path
|
||||
import scala.annotation.tailrec
|
||||
import scala.util.{Success, Try}
|
||||
|
||||
/** A default implementation of [[PublishedLibraryProvider]]. */
|
||||
/** A default implementation of [[PublishedLibraryProvider]] which uses one
|
||||
* primary cache to which it will download missing packages and auxiliary
|
||||
* read-only caches which may provide additional libraries.
|
||||
*/
|
||||
class DefaultPublishedLibraryProvider(
|
||||
@nowarn("msg=never used") distributionManager: DistributionManager
|
||||
primaryCache: LibraryCache,
|
||||
auxiliaryCaches: List[ReadOnlyLibraryCache]
|
||||
) extends PublishedLibraryProvider {
|
||||
|
||||
// TODO [RW] This is just a stub and will be properly implemented in #1772.
|
||||
private val caches: List[ReadOnlyLibraryCache] =
|
||||
primaryCache :: auxiliaryCaches
|
||||
|
||||
@tailrec
|
||||
private def findCached(
|
||||
libraryName: LibraryName,
|
||||
version: SemVer,
|
||||
caches: List[ReadOnlyLibraryCache]
|
||||
): Option[Path] = caches match {
|
||||
case head :: tail =>
|
||||
head.findCachedLibrary(libraryName, version) match {
|
||||
case Some(found) => Some(found)
|
||||
case None => findCached(libraryName, version, tail)
|
||||
}
|
||||
case Nil => None
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def findLibrary(
|
||||
@ -20,9 +43,15 @@ class DefaultPublishedLibraryProvider(
|
||||
version: SemVer,
|
||||
recommendedRepository: Editions.Repository,
|
||||
dependencyResolver: LibraryName => Option[LibraryVersion]
|
||||
): LibraryResolutionResult = LibraryResolutionResult.ResolutionFailure(
|
||||
new NotImplementedError(
|
||||
"TODO library management is not yet implemented"
|
||||
)
|
||||
)
|
||||
): Try[Path] = {
|
||||
val cached = findCached(libraryName, version, caches)
|
||||
cached.map(Success(_)).getOrElse {
|
||||
primaryCache.findOrInstallLibrary(
|
||||
libraryName,
|
||||
version,
|
||||
recommendedRepository,
|
||||
dependencyResolver
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,9 @@ package org.enso.librarymanager.published
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.editions.Editions.Repository
|
||||
import org.enso.editions.{LibraryName, LibraryVersion}
|
||||
import org.enso.librarymanager.LibraryResolutionResult
|
||||
|
||||
import java.nio.file.Path
|
||||
import scala.util.Try
|
||||
|
||||
/** A provider of published libraries.
|
||||
*
|
||||
@ -23,5 +25,5 @@ trait PublishedLibraryProvider {
|
||||
version: SemVer,
|
||||
recommendedRepository: Repository,
|
||||
dependencyResolver: LibraryName => Option[LibraryVersion]
|
||||
): LibraryResolutionResult
|
||||
): Try[Path]
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
package org.enso.librarymanager.published.bundles
|
||||
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.editions.LibraryName
|
||||
import org.enso.librarymanager.published.cache.{
|
||||
LibraryCache,
|
||||
ReadOnlyLibraryCache
|
||||
}
|
||||
|
||||
import java.nio.file.{Files, Path}
|
||||
|
||||
/** Implements a read-only cache backed by a repository on the local filesystem.
|
||||
*
|
||||
* This repository is either immutable (like the bundles) or user-managed (for
|
||||
* custom solutions). In the first case synchronization is not necessary, in
|
||||
* the second case it is impossible (as we have no authority over user's
|
||||
* actions). So this class performs no synchronization and in the second case
|
||||
* it is the user's case to not import libraries that are in the middle of
|
||||
* being copied into this repository.
|
||||
*/
|
||||
class LocalReadOnlyRepository(root: Path) extends ReadOnlyLibraryCache {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def findCachedLibrary(
|
||||
libraryName: LibraryName,
|
||||
version: SemVer
|
||||
): Option[Path] = {
|
||||
val path = LibraryCache.resolvePath(root, libraryName, version)
|
||||
if (Files.exists(path)) Some(path) else None
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package org.enso.librarymanager.published.cache
|
||||
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.editions.{Editions, LibraryName, LibraryVersion}
|
||||
|
||||
import java.nio.file.Path
|
||||
import scala.util.Try
|
||||
|
||||
/** A library cache that is also capable of downloading missing libraries (which
|
||||
* will then be cached).
|
||||
*/
|
||||
trait LibraryCache extends ReadOnlyLibraryCache {
|
||||
|
||||
/** Returns the path to the library it is already cached.
|
||||
*
|
||||
* This method should not attempt to download the library if it is missing,
|
||||
* because other providers may have it.
|
||||
*
|
||||
* As this repository is not immutable - new libraries may be added during
|
||||
* the runtime, this method must too be aware of the concurrency - it should
|
||||
* use locks to make sure that, if the library is currently being installed,
|
||||
* it is not returned before the installation is complete (as otherwise the
|
||||
* runtime could access an incompletely installed library which could lead to
|
||||
* errors).
|
||||
*/
|
||||
override def findCachedLibrary(
|
||||
libraryName: LibraryName,
|
||||
version: SemVer
|
||||
): Option[Path]
|
||||
|
||||
/** If the cache contains the library, it is returned immediately, otherwise,
|
||||
* it tries to download the missing library.
|
||||
*
|
||||
* @param libraryName the name of the library to search for
|
||||
* @param version the library version
|
||||
* @param recommendedRepository the repository that should be used to
|
||||
* download the library from, if it is missing
|
||||
* @param dependencyResolver a function that will specify what versions of
|
||||
* dependencies should be also downloaded when
|
||||
* installing the missing library (if any)
|
||||
* TODO [RW] the design of this function should be refined in #1772
|
||||
* @return the path to the library or a failure if the library could not be
|
||||
* installed
|
||||
*/
|
||||
def findOrInstallLibrary(
|
||||
libraryName: LibraryName,
|
||||
version: SemVer,
|
||||
recommendedRepository: Editions.Repository,
|
||||
dependencyResolver: LibraryName => Option[LibraryVersion]
|
||||
): Try[Path]
|
||||
}
|
||||
|
||||
object LibraryCache {
|
||||
|
||||
/** Finds a path to a particular library version inside of a local
|
||||
* repository/cache according to the cache's directory structure.
|
||||
*
|
||||
* @param root path to the root of the repository
|
||||
* @param libraryName name of the library
|
||||
* @param version library version
|
||||
* @return the path at which the specified library would be located in the
|
||||
* repository
|
||||
*/
|
||||
def resolvePath(root: Path, libraryName: LibraryName, version: SemVer): Path =
|
||||
root
|
||||
.resolve(libraryName.namespace)
|
||||
.resolve(libraryName.name)
|
||||
.resolve(version.toString)
|
||||
}
|
30
lib/scala/library-manager/src/main/scala/org/enso/librarymanager/published/cache/NoOpCache.scala
vendored
Normal file
30
lib/scala/library-manager/src/main/scala/org/enso/librarymanager/published/cache/NoOpCache.scala
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
package org.enso.librarymanager.published.cache
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.editions.{Editions, LibraryName, LibraryVersion}
|
||||
|
||||
import java.nio.file.Path
|
||||
import scala.util.{Failure, Try}
|
||||
|
||||
/** A temporary cache that provides no libraries.
|
||||
*
|
||||
* This is a temporary poly-fill which will later be replaced when the
|
||||
* downloading mechanism is implemented.
|
||||
*/
|
||||
class NoOpCache extends LibraryCache {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def findCachedLibrary(
|
||||
libraryName: LibraryName,
|
||||
version: SemVer
|
||||
): Option[Path] = None
|
||||
|
||||
/** @inheritdoc */
|
||||
override def findOrInstallLibrary(
|
||||
libraryName: LibraryName,
|
||||
version: SemVer,
|
||||
recommendedRepository: Editions.Repository,
|
||||
dependencyResolver: LibraryName => Option[LibraryVersion]
|
||||
): Try[Path] = Failure(
|
||||
new NotImplementedError("Downloading libraries is not yet implemented.")
|
||||
)
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package org.enso.librarymanager.published.cache
|
||||
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.editions.LibraryName
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
/** A read-only cache may contain some pre-defined set of libraries, but it does
|
||||
* not necessarily install any additional libraries.
|
||||
*
|
||||
* An example of a read-only cache are libraries bundled with the engine.
|
||||
*/
|
||||
trait ReadOnlyLibraryCache {
|
||||
|
||||
/** Locates the library in the cache and returns the path to its root if it
|
||||
* has been found.
|
||||
*/
|
||||
def findCachedLibrary(libraryName: LibraryName, version: SemVer): Option[Path]
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package org.slf4j.impl;
|
||||
|
||||
import org.enso.truffleloggerwrapper.TruffleLoggerWrapperFactory;
|
||||
import org.slf4j.ILoggerFactory;
|
||||
|
||||
/**
|
||||
* Binds the SLF4J logger instance within the runtime to a logger which wraps the TruffleLogger.
|
||||
*
|
||||
* <p>This way, the standard SLF4J interface that is used in other subprojects, can also be used
|
||||
* within the runtime and its log messages are correctly passed to the TruffleLogger. Thus libraries
|
||||
* that are used both inside and outside of runtime can keep a simple interface.
|
||||
*
|
||||
* <p>The public interface of this class must conform to what is expected by an SLF4J backend. See
|
||||
* slf4j-simple for reference.
|
||||
*/
|
||||
public class StaticLoggerBinder {
|
||||
/** Should be in sync with `slf4jVersion` in `build.sbt`. */
|
||||
public static String REQUESTED_API_VERSION = "1.7.30";
|
||||
|
||||
private static final StaticLoggerBinder singleton = new StaticLoggerBinder();
|
||||
|
||||
public static StaticLoggerBinder getSingleton() {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
private final TruffleLoggerWrapperFactory factory = new TruffleLoggerWrapperFactory();
|
||||
private final String factoryClassStr = TruffleLoggerWrapperFactory.class.getName();
|
||||
|
||||
public ILoggerFactory getLoggerFactory() {
|
||||
return factory;
|
||||
}
|
||||
|
||||
public String getLoggerFactoryClassStr() {
|
||||
return factoryClassStr;
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package org.slf4j.impl;
|
||||
|
||||
import org.slf4j.helpers.NOPMDCAdapter;
|
||||
import org.slf4j.spi.MDCAdapter;
|
||||
|
||||
/**
|
||||
* Provides a no-op MDC adapter for the SLF4J backend.
|
||||
*
|
||||
* <p>MDC handling is an optional SLF4J feature and currently the logging service does not support
|
||||
* it, so it provides a no-op adapter.
|
||||
*
|
||||
* <p>The public interface of this class must conform to what is expected by an SLF4J backend. See
|
||||
* slf4j-simple for reference.
|
||||
*/
|
||||
public class StaticMDCBinder {
|
||||
|
||||
private static final StaticMDCBinder singleton = new StaticMDCBinder();
|
||||
|
||||
public static StaticMDCBinder getSingleton() {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
private final MDCAdapter adapter = new NOPMDCAdapter();
|
||||
private final String adapterClassStr = NOPMDCAdapter.class.getName();
|
||||
|
||||
public MDCAdapter getMDCA() {
|
||||
return adapter;
|
||||
}
|
||||
|
||||
public String getMDCAdapterClassStr() {
|
||||
return adapterClassStr;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package org.slf4j.impl;
|
||||
|
||||
import org.slf4j.IMarkerFactory;
|
||||
import org.slf4j.helpers.BasicMarkerFactory;
|
||||
|
||||
/**
|
||||
* Provides a simple marker factory for the SLF4J backend.
|
||||
*
|
||||
* <p>The public interface of this class must conform to what is expected by an SLF4J backend. See
|
||||
* slf4j-simple for reference.
|
||||
*/
|
||||
public class StaticMarkerBinder {
|
||||
|
||||
private static final StaticMarkerBinder singleton = new StaticMarkerBinder();
|
||||
|
||||
public static StaticMarkerBinder getSingleton() {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
private final IMarkerFactory markerFactory = new BasicMarkerFactory();
|
||||
private final String markerFactoryClassStr = BasicMarkerFactory.class.getName();
|
||||
|
||||
public IMarkerFactory getMarkerFactory() {
|
||||
return markerFactory;
|
||||
}
|
||||
|
||||
public String getMarkerFactoryClassStr() {
|
||||
return markerFactoryClassStr;
|
||||
}
|
||||
}
|
@ -0,0 +1,319 @@
|
||||
package org.enso.truffleloggerwrapper
|
||||
|
||||
import com.oracle.truffle.api.TruffleLogger
|
||||
import org.enso.logger.masking.Masking
|
||||
import org.enso.polyglot.LanguageInfo
|
||||
import org.slf4j.helpers.MessageFormatter
|
||||
import org.slf4j.{Logger, Marker}
|
||||
|
||||
import java.util.logging.Level
|
||||
import scala.annotation.unused
|
||||
|
||||
/** A wrapper around [[TruffleLogger]] that abides by the SLF4J's [[Logger]]
|
||||
* interface.
|
||||
*
|
||||
* It is used so that libraries which are used both inside and outside of the
|
||||
* runtime can simply use the SLF4J API and the log messages are passed to the
|
||||
* correct backend (in the case of the runtime, they are forwarded to the
|
||||
* [[TruffleLogger]]).
|
||||
*/
|
||||
class TruffleLoggerWrapper(name: String, masking: Masking) extends Logger {
|
||||
final private val underlying = TruffleLogger.getLogger(LanguageInfo.ID, name)
|
||||
|
||||
override def getName: String = underlying.getName
|
||||
|
||||
private def isEnabled(level: Level): Boolean =
|
||||
underlying.isLoggable(level)
|
||||
|
||||
private def log(
|
||||
level: Level,
|
||||
msg: String
|
||||
): Unit = {
|
||||
if (isEnabled(level)) {
|
||||
underlying.log(level, msg)
|
||||
}
|
||||
}
|
||||
|
||||
private def log(
|
||||
level: Level,
|
||||
format: String,
|
||||
arg: AnyRef
|
||||
): Unit = {
|
||||
if (isEnabled(level)) {
|
||||
val maskedArg = masking.mask(arg)
|
||||
val fp = MessageFormatter.format(format, maskedArg)
|
||||
if (fp.getThrowable == null) {
|
||||
underlying.log(level, fp.getMessage)
|
||||
} else {
|
||||
underlying.log(level, fp.getMessage, fp.getThrowable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def log(
|
||||
level: Level,
|
||||
format: String,
|
||||
arg1: AnyRef,
|
||||
arg2: AnyRef
|
||||
): Unit = {
|
||||
if (isEnabled(level)) {
|
||||
val maskedArg1 = masking.mask(arg1)
|
||||
val maskedArg2 = masking.mask(arg2)
|
||||
val fp = MessageFormatter.format(format, maskedArg1, maskedArg2)
|
||||
if (fp.getThrowable == null) {
|
||||
underlying.log(level, fp.getMessage)
|
||||
} else {
|
||||
underlying.log(level, fp.getMessage, fp.getThrowable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def log(
|
||||
level: Level,
|
||||
format: String,
|
||||
args: Seq[AnyRef]
|
||||
): Unit = {
|
||||
if (isEnabled(level)) {
|
||||
val maskedArgs = args.map(masking.mask)
|
||||
val fp = MessageFormatter.arrayFormat(format, maskedArgs.toArray)
|
||||
if (fp.getThrowable == null) {
|
||||
underlying.log(level, fp.getMessage)
|
||||
} else {
|
||||
underlying.log(level, fp.getMessage, fp.getThrowable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def log(
|
||||
level: Level,
|
||||
msg: String,
|
||||
throwable: Throwable
|
||||
): Unit = {
|
||||
if (isEnabled(level)) {
|
||||
underlying.log(level, msg, throwable)
|
||||
}
|
||||
}
|
||||
|
||||
override def isTraceEnabled: Boolean = isEnabled(Level.FINER)
|
||||
|
||||
override def trace(msg: String): Unit = log(Level.FINER, msg)
|
||||
|
||||
override def trace(format: String, arg: AnyRef): Unit =
|
||||
log(Level.FINER, format, arg)
|
||||
|
||||
override def trace(format: String, arg1: AnyRef, arg2: AnyRef): Unit =
|
||||
log(Level.FINER, format, arg1, arg2)
|
||||
|
||||
override def trace(format: String, arguments: AnyRef*): Unit =
|
||||
log(Level.FINER, format, arguments)
|
||||
|
||||
override def trace(msg: String, t: Throwable): Unit = log(Level.FINER, msg, t)
|
||||
|
||||
override def isTraceEnabled(@unused marker: Marker): Boolean =
|
||||
isEnabled(Level.FINER)
|
||||
|
||||
override def trace(@unused marker: Marker, msg: String): Unit =
|
||||
log(Level.FINER, msg)
|
||||
|
||||
override def trace(
|
||||
@unused marker: Marker,
|
||||
format: String,
|
||||
arg: AnyRef
|
||||
): Unit =
|
||||
log(Level.FINER, format, arg)
|
||||
|
||||
override def trace(
|
||||
@unused marker: Marker,
|
||||
format: String,
|
||||
arg1: AnyRef,
|
||||
arg2: AnyRef
|
||||
): Unit = log(Level.FINER, format, arg1, arg2)
|
||||
|
||||
override def trace(
|
||||
@unused marker: Marker,
|
||||
format: String,
|
||||
argArray: AnyRef*
|
||||
): Unit =
|
||||
log(Level.FINER, format, argArray)
|
||||
|
||||
override def trace(@unused marker: Marker, msg: String, t: Throwable): Unit =
|
||||
log(Level.FINER, msg, t)
|
||||
|
||||
override def isDebugEnabled: Boolean = isEnabled(Level.FINE)
|
||||
|
||||
override def debug(msg: String): Unit = log(Level.FINE, msg)
|
||||
|
||||
override def debug(format: String, arg: AnyRef): Unit =
|
||||
log(Level.FINE, format, arg)
|
||||
|
||||
override def debug(format: String, arg1: AnyRef, arg2: AnyRef): Unit =
|
||||
log(Level.FINE, format, arg1, arg2)
|
||||
|
||||
override def debug(format: String, arguments: AnyRef*): Unit =
|
||||
log(Level.FINE, format, arguments)
|
||||
|
||||
override def debug(msg: String, t: Throwable): Unit = log(Level.FINE, msg, t)
|
||||
|
||||
override def isDebugEnabled(@unused marker: Marker): Boolean = isEnabled(
|
||||
Level.FINE
|
||||
)
|
||||
|
||||
override def debug(@unused marker: Marker, msg: String): Unit =
|
||||
log(Level.FINE, msg)
|
||||
|
||||
override def debug(
|
||||
@unused marker: Marker,
|
||||
format: String,
|
||||
arg: AnyRef
|
||||
): Unit =
|
||||
log(Level.FINE, format, arg)
|
||||
|
||||
override def debug(
|
||||
@unused marker: Marker,
|
||||
format: String,
|
||||
arg1: AnyRef,
|
||||
arg2: AnyRef
|
||||
): Unit = log(Level.FINE, format, arg1, arg2)
|
||||
|
||||
override def debug(
|
||||
@unused marker: Marker,
|
||||
format: String,
|
||||
arguments: AnyRef*
|
||||
): Unit =
|
||||
log(Level.FINE, format, arguments)
|
||||
|
||||
override def debug(@unused marker: Marker, msg: String, t: Throwable): Unit =
|
||||
log(Level.FINE, msg, t)
|
||||
|
||||
override def isInfoEnabled: Boolean = isEnabled(Level.INFO)
|
||||
|
||||
override def info(msg: String): Unit = log(Level.INFO, msg)
|
||||
|
||||
override def info(format: String, arg: AnyRef): Unit =
|
||||
log(Level.INFO, format, arg)
|
||||
|
||||
override def info(format: String, arg1: AnyRef, arg2: AnyRef): Unit =
|
||||
log(Level.INFO, format, arg1, arg2)
|
||||
|
||||
override def info(format: String, arguments: AnyRef*): Unit =
|
||||
log(Level.INFO, format, arguments)
|
||||
|
||||
override def info(msg: String, t: Throwable): Unit = log(Level.INFO, msg, t)
|
||||
|
||||
override def isInfoEnabled(@unused marker: Marker): Boolean = isEnabled(
|
||||
Level.INFO
|
||||
)
|
||||
|
||||
override def info(@unused marker: Marker, msg: String): Unit =
|
||||
log(Level.INFO, msg)
|
||||
|
||||
override def info(@unused marker: Marker, format: String, arg: AnyRef): Unit =
|
||||
log(Level.INFO, format, arg)
|
||||
|
||||
override def info(
|
||||
@unused marker: Marker,
|
||||
format: String,
|
||||
arg1: AnyRef,
|
||||
arg2: AnyRef
|
||||
): Unit = log(Level.INFO, format, arg1, arg2)
|
||||
|
||||
override def info(
|
||||
@unused marker: Marker,
|
||||
format: String,
|
||||
arguments: AnyRef*
|
||||
): Unit =
|
||||
log(Level.INFO, format, arguments)
|
||||
|
||||
override def info(@unused marker: Marker, msg: String, t: Throwable): Unit =
|
||||
log(Level.INFO, msg, t)
|
||||
|
||||
override def isWarnEnabled: Boolean = isEnabled(Level.WARNING)
|
||||
|
||||
override def warn(msg: String): Unit = log(Level.WARNING, msg)
|
||||
|
||||
override def warn(format: String, arg: AnyRef): Unit =
|
||||
log(Level.WARNING, format, arg)
|
||||
|
||||
override def warn(format: String, arguments: AnyRef*): Unit =
|
||||
log(Level.WARNING, format, arguments)
|
||||
|
||||
override def warn(format: String, arg1: AnyRef, arg2: AnyRef): Unit =
|
||||
log(Level.WARNING, format, arg1, arg2)
|
||||
|
||||
override def warn(msg: String, t: Throwable): Unit =
|
||||
log(Level.WARNING, msg, t)
|
||||
|
||||
override def isWarnEnabled(@unused marker: Marker): Boolean = isEnabled(
|
||||
Level.WARNING
|
||||
)
|
||||
|
||||
override def warn(@unused marker: Marker, msg: String): Unit =
|
||||
log(Level.WARNING, msg)
|
||||
|
||||
override def warn(@unused marker: Marker, format: String, arg: AnyRef): Unit =
|
||||
log(Level.WARNING, format, arg)
|
||||
|
||||
override def warn(
|
||||
@unused marker: Marker,
|
||||
format: String,
|
||||
arg1: AnyRef,
|
||||
arg2: AnyRef
|
||||
): Unit = log(Level.WARNING, format, arg1, arg2)
|
||||
|
||||
override def warn(
|
||||
@unused marker: Marker,
|
||||
format: String,
|
||||
arguments: AnyRef*
|
||||
): Unit =
|
||||
log(Level.WARNING, format, arguments)
|
||||
|
||||
override def warn(@unused marker: Marker, msg: String, t: Throwable): Unit =
|
||||
log(Level.WARNING, msg, t)
|
||||
|
||||
override def isErrorEnabled: Boolean = isEnabled(Level.SEVERE)
|
||||
|
||||
override def error(msg: String): Unit = log(Level.SEVERE, msg)
|
||||
|
||||
override def error(format: String, arg: AnyRef): Unit =
|
||||
log(Level.SEVERE, format, arg)
|
||||
|
||||
override def error(format: String, arg1: AnyRef, arg2: AnyRef): Unit =
|
||||
log(Level.SEVERE, format, arg1, arg2)
|
||||
|
||||
override def error(format: String, arguments: AnyRef*): Unit =
|
||||
log(Level.SEVERE, format, arguments)
|
||||
|
||||
override def error(msg: String, t: Throwable): Unit =
|
||||
log(Level.SEVERE, msg, t)
|
||||
|
||||
override def isErrorEnabled(@unused marker: Marker): Boolean = isEnabled(
|
||||
Level.SEVERE
|
||||
)
|
||||
|
||||
override def error(@unused marker: Marker, msg: String): Unit =
|
||||
log(Level.SEVERE, msg)
|
||||
|
||||
override def error(
|
||||
@unused marker: Marker,
|
||||
format: String,
|
||||
arg: AnyRef
|
||||
): Unit =
|
||||
log(Level.SEVERE, format, arg)
|
||||
|
||||
override def error(
|
||||
@unused marker: Marker,
|
||||
format: String,
|
||||
arg1: AnyRef,
|
||||
arg2: AnyRef
|
||||
): Unit = log(Level.SEVERE, format, arg1, arg2)
|
||||
|
||||
override def error(
|
||||
@unused marker: Marker,
|
||||
format: String,
|
||||
arguments: AnyRef*
|
||||
): Unit =
|
||||
log(Level.SEVERE, format, arguments)
|
||||
|
||||
override def error(@unused marker: Marker, msg: String, t: Throwable): Unit =
|
||||
log(Level.SEVERE, msg, t)
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package org.enso.truffleloggerwrapper
|
||||
|
||||
import org.enso.logger.masking.Masking
|
||||
import org.slf4j.ILoggerFactory
|
||||
|
||||
/** A factory for [[TruffleLoggerWrapper]]. */
|
||||
class TruffleLoggerWrapperFactory extends ILoggerFactory {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def getLogger(name: String) =
|
||||
new TruffleLoggerWrapper(name, Masking())
|
||||
}
|
@ -16,7 +16,8 @@ class HomeManager[F](languageHomePath: F, implicit val fs: FileSystem[F]) {
|
||||
val packageManager = new PackageManager[F]()
|
||||
|
||||
val rootPath: F = languageHomePath.getParent
|
||||
val libPath: F = rootPath.getChild("std-lib")
|
||||
// TODO [RW] rename to lib or sth more general than just std
|
||||
val libPath: F = rootPath.getChild("std-lib")
|
||||
|
||||
/** @return a stream of packages found in the `std-lib` home directory.
|
||||
*/
|
||||
|
@ -1,7 +1,7 @@
|
||||
package org.enso.pkg
|
||||
|
||||
import cats.Show
|
||||
import org.enso.editions.{DefaultEnsoVersion, EnsoVersion}
|
||||
import org.enso.editions.{DefaultEnsoVersion, EnsoVersion, LibraryName}
|
||||
import org.enso.filesystem.FileSystem
|
||||
import org.enso.pkg.validation.NameValidation
|
||||
|
||||
@ -124,6 +124,9 @@ case class Package[F](
|
||||
|
||||
def namespace: String = config.namespace
|
||||
|
||||
/** A [[LibraryName]] associated with the package. */
|
||||
def libraryName: LibraryName = LibraryName(config.namespace, config.name)
|
||||
|
||||
/** Parses a file path into a qualified module name belonging to this
|
||||
* package.
|
||||
*
|
||||
|
@ -89,6 +89,7 @@ object ExecutorWithUnlimitedPool extends LanguageServerExecutor {
|
||||
)
|
||||
val runner = new Runner(
|
||||
runtimeVersionManager = versionManager,
|
||||
distributionManager = distributionConfiguration.distributionManager,
|
||||
globalConfigurationManager = configurationManager,
|
||||
editionManager = distributionConfiguration.editionManager,
|
||||
environment = distributionConfiguration.environment,
|
||||
|
@ -45,6 +45,7 @@ class ProjectCreationService[
|
||||
val runner =
|
||||
new Runner(
|
||||
runtimeVersionManager = versionManager,
|
||||
distributionManager = distributionConfiguration.distributionManager,
|
||||
globalConfigurationManager = configurationManager,
|
||||
editionManager = distributionConfiguration.editionManager,
|
||||
environment = distributionConfiguration.environment,
|
||||
|
@ -860,12 +860,13 @@ class RuntimeVersionManager(
|
||||
.getOrElse("no runtime found")
|
||||
val broken = if (engine.isMarkedBroken) " (broken)" else ""
|
||||
s" - Enso ${engine.version}$broken [runtime: $runtimeName] " +
|
||||
s"[location: ${MaskedPath(engine.path)}]"
|
||||
s"[location: ${MaskedPath(engine.path).applyMasking()}]"
|
||||
}
|
||||
|
||||
val runtimes =
|
||||
for (runtime <- listInstalledGraalRuntimes())
|
||||
yield s" - $runtime [location: ${MaskedPath(runtime.path)}]"
|
||||
yield s" - $runtime [location: " +
|
||||
s"${MaskedPath(runtime.path).applyMasking()}]"
|
||||
|
||||
logger.trace(
|
||||
s"Installed engines (${engines.length}):\n${engines.mkString("\n")}\n\n" +
|
||||
|
@ -3,7 +3,7 @@ package org.enso.runtimeversionmanager.runner
|
||||
import akka.http.scaladsl.model.Uri
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.distribution.{EditionManager, Environment}
|
||||
import org.enso.distribution.{DistributionManager, EditionManager, Environment}
|
||||
import org.enso.editions.{DefaultEnsoVersion, SemVerEnsoVersion}
|
||||
import org.enso.logger.masking.MaskedString
|
||||
import org.enso.loggingservice.LogLevel
|
||||
@ -26,6 +26,7 @@ import scala.util.Try
|
||||
*/
|
||||
class Runner(
|
||||
runtimeVersionManager: RuntimeVersionManager,
|
||||
distributionManager: DistributionManager,
|
||||
globalConfigurationManager: GlobalConfigurationManager,
|
||||
editionManager: EditionManager,
|
||||
environment: Environment,
|
||||
@ -179,9 +180,12 @@ class Runner(
|
||||
val command = Seq(javaCommand.executableName) ++
|
||||
jvmArguments ++ loggingConnectionArguments ++ runSettings.runnerArguments
|
||||
|
||||
// TODO [RW] set the paths from DistributionManager so that the engine can use the simple distribution resolution logic
|
||||
val distributionSettings =
|
||||
distributionManager.getEnvironmentToInheritSettings
|
||||
val extraEnvironmentOverrides =
|
||||
javaCommand.javaHomeOverride.map("JAVA_HOME" -> _).toSeq
|
||||
javaCommand.javaHomeOverride
|
||||
.map("JAVA_HOME" -> _)
|
||||
.toSeq ++ distributionSettings.toSeq
|
||||
|
||||
action(Command(command, extraEnvironmentOverrides))
|
||||
}
|
||||
|
@ -1 +0,0 @@
|
||||
Copyright (c) 2004-2011 QOS.ch
|
@ -1,3 +1,3 @@
|
||||
362FC1C027AFBFD6F5AD317AA49A3CC4DBB8DF5F64F03F7F421DBEC5D84F00D1
|
||||
00E5CD7BD57C6E26B84CA9835FE8ECE2239DEF94E8FBB719A35BA909079ADEA4
|
||||
167455C7D1FE65E7901D5613C3DBB3158CE003D45DE21D8D085815A2D20B317C
|
||||
64FE86A276F737CE2B4D352D8F35F790CA0DA18F329A48A467F34FC9C3CAF07D
|
||||
0
|
||||
|
@ -1,3 +1,3 @@
|
||||
0BA0D3694722E724BABC3D4D860FA5D11DA541B20632F18175E1F45BF44DE717
|
||||
378F1F6DE5E96E55648F0B31C20AF99F1C9190926BE6429B0FC8BE7CAF9E6E47
|
||||
B7948AB0E996317E7F073260BA28A63D14215436079C09E793F803CF999D607C
|
||||
0
|
||||
|
Loading…
Reference in New Issue
Block a user