Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Projects loaded from arbitrary URIs #255

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
53 changes: 42 additions & 11 deletions pkl-cli/src/main/kotlin/org/pkl/cli/CliProjectPackager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,17 @@ import org.pkl.commons.cli.CliBaseOptions
import org.pkl.commons.cli.CliException
import org.pkl.commons.cli.CliTestException
import org.pkl.commons.cli.CliTestOptions
import org.pkl.core.Loggers
import org.pkl.core.SecurityManagers
import org.pkl.core.StackFrameTransformers
import org.pkl.core.module.ModuleKeyFactories
import org.pkl.core.project.Project
import org.pkl.core.project.ProjectPackager
import org.pkl.core.resource.ResourceReaders
import org.pkl.core.runtime.ModuleResolver
import org.pkl.core.runtime.ResourceManager
import org.pkl.core.runtime.VmContext
import org.pkl.core.runtime.VmUtils
import org.pkl.core.util.ErrorMessages

class CliProjectPackager(
Expand Down Expand Up @@ -76,16 +85,38 @@ class CliProjectPackager(
}
}
}
ProjectPackager(
projects,
cliOptions.normalizedWorkingDir,
outputPath,
stackFrameTransformer,
securityManager,
httpClient,
skipPublishCheck,
consoleWriter
)
.createPackages()
// A VmContext is needed here because loading PklProject.deps.json uses resource readers
VmUtils.createContext {
val vmContext = VmContext.get(null)
vmContext.initialize(
VmContext.Holder(
StackFrameTransformers.defaultTransformer,
SecurityManagers.defaultManager,
httpClient,
ModuleResolver(listOf(ModuleKeyFactories.standardLibrary)),
ResourceManager(SecurityManagers.defaultManager, listOf(ResourceReaders.file())),
Loggers.noop(),
mapOf(),
mapOf(),
null,
null,
null,
null
)
)

ProjectPackager(
projects,
cliOptions.normalizedWorkingDir,
outputPath,
stackFrameTransformer,
securityManager,
httpClient,
skipPublishCheck,
consoleWriter
)
.createPackages()
}
.close()
}
}
37 changes: 20 additions & 17 deletions pkl-core/src/main/java/org/pkl/core/module/ModuleKeys.java
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ public ResolvedModuleKey resolve(SecurityManager securityManager)
@Override
protected Map<String, ? extends Dependency> getDependencies() {
var projectDepsManager = VmContext.get(null).getProjectDependenciesManager();
if (projectDepsManager == null || !projectDepsManager.hasPath(path)) {
if (projectDepsManager == null || !projectDepsManager.hasUri(uri)) {
throw new PackageLoadError("cannotResolveDependencyNoProject");
}
return projectDepsManager.getDependencies();
Expand Down Expand Up @@ -517,11 +517,11 @@ public ResolvedModuleKey resolve(SecurityManager securityManager)
}

/** Base implementation; knows how to resolve dependencies prefixed with <code>@</code>. */
private abstract static class DependencyAwareModuleKey implements ModuleKey {
public abstract static class DependencyAwareModuleKey implements ModuleKey {

protected final URI uri;

DependencyAwareModuleKey(URI uri) {
protected DependencyAwareModuleKey(URI uri) {
this.uri = uri;
}

Expand Down Expand Up @@ -672,12 +672,12 @@ private ProjectDependenciesManager getProjectDepsResolver() {
return projectDepsManager;
}

private @Nullable Path getLocalPath(Dependency dependency) {
private @Nullable URI getLocalUri(Dependency dependency, PackageAssetUri assetUri) {
if (!(dependency instanceof LocalDependency)) {
return null;
}
return ((LocalDependency) dependency)
.resolveAssetPath(getProjectDepsResolver().getProjectDir(), packageAssetUri);
.resolveAssetUri(getProjectDepsResolver().getProjectBaseUri(), assetUri);
}

@Override
Expand All @@ -686,10 +686,9 @@ public ResolvedModuleKey resolve(SecurityManager securityManager)
securityManager.checkResolveModule(packageAssetUri.getUri());
var dependency =
getProjectDepsResolver().getResolvedDependency(packageAssetUri.getPackageUri());
var path = getLocalPath(dependency);
if (path != null) {
securityManager.checkResolveModule(path.toUri());
return ResolvedModuleKeys.file(this, path.toUri(), path);
var local = getLocalUri(dependency, packageAssetUri);
if (local != null) {
return VmContext.get(null).getModuleResolver().resolve(local).resolve(securityManager);
}
var dep = (Dependency.RemoteDependency) dependency;
assert dep.getChecksums() != null;
Expand All @@ -704,10 +703,12 @@ public List<PathElement> listElements(SecurityManager securityManager, URI baseU
var packageAssetUri = PackageAssetUri.create(baseUri);
var dependency =
getProjectDepsResolver().getResolvedDependency(packageAssetUri.getPackageUri());
var path = getLocalPath(dependency);
if (path != null) {
securityManager.checkResolveModule(path.toUri());
return FileResolver.listElements(path);
var local = getLocalUri(dependency, packageAssetUri);
if (local != null) {
return VmContext.get(null)
.getModuleResolver()
.resolve(local)
.listElements(securityManager, local);
}
var dep = (Dependency.RemoteDependency) dependency;
assert dep.getChecksums() != null;
Expand All @@ -721,10 +722,12 @@ public boolean hasElement(SecurityManager securityManager, URI elementUri)
var packageAssetUri = PackageAssetUri.create(elementUri);
var dependency =
getProjectDepsResolver().getResolvedDependency(packageAssetUri.getPackageUri());
var path = getLocalPath(dependency);
if (path != null) {
securityManager.checkResolveModule(path.toUri());
return FileResolver.hasElement(path);
var local = getLocalUri(dependency, packageAssetUri);
if (local != null) {
return VmContext.get(null)
.getModuleResolver()
.resolve(local)
.hasElement(securityManager, local);
}
var dep = (Dependency.RemoteDependency) dependency;
assert dep.getChecksums() != null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,25 @@
*/
package org.pkl.core.module;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import javax.annotation.concurrent.GuardedBy;
import org.graalvm.collections.EconomicMap;
import org.pkl.core.PklBugException;
import org.pkl.core.packages.Dependency;
import org.pkl.core.packages.DependencyMetadata;
import org.pkl.core.packages.PackageLoadError;
import org.pkl.core.packages.PackageUri;
import org.pkl.core.project.CanonicalPackageUri;
import org.pkl.core.project.DeclaredDependencies;
import org.pkl.core.project.ProjectDeps;
import org.pkl.core.resource.Resource;
import org.pkl.core.runtime.VmContext;
import org.pkl.core.runtime.VmExceptionBuilder;
import org.pkl.core.runtime.VmTyped;
import org.pkl.core.util.EconomicMaps;
import org.pkl.core.util.json.Json.JsonParseException;

Expand All @@ -41,7 +43,7 @@ public class ProjectDependenciesManager {
public static final String PKL_PROJECT_DEPS_FILENAME = "PklProject.deps.json";

private final DeclaredDependencies declaredDependencies;
private final Path projectDir;
private final URI projectBaseUri;

@GuardedBy("lock")
private ProjectDeps projectDeps;
Expand All @@ -60,11 +62,14 @@ public class ProjectDependenciesManager {

public ProjectDependenciesManager(DeclaredDependencies declaredDependencies) {
this.declaredDependencies = declaredDependencies;
this.projectDir = Path.of(declaredDependencies.getProjectFileUri()).getParent();
// new URI("scheme://host/a/b/c.txt").resolve(".") == new URI("scheme://host/a/b/")
this.projectBaseUri = declaredDependencies.getProjectFileUri().resolve(".");
}

public boolean hasPath(Path path) {
return path.startsWith(projectDir);
public boolean hasUri(URI uri) {
return projectBaseUri.getScheme().equals(uri.getScheme())
&& Objects.equals(projectBaseUri.getAuthority(), uri.getAuthority())
&& uri.getPath().startsWith(projectBaseUri.getPath());
}

private void ensureDependenciesInitialized() {
Expand Down Expand Up @@ -195,26 +200,45 @@ public Dependency getResolvedDependency(PackageUri packageUri) {
return dep;
}

public Path getProjectDir() {
return projectDir;
public URI getProjectBaseUri() {
return projectBaseUri;
}

public Path getProjectDepsFile() {
return projectDir.resolve(PKL_PROJECT_DEPS_FILENAME);
public URI getProjectDepsFileUri() {
return projectBaseUri.resolve(PKL_PROJECT_DEPS_FILENAME);
}

private ProjectDeps getProjectDeps() {
synchronized (lock) {
if (projectDeps == null) {
var depsPath = getProjectDepsFile();
if (!Files.exists(depsPath)) {
throw new VmExceptionBuilder().evalError("missingProjectDepsJson", projectDir).build();
}
var depsUri = getProjectDepsFileUri();

try {
projectDeps = ProjectDeps.parse(depsPath);
} catch (IOException | URISyntaxException | JsonParseException e) {
var resource = VmContext.get(null).getResourceManager().read(depsUri, null);
if (resource.isEmpty()) {
throw new VmExceptionBuilder()
.evalError("missingProjectDepsJson", projectBaseUri)
.build();
}

var res = resource.get();
if (res instanceof String) {
projectDeps = ProjectDeps.parse((String) res);
} else if (res instanceof VmTyped) {
var resInner = ((VmTyped) res).getExtraStorage();
if (resInner instanceof Resource) {
projectDeps = ProjectDeps.parse(((Resource) resInner).getText());
} else {
// ResourceManager.read() already catches this condition
throw PklBugException.unreachableCode();
}
} else {
// ResourceManager.read() already catches this condition
throw PklBugException.unreachableCode();
}
} catch (URISyntaxException | JsonParseException e) {
throw new VmExceptionBuilder()
.evalError("invalidProjectDepsJson", depsPath, e.getMessage())
.evalError("invalidProjectDepsJson", depsUri, e.getMessage())
.withCause(e)
.build();
}
Expand Down
5 changes: 3 additions & 2 deletions pkl-core/src/main/java/org/pkl/core/packages/Dependency.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package org.pkl.core.packages;

import java.net.URI;
import java.nio.file.Path;
import java.util.Objects;
import org.pkl.core.Version;
Expand Down Expand Up @@ -48,10 +49,10 @@ public Path getPath() {
return path;
}

public Path resolveAssetPath(Path projectDir, PackageAssetUri packageAssetUri) {
public URI resolveAssetUri(URI projectBaseUri, PackageAssetUri packageAssetUri) {
// drop 1 to remove leading `/`
var assetPath = packageAssetUri.getAssetPath().toString().substring(1);
return projectDir.resolve(path).resolve(assetPath);
return projectBaseUri.resolve(path.resolve(assetPath).toString());
}

@Override
Expand Down