some refactor and expanded tables functionality
This commit is contained in:
parent
9170104a12
commit
47a077bffc
@ -25,7 +25,7 @@ public class BuildingIssueSink extends ReadOnlyCollection<BuildingIssue> {
|
||||
return this;
|
||||
}
|
||||
|
||||
public BuildingIssueSink report(final BuildingIssueSink issues) {
|
||||
public BuildingIssueSink merge(final BuildingIssueSink issues) {
|
||||
hasErrors |= issues.hasErrors();
|
||||
collection.addAll(issues.collection);
|
||||
return this;
|
||||
|
||||
@ -3,11 +3,13 @@ package p.studio.compiler.source.tables;
|
||||
import p.studio.compiler.source.identifiers.SourceIdentifier;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
public abstract class DenseTable<IDENTIFIER extends SourceIdentifier, VALUE> {
|
||||
public abstract class DenseTable<IDENTIFIER extends SourceIdentifier, VALUE>
|
||||
implements DenseTableReader<IDENTIFIER, VALUE>, DenseTableWriter<IDENTIFIER, VALUE> {
|
||||
private final List<VALUE> values = new ArrayList<>();
|
||||
private final Function<Integer, IDENTIFIER> identifierGenerator;
|
||||
|
||||
@ -15,19 +17,65 @@ public abstract class DenseTable<IDENTIFIER extends SourceIdentifier, VALUE> {
|
||||
this.identifierGenerator = Objects.requireNonNull(identifierGenerator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int size() { return values.size(); }
|
||||
|
||||
public final VALUE get(IDENTIFIER id) {
|
||||
return values.get(id.getIndex());
|
||||
@Override
|
||||
public final VALUE get(final IDENTIFIER identifier) {
|
||||
return values.get(identifier.getIndex());
|
||||
}
|
||||
|
||||
public IDENTIFIER register(VALUE value) {
|
||||
@Override
|
||||
public IDENTIFIER register(final VALUE value) {
|
||||
final int idx = values.size();
|
||||
values.add(value);
|
||||
return identifierGenerator.apply(idx);
|
||||
}
|
||||
|
||||
protected void clear() {
|
||||
@Override
|
||||
public void clear() {
|
||||
values.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<IDENTIFIER> identifiers() {
|
||||
return () -> new Iterator<>() {
|
||||
|
||||
private int current = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return current < values.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IDENTIFIER next() {
|
||||
return identifierGenerator.apply(current++);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<VALUE> values() {
|
||||
return () -> new Iterator<>() {
|
||||
|
||||
private int current = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return current < values.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VALUE next() {
|
||||
final var identifier = identifierGenerator.apply(current++);
|
||||
return get(identifier);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<IDENTIFIER> iterator() {
|
||||
return identifiers().iterator();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,10 @@
|
||||
package p.studio.compiler.source.tables;
|
||||
|
||||
import p.studio.compiler.source.identifiers.SourceIdentifier;
|
||||
|
||||
public interface DenseTableReader<IDENTIFIER extends SourceIdentifier, VALUE> extends Iterable<IDENTIFIER> {
|
||||
int size();
|
||||
VALUE get(IDENTIFIER identifier);
|
||||
Iterable<IDENTIFIER> identifiers();
|
||||
Iterable<VALUE> values();
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package p.studio.compiler.source.tables;
|
||||
|
||||
import p.studio.compiler.source.identifiers.SourceIdentifier;
|
||||
|
||||
public interface DenseTableWriter<IDENTIFIER extends SourceIdentifier, VALUE> {
|
||||
IDENTIFIER register(VALUE value);
|
||||
void clear();
|
||||
}
|
||||
@ -8,7 +8,7 @@ import p.studio.utilities.structures.ReadOnlyList;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class FileTable extends InternTable<FileId, SourceHandle> {
|
||||
public class FileTable extends InternTable<FileId, SourceHandle> implements FileTableReader {
|
||||
|
||||
private final Map<ProjectId, Set<FileId>> projectFiles = new HashMap<>();
|
||||
|
||||
@ -23,6 +23,7 @@ public class FileTable extends InternTable<FileId, SourceHandle> {
|
||||
return fileId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadOnlyList<FileId> getFiles(final ProjectId projectId) {
|
||||
final var fileIds = projectFiles.get(projectId);
|
||||
if (CollectionUtils.isEmpty(fileIds)) {
|
||||
|
||||
@ -0,0 +1,10 @@
|
||||
package p.studio.compiler.source.tables;
|
||||
|
||||
import p.studio.compiler.models.SourceHandle;
|
||||
import p.studio.compiler.source.identifiers.FileId;
|
||||
import p.studio.compiler.source.identifiers.ProjectId;
|
||||
import p.studio.utilities.structures.ReadOnlyList;
|
||||
|
||||
public interface FileTableReader extends DenseTableReader<FileId, SourceHandle> {
|
||||
ReadOnlyList<FileId> getFiles(ProjectId projectId);
|
||||
}
|
||||
@ -8,11 +8,12 @@ import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
public abstract class InternTable<IDENTIFIER extends SourceIdentifier, VALUE>
|
||||
extends DenseTable<IDENTIFIER, VALUE> {
|
||||
extends DenseTable<IDENTIFIER, VALUE>
|
||||
implements InternTableReader<IDENTIFIER, VALUE> {
|
||||
|
||||
private final Map<VALUE, IDENTIFIER> identifierByValue = new HashMap<>();
|
||||
|
||||
public InternTable(Function<Integer, IDENTIFIER> identifierGenerator) {
|
||||
public InternTable(final Function<Integer, IDENTIFIER> identifierGenerator) {
|
||||
super(identifierGenerator);
|
||||
}
|
||||
|
||||
@ -21,17 +22,19 @@ public abstract class InternTable<IDENTIFIER extends SourceIdentifier, VALUE>
|
||||
return identifierByValue.computeIfAbsent(value, super::register);
|
||||
}
|
||||
|
||||
public Optional<IDENTIFIER> optional(final VALUE value) {
|
||||
return Optional.ofNullable(identifierByValue.get(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
super.clear();
|
||||
identifierByValue.clear();
|
||||
}
|
||||
|
||||
public boolean containsKey(VALUE value) {
|
||||
@Override
|
||||
public Optional<IDENTIFIER> optional(final VALUE value) {
|
||||
return Optional.ofNullable(identifierByValue.get(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(final VALUE value) {
|
||||
return identifierByValue.containsKey(value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,10 @@
|
||||
package p.studio.compiler.source.tables;
|
||||
|
||||
import p.studio.compiler.source.identifiers.SourceIdentifier;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface InternTableReader<IDENTIFIER extends SourceIdentifier, VALUE> extends DenseTableReader<IDENTIFIER, VALUE> {
|
||||
Optional<IDENTIFIER> optional(VALUE value);
|
||||
boolean containsKey(VALUE value);
|
||||
}
|
||||
@ -8,7 +8,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ProjectTable extends DenseTable<ProjectId, ProjectDescriptor> {
|
||||
public class ProjectTable extends DenseTable<ProjectId, ProjectDescriptor> implements ProjectTableReader {
|
||||
|
||||
private final Map<Path, ProjectId> projectIdByPath = new HashMap<>();
|
||||
|
||||
@ -27,11 +27,13 @@ public class ProjectTable extends DenseTable<ProjectId, ProjectDescriptor> {
|
||||
projectIdByPath.clear();
|
||||
}
|
||||
|
||||
public Optional<ProjectDescriptor> optional(Path pathCanon) {
|
||||
@Override
|
||||
public Optional<ProjectDescriptor> optional(final Path pathCanon) {
|
||||
return optionalId(pathCanon).map(this::get);
|
||||
}
|
||||
|
||||
public Optional<ProjectId> optionalId(Path pathCanon) {
|
||||
@Override
|
||||
public Optional<ProjectId> optionalId(final Path pathCanon) {
|
||||
return Optional.ofNullable(projectIdByPath.get(pathCanon));
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
package p.studio.compiler.source.tables;
|
||||
|
||||
import p.studio.compiler.models.ProjectDescriptor;
|
||||
import p.studio.compiler.source.identifiers.ProjectId;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface ProjectTableReader extends DenseTableReader<ProjectId, ProjectDescriptor> {
|
||||
Optional<ProjectDescriptor> optional(Path pathCanon);
|
||||
Optional<ProjectId> optionalId(Path pathCanon);
|
||||
}
|
||||
@ -1,21 +1,57 @@
|
||||
package p.studio.compiler.utilities;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import p.studio.compiler.FrontendRegistryService;
|
||||
import p.studio.compiler.dtos.PrometeuManifestDTO;
|
||||
import p.studio.compiler.messages.BuildingIssueSink;
|
||||
import p.studio.compiler.models.PrometeuManifest;
|
||||
import p.studio.compiler.workspaces.phases.DependencyResolver;
|
||||
import p.studio.utilities.structures.ReadOnlyCollection;
|
||||
import p.studio.utilities.structures.ReadOnlyList;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
|
||||
@UtilityClass
|
||||
public final class PrometeuManifestUtils {
|
||||
public static final PrometeuManifestUtils INSTANCE = new PrometeuManifestUtils();
|
||||
|
||||
private final ObjectMapper mapper = new ObjectMapper();
|
||||
private final static ObjectMapper MAPPER = new ObjectMapper();
|
||||
|
||||
public PrometeuManifestDTO read(Path manifestPath) {
|
||||
static PrometeuManifestDTO read(Path manifestPath) {
|
||||
try {
|
||||
return mapper.readValue(manifestPath.toFile(), PrometeuManifestDTO.class);
|
||||
return MAPPER.readValue(manifestPath.toFile(), PrometeuManifestDTO.class);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("failed to read manifest " + manifestPath, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<PrometeuManifest> buildManifest(
|
||||
final Path mainProjectRootPathCanon,
|
||||
final Path manifestPathCanon,
|
||||
final BuildingIssueSink issues) {
|
||||
final var issuesLocal = BuildingIssueSink.empty();
|
||||
final PrometeuManifestDTO dto = read(manifestPathCanon);
|
||||
if (StringUtils.isBlank(dto.name())) {
|
||||
issuesLocal.report(builder -> builder
|
||||
.error(true)
|
||||
.message("[DEPS]: manifest missing 'name': " + manifestPathCanon));
|
||||
}
|
||||
if (StringUtils.isBlank(dto.version())) {
|
||||
issuesLocal.report(builder -> builder
|
||||
.error(true)
|
||||
.message("[DEPS]: manifest missing 'version': " + manifestPathCanon));
|
||||
}
|
||||
final var language = StringUtils.isBlank(dto.language())
|
||||
? FrontendRegistryService.getDefaultFrontendSpec().getLanguageId()
|
||||
: dto.language();
|
||||
final var dependencies = DependencyResolver.resolveDependencies(mainProjectRootPathCanon, dto.dependencies(), issuesLocal);
|
||||
if (ReadOnlyCollection.isNotEmpty(issuesLocal)) {
|
||||
issues.merge(issuesLocal);
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(new PrometeuManifest(dto.name(), dto.version(), language, ReadOnlyList.wrap(dependencies)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,86 @@
|
||||
package p.studio.compiler.workspaces.phases;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import p.studio.compiler.dtos.PrometeuManifestDTO;
|
||||
import p.studio.compiler.messages.BuildingIssueSink;
|
||||
import p.studio.compiler.workspaces.DependencyReference;
|
||||
import p.studio.registry.models.ProjectRef;
|
||||
import p.studio.registry.models.Registry;
|
||||
import p.studio.registry.utilities.RegistryStore;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@UtilityClass
|
||||
public final class DependencyResolver {
|
||||
public static List<DependencyReference> resolveDependencies(
|
||||
final Path mainProjectRootPathCanon,
|
||||
final List<PrometeuManifestDTO.DependencyDeclaration> dependencies,
|
||||
final BuildingIssueSink issues) {
|
||||
if (CollectionUtils.isEmpty(dependencies)) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
final var registryMaybe = RegistryStore.INSTANCE.load(mainProjectRootPathCanon);
|
||||
if (registryMaybe.isEmpty()) {
|
||||
issues.report(builder -> builder
|
||||
.error(true)
|
||||
.message("[DEPS]: failed to load registry from " + mainProjectRootPathCanon));
|
||||
return List.of();
|
||||
}
|
||||
final var registry = registryMaybe.get();
|
||||
|
||||
final var deps = new ArrayList<DependencyReference>(dependencies.size());
|
||||
for (var dependency : dependencies) {
|
||||
|
||||
// Resolves each dependency based on its declaration type
|
||||
switch (dependency) {
|
||||
case PrometeuManifestDTO.DependencyDeclaration.Local loc -> resolveLocal(loc, mainProjectRootPathCanon, issues, deps);
|
||||
case PrometeuManifestDTO.DependencyDeclaration.Registry reg -> resolveRegistry(reg, registry, issues, deps);
|
||||
default -> issues.report(builder -> builder
|
||||
.error(true)
|
||||
.message("[DEPS]: unknown dependency declaration type: " + dependency.getClass().getSimpleName()));
|
||||
}
|
||||
}
|
||||
|
||||
return deps;
|
||||
}
|
||||
|
||||
// Resolves local dependency path; reports canonicalization errors
|
||||
private static void resolveLocal(
|
||||
final PrometeuManifestDTO.DependencyDeclaration.Local loc,
|
||||
final Path rootProjectCanonPath,
|
||||
final BuildingIssueSink issues,
|
||||
final List<DependencyReference> deps) {
|
||||
final var pathResolve = rootProjectCanonPath.resolve(loc.path());
|
||||
try {
|
||||
final Path pathCanon = pathResolve.toRealPath();
|
||||
deps.add(new DependencyReference(pathCanon));
|
||||
} catch (IOException e) {
|
||||
issues.report(builder -> builder
|
||||
.error(true)
|
||||
.message("[DEPS]: failed to canonicalize dependency path: " + pathResolve)
|
||||
.exception(e));
|
||||
}
|
||||
}
|
||||
|
||||
// Looks up the registry for a dependency path; reports missing path errors
|
||||
private static void resolveRegistry(
|
||||
final PrometeuManifestDTO.DependencyDeclaration.Registry reg,
|
||||
final Registry registry,
|
||||
final BuildingIssueSink issues,
|
||||
final List<DependencyReference> deps) {
|
||||
final var ref = new ProjectRef(reg.name(), reg.version());
|
||||
final var canonPath = registry.optional(ref);
|
||||
if (canonPath.isEmpty()) {
|
||||
issues.report(builder -> builder
|
||||
.error(true)
|
||||
.message("[DEPS]: registry dependency not found: " + ref));
|
||||
return;
|
||||
}
|
||||
deps.add(new DependencyReference(canonPath.get()));
|
||||
}
|
||||
}
|
||||
@ -1,180 +1,106 @@
|
||||
package p.studio.compiler.workspaces.phases;
|
||||
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import p.studio.compiler.FrontendRegistryService;
|
||||
import p.studio.compiler.dtos.PrometeuManifestDTO;
|
||||
import p.studio.compiler.messages.BuildingIssueSink;
|
||||
import p.studio.compiler.models.DependencyContext;
|
||||
import p.studio.compiler.models.ProjectInfo;
|
||||
import p.studio.compiler.models.ProjectInfoId;
|
||||
import p.studio.compiler.models.PrometeuManifest;
|
||||
import p.studio.compiler.utilities.PrometeuManifestUtils;
|
||||
import p.studio.compiler.workspaces.DependencyPhase;
|
||||
import p.studio.compiler.workspaces.DependencyReference;
|
||||
import p.studio.registry.models.ProjectRef;
|
||||
import p.studio.registry.utilities.RegistryStore;
|
||||
import p.studio.utilities.structures.ReadOnlyCollection;
|
||||
import p.studio.utilities.structures.ReadOnlyList;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
|
||||
public class DiscoverPhase implements DependencyPhase {
|
||||
@Override
|
||||
public BuildingIssueSink run(final DependencyContext ctx) {
|
||||
final Map<Path, ProjectInfoId> projectIndexByDirectory = new HashMap<>();
|
||||
final BuildingIssueSink issues = BuildingIssueSink.empty();
|
||||
final var projectsSeen = new HashSet<Path>();
|
||||
final var issues = BuildingIssueSink.empty();
|
||||
// Discovers projects; registers them; adds dependencies to queue
|
||||
while (!ctx.pending.isEmpty()) {
|
||||
final var rootPathCanon = ctx.pending.pollFirst();
|
||||
|
||||
if (projectIndexByDirectory.containsKey(rootPathCanon)) {
|
||||
if (projectsSeen.contains(rootPathCanon)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final Path manifestPathCanon;
|
||||
try {
|
||||
manifestPathCanon = rootPathCanon.resolve("prometeu.json").toRealPath();
|
||||
} catch (IOException e) {
|
||||
issues.report(builder -> builder
|
||||
.error(true)
|
||||
.message("[DEPS]: manifest canonPath does not exist: " + rootPathCanon)
|
||||
.exception(e));
|
||||
continue;
|
||||
}
|
||||
if (!Files.exists(manifestPathCanon) || !Files.isRegularFile(manifestPathCanon)) {
|
||||
issues.report(builder -> builder
|
||||
.error(true)
|
||||
.message("[DEPS]: manifest not found: expected a file " + manifestPathCanon));
|
||||
continue;
|
||||
}
|
||||
|
||||
final var prometeuManifestDTO = PrometeuManifestUtils.INSTANCE.read(manifestPathCanon);
|
||||
final var manifestMaybe = map(ctx.mainProjectRootPathCanon, prometeuManifestDTO, issues);
|
||||
|
||||
if (manifestMaybe.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final var manifest = manifestMaybe.get();
|
||||
|
||||
final var frontendSpec = FrontendRegistryService.getFrontendSpec(manifest.language());
|
||||
if (frontendSpec.isEmpty()) {
|
||||
issues.report(builder -> builder
|
||||
.error(true)
|
||||
.message("[DEPS]: unknown language " + manifest.language() + " for project " + manifest.name()));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Objects.isNull(ctx.frontendSpec)) {
|
||||
ctx.frontendSpec = frontendSpec.get();
|
||||
} else if (!ctx.frontendSpec.getLanguageId().equals(frontendSpec.get().getLanguageId())) {
|
||||
issues.report(builder -> builder
|
||||
.error(true)
|
||||
.message(String.format("[DEPS]: inconsistent language: [ %s ] has \"%s\" but should be \"%s\"",
|
||||
manifest.name(), frontendSpec.get().getLanguageId(), ctx.frontendSpec.getLanguageId())));
|
||||
continue;
|
||||
}
|
||||
|
||||
final var projectInfo = ProjectInfo
|
||||
.builder()
|
||||
.rootDirectory(rootPathCanon)
|
||||
.manifestPath(manifestPathCanon)
|
||||
.manifest(manifest)
|
||||
.build();
|
||||
final var projectInfoId = ctx.projectInfoTable.register(projectInfo);
|
||||
projectIndexByDirectory.put(rootPathCanon, projectInfoId);
|
||||
|
||||
manifest.dependencies().forEach(depRef -> ctx.pending.add(depRef.canonPath()));
|
||||
|
||||
ctx.projectNameAndVersions.computeIfAbsent(manifest.name(), ignore -> new HashSet<>()).add(manifest.version());
|
||||
discoverOne(ctx, rootPathCanon, issues).ifPresent(projectInfoId -> {
|
||||
projectsSeen.add(rootPathCanon);
|
||||
final var projectInfo = ctx.projectInfoTable.get(projectInfoId);
|
||||
projectInfo.manifest.dependencies().forEach(dep -> ctx.pending.add(dep.canonPath()));
|
||||
ctx.projectNameAndVersions
|
||||
.computeIfAbsent(projectInfo.manifest.name(), ignored -> new HashSet<>())
|
||||
.add(projectInfo.manifest.version());
|
||||
});
|
||||
}
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
||||
public static Optional<PrometeuManifest> map(
|
||||
final Path rootPath,
|
||||
final PrometeuManifestDTO dto,
|
||||
private Optional<ProjectInfoId> discoverOne(
|
||||
final DependencyContext ctx,
|
||||
final Path rootPathCanon,
|
||||
final BuildingIssueSink issues) {
|
||||
|
||||
if (StringUtils.isBlank(dto.name())) {
|
||||
issues.report(builder -> builder
|
||||
.error(true)
|
||||
.message("[DEPS]: manifest missing 'name': " + rootPath));
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(dto.version())) {
|
||||
issues.report(builder -> builder
|
||||
.error(true)
|
||||
.message("[DEPS]: manifest missing 'version': " + rootPath));
|
||||
}
|
||||
|
||||
final var language = StringUtils.isBlank(dto.language())
|
||||
? FrontendRegistryService.getDefaultFrontendSpec().getLanguageId()
|
||||
: dto.language();
|
||||
|
||||
final var dependencies = resolveDependencies(rootPath, dto.dependencies(), issues);
|
||||
|
||||
if (ReadOnlyCollection.isNotEmpty(issues)) {
|
||||
final var manifestPathCanon = canonicalizeManifestPath(rootPathCanon, issues);
|
||||
if (manifestPathCanon.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(new PrometeuManifest(dto.name(), dto.version(), language, ReadOnlyList.wrap(dependencies)));
|
||||
}
|
||||
|
||||
private static List<DependencyReference> resolveDependencies(
|
||||
final Path rootProjectCanonPath,
|
||||
final List<PrometeuManifestDTO.DependencyDeclaration> dependencies,
|
||||
final BuildingIssueSink issues) {
|
||||
if (CollectionUtils.isEmpty(dependencies)) {
|
||||
return List.of();
|
||||
final var manifest = PrometeuManifestUtils.buildManifest(ctx.mainProjectRootPathCanon, manifestPathCanon.get(), issues);
|
||||
if (manifest.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
final var registryMaybe = RegistryStore.INSTANCE.load(rootProjectCanonPath);
|
||||
if (registryMaybe.isEmpty()) {
|
||||
final var frontendSpec = FrontendRegistryService.getFrontendSpec(manifest.get().language());
|
||||
// Returns empty when language is unknown
|
||||
if (frontendSpec.isEmpty()) {
|
||||
issues.report(builder -> builder
|
||||
.error(true)
|
||||
.message("[DEPS]: failed to load registry from " + rootProjectCanonPath));
|
||||
return List.of();
|
||||
.message("[DEPS]: unknown language " + manifest.get().language() + " for project " + manifest.get().name()));
|
||||
return Optional.empty();
|
||||
}
|
||||
final var registry = registryMaybe.get();
|
||||
|
||||
final var deps = new ArrayList<DependencyReference>(dependencies.size());
|
||||
for (var dependency : dependencies) {
|
||||
|
||||
// Resolves each dependency based on its declaration type
|
||||
switch (dependency) {
|
||||
|
||||
case PrometeuManifestDTO.DependencyDeclaration.Local loc -> {
|
||||
// Resolves local dependency path; reports canonicalization errors
|
||||
final var pathResolve = rootProjectCanonPath.resolve(loc.path());
|
||||
try {
|
||||
final Path pathCanon = pathResolve.toRealPath();
|
||||
deps.add(new DependencyReference(pathCanon));
|
||||
} catch (IOException e) {
|
||||
issues.report(builder -> builder
|
||||
.error(true)
|
||||
.message("[DEPS]: failed to canonicalize dependency path: " + pathResolve)
|
||||
.exception(e));
|
||||
}
|
||||
}
|
||||
|
||||
case PrometeuManifestDTO.DependencyDeclaration.Registry reg -> {
|
||||
// Looks up the registry for a dependency path; reports missing path errors
|
||||
final var ref = new ProjectRef(reg.name(), reg.version());
|
||||
registry.optional(ref).ifPresent(pathCanon -> deps.add(new DependencyReference(pathCanon)));
|
||||
}
|
||||
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
// Enforces consistent language across projects
|
||||
if (Objects.isNull(ctx.frontendSpec)) {
|
||||
ctx.frontendSpec = frontendSpec.get();
|
||||
} else if (!ctx.frontendSpec.getLanguageId().equals(frontendSpec.get().getLanguageId())) {
|
||||
// Reports language inconsistency across projects
|
||||
issues.report(builder -> builder
|
||||
.error(true)
|
||||
.message(String.format("[DEPS]: inconsistent language: [ %s ] has \"%s\" but should be \"%s\"",
|
||||
manifest.get().name(), frontendSpec.get().getLanguageId(), ctx.frontendSpec.getLanguageId())));
|
||||
return Optional.empty();
|
||||
}
|
||||
final var projectInfo = ProjectInfo
|
||||
.builder()
|
||||
.rootDirectory(rootPathCanon)
|
||||
.manifestPath(manifestPathCanon.get())
|
||||
.manifest(manifest.get())
|
||||
.build();
|
||||
final var projectInfoId = ctx.projectInfoTable.register(projectInfo);
|
||||
return Optional.of(projectInfoId);
|
||||
}
|
||||
|
||||
return deps;
|
||||
private static Optional<Path> canonicalizeManifestPath(
|
||||
final Path rootPathCanon,
|
||||
final BuildingIssueSink issues) {
|
||||
final var manifestPath = rootPathCanon.resolve("prometeu.json");
|
||||
final Path manifestPathCanon;
|
||||
// Resolves manifest path; reports errors on failure
|
||||
try {
|
||||
manifestPathCanon = manifestPath.toRealPath();
|
||||
} catch (IOException e) {
|
||||
issues.report(builder -> builder
|
||||
.error(true)
|
||||
.message("[DEPS]: manifest canonPath does not exist: " + manifestPath)
|
||||
.exception(e));
|
||||
return Optional.empty();
|
||||
}
|
||||
if (!Files.isRegularFile(manifestPathCanon)) {
|
||||
issues.report(builder -> builder
|
||||
.error(true)
|
||||
.message("[DEPS]: manifest not found: expected a file " + manifestPathCanon));
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(manifestPathCanon);
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,8 +25,7 @@ public final class WireProjectsPhase implements DependencyPhase {
|
||||
|
||||
final BuildingIssueSink issues = BuildingIssueSink.empty();
|
||||
|
||||
for (int index = 0; index < ctx.projectInfoTable.size(); index++) {
|
||||
final var projectInfo = ctx.projectInfoTable.get(new ProjectInfoId(index));
|
||||
for (final var projectInfo : ctx.projectInfoTable.values()) {
|
||||
final var projectDescriptor = buildProjectDescriptor(ctx, projectInfo, issues);
|
||||
final var projectId = ctx.projectTable.register(projectDescriptor);
|
||||
ctx.projectIds.add(projectId);
|
||||
@ -82,7 +81,7 @@ public final class WireProjectsPhase implements DependencyPhase {
|
||||
}
|
||||
if (sourceRootIssues.size() == ctx.frontendSpec.getSourceRoots().size()) {
|
||||
// no source roots were found at all
|
||||
issues.report(sourceRootIssues);
|
||||
issues.merge(sourceRootIssues);
|
||||
}
|
||||
|
||||
return ProjectDescriptor
|
||||
|
||||
@ -10,4 +10,9 @@ public record ProjectRef(String name, String version) {
|
||||
final var version = ps[1];
|
||||
return Optional.of(new ProjectRef(name, version));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name + "@" + version;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user