From f627914c9f56f54bf0a235e9302aa40d8086ee5e Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Wed, 25 Feb 2026 08:40:30 +0000 Subject: [PATCH] added Load into compiler pipeline --- gradle/libs.versions.toml | 2 +- .../p/studio/compiler/PBSDefinitions.java | 2 +- .../models/BuilderPipelineContext.java | 16 +- .../workspaces/BuilderPipelineService.java | 6 +- .../workspaces/stages/LoadPipelineStage.java | 145 ++++++++++++++++++ .../stages/ResolvePipelineStage.java | 7 +- .../compiler/models/ProjectDescriptor.java | 6 +- .../studio/compiler/models/SourceHandle.java | 49 ++++++ .../p/studio/compiler/source/SourceFile.java | 15 -- .../compiler/source/tables/FileTable.java | 13 +- .../compiler/utilities/SourceProvider.java | 8 + .../utilities/SourceProviderFactory.java | 28 ++++ .../compiler/models/ResolvedWorkspace.java | 9 +- prometeu-infra/build.gradle.kts | 1 + .../utilities/structures/ReadOnlyList.java | 6 +- test-projects/main/src/main.pbs | 0 test-projects/main/src/module-a/mod.barrel | 0 test-projects/main/src/module-a/source-1.pbs | 0 test-projects/main/src/module-a/source-2.pbs | 0 test-projects/main/src/module-a/source-3.pbs | 0 test-projects/main/src/module-b/mod.barrel | 0 test-projects/main/src/module-b/source-1.pbs | 0 test-projects/main/src/module-b/source-2.pbs | 0 test-projects/main/src/module-b/source-3.pbs | 0 test-projects/main/src/module-c/mod.barrel | 0 test-projects/main/src/module-c/source-1.pbs | 0 test-projects/main/src/module-c/source-2.pbs | 0 test-projects/main/src/module-c/source-3.pbs | 0 28 files changed, 269 insertions(+), 44 deletions(-) create mode 100644 prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/workspaces/stages/LoadPipelineStage.java create mode 100644 prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/models/SourceHandle.java delete mode 100644 prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/source/SourceFile.java create mode 100644 prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/utilities/SourceProvider.java create mode 100644 prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/utilities/SourceProviderFactory.java create mode 100644 test-projects/main/src/main.pbs create mode 100644 test-projects/main/src/module-a/mod.barrel create mode 100644 test-projects/main/src/module-a/source-1.pbs create mode 100644 test-projects/main/src/module-a/source-2.pbs create mode 100644 test-projects/main/src/module-a/source-3.pbs create mode 100644 test-projects/main/src/module-b/mod.barrel create mode 100644 test-projects/main/src/module-b/source-1.pbs create mode 100644 test-projects/main/src/module-b/source-2.pbs create mode 100644 test-projects/main/src/module-b/source-3.pbs create mode 100644 test-projects/main/src/module-c/mod.barrel create mode 100644 test-projects/main/src/module-c/source-1.pbs create mode 100644 test-projects/main/src/module-c/source-2.pbs create mode 100644 test-projects/main/src/module-c/source-3.pbs diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7b632744..f962385c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ javafx-controls = { group = "org.openjfx", name = "javafx-controls", version.ref javafx-fxml = { group = "org.openjfx", name = "javafx-fxml", version.ref = "javafx" } richtextfx = { group = "org.fxmisc.richtext", name = "richtextfx", version.ref = "richtextfx" } jackson-databind = { group = "com.fasterxml.jackson.core", name = "jackson-databind", version.ref = "jackson" } -apache-commons-lang3 = { group = "org.apache.commons", name = "commons-lang3", version = "3.13.0" } +apache-commons-lang3 = { group = "org.apache.commons", name = "commons-lang3", version = "3.18.0" } apache-commons-io = { group = "commons-io", name = "commons-io", version = "2.13.0" } apache-commons-collections = { group = "org.apache.commons", name = "commons-collections4", version = "4.4" } diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/PBSDefinitions.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/PBSDefinitions.java index 2914eec5..faaf199b 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/PBSDefinitions.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/PBSDefinitions.java @@ -7,7 +7,7 @@ public class PBSDefinitions { public static final FrontendSpec PBS = FrontendSpec .builder() .languageId("pbs") - .allowedExtensions(ReadOnlySet.from("pbs")) + .allowedExtensions(ReadOnlySet.from("pbs", "barrel")) .sourceRoots(ReadOnlySet.from("src")) .build(); } diff --git a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/models/BuilderPipelineContext.java b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/models/BuilderPipelineContext.java index 9fef890a..b2499fc5 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/models/BuilderPipelineContext.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/models/BuilderPipelineContext.java @@ -1,18 +1,28 @@ package p.studio.compiler.models; import p.studio.compiler.messages.BuilderPipelineConfig; +import p.studio.compiler.source.tables.FileTable; +import p.studio.compiler.utilities.SourceProviderFactory; + +import java.nio.file.Path; public class BuilderPipelineContext { public final BuilderPipelineConfig config; + public final SourceProviderFactory sourceProviderFactory; + public Path rootProjectPathCanon; public ResolvedWorkspace resolvedWorkspace; + public final FileTable fileTable = new FileTable(); + private BuilderPipelineContext( - final BuilderPipelineConfig config) { + final BuilderPipelineConfig config, + final SourceProviderFactory factory) { this.config = config; + this.sourceProviderFactory = factory; } - public static BuilderPipelineContext basedOn(BuilderPipelineConfig config) { - return new BuilderPipelineContext(config); + public static BuilderPipelineContext compilerContext(final BuilderPipelineConfig config) { + return new BuilderPipelineContext(config, SourceProviderFactory.filesystem()); } } diff --git a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/workspaces/BuilderPipelineService.java b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/workspaces/BuilderPipelineService.java index ef29c13c..578f21f9 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/workspaces/BuilderPipelineService.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/workspaces/BuilderPipelineService.java @@ -4,6 +4,7 @@ import lombok.extern.slf4j.Slf4j; import p.studio.compiler.exceptions.BuildException; import p.studio.compiler.messages.BuilderPipelineConfig; import p.studio.compiler.models.BuilderPipelineContext; +import p.studio.compiler.workspaces.stages.LoadPipelineStage; import p.studio.compiler.workspaces.stages.ResolvePipelineStage; import p.studio.utilities.logs.LogAggregator; import p.studio.utilities.structures.ReadOnlyCollection; @@ -16,7 +17,8 @@ public class BuilderPipelineService { static { final var stages = List.of( - new ResolvePipelineStage() + new ResolvePipelineStage(), + new LoadPipelineStage() ); INSTANCE = new BuilderPipelineService(stages); } @@ -30,7 +32,7 @@ public class BuilderPipelineService { public void run( final BuilderPipelineConfig config, final LogAggregator logs) { - final var ctx = BuilderPipelineContext.basedOn(config); + final var ctx = BuilderPipelineContext.compilerContext(config); for (final var builderPipelineStage : stages) { final var issues = builderPipelineStage.run(ctx, logs); diff --git a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/workspaces/stages/LoadPipelineStage.java b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/workspaces/stages/LoadPipelineStage.java new file mode 100644 index 00000000..93145af5 --- /dev/null +++ b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/workspaces/stages/LoadPipelineStage.java @@ -0,0 +1,145 @@ +package p.studio.compiler.workspaces.stages; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; +import p.studio.compiler.messages.BuildingIssues; +import p.studio.compiler.models.BuilderPipelineContext; +import p.studio.compiler.models.SourceHandle; +import p.studio.compiler.workspaces.PipelineStage; +import p.studio.utilities.logs.LogAggregator; +import p.studio.utilities.structures.ReadOnlySet; + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +@Slf4j +public class LoadPipelineStage implements PipelineStage { + + @Override + public BuildingIssues run(final BuilderPipelineContext ctx, final LogAggregator logs) { + final var issues = BuildingIssues.empty(); + + // Iterates projects; "loads sources"; registers files + ctx.resolvedWorkspace.topologicalOrder().forEach(pId -> { + final var pd = ctx.resolvedWorkspace.graph().projectTable().get(pId); + logs.using(log).info("Project [ " + pd.getName() + " ] source loading..."); + + final var allowedExtensions = normalize(pd.getFrontendSpec().getAllowedExtensions()); + for (final var sourceRootPath : pd.getSourceRoots()) { + logs.using(log).debug("Walking source root [ " + sourceRootPath + " ]"); + + try { + final List paths = new ArrayList<>(); + Files.walkFileTree(sourceRootPath, new SourceCrawler(allowedExtensions, paths)); + paths.sort(Path::compareTo); + for (var path : paths) { + logs.using(log).debug("file tabling [ " + path + " ]"); + final var attributes = Files.readAttributes(path, BasicFileAttributes.class); + final var size = attributes.size(); + final var lastModified = attributes.lastModifiedTime().toMillis(); + final var rawFile = new SourceHandle(pId, path, size, lastModified, ctx.sourceProviderFactory); + // register in dense tables + var fileId = ctx.fileTable.register(rawFile); + } + } catch (IOException e) { + issues.add(builder -> builder + .error(true) + .message("Failed to load project [ " + pd.getName() + " ]") + .exception(e)); + } + } + }); + return BuildingIssues.empty(); + } + + private static ReadOnlySet normalize(final ReadOnlySet extensions) { + return ReadOnlySet + .wrap(extensions + .map(String::toLowerCase) + .map(s -> s.startsWith(".") ? s.substring(1) : s) + .collect(Collectors.toSet())); + } + + private static final class SourceCrawler extends SimpleFileVisitor { + + private static final Set IGNORED_DIRS = Set.of( + ".git", + ".prometeu", + ".workspace", + "target", + "build", + "out", + "node_modules" + ); + + private final ReadOnlySet allowedExtensions; + private final List paths; + + public SourceCrawler( + final ReadOnlySet allowedExtensions, + final List paths) { + this.allowedExtensions = allowedExtensions; + this.paths = paths; + } + + @Override + public FileVisitResult preVisitDirectory(final Path path, final BasicFileAttributes attrs) { + for (final var directory : path) { + if (!isAllowedPath(directory)) { + return FileVisitResult.SKIP_SUBTREE; + } + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(final Path path, final BasicFileAttributes attrs) { + if (!attrs.isRegularFile()) { + return FileVisitResult.CONTINUE; + } + + if (hasAllowedExt(path, allowedExtensions)) { + paths.add(path); + } + + return FileVisitResult.CONTINUE; + } + + private static boolean isAllowedPath( + final Path path) { + for (Path part : path) { + if (IGNORED_DIRS.contains(part.toString())) { + return false; + } + } + return true; + } + + private static boolean hasAllowedExt( + final Path path, + final ReadOnlySet allowedExtensions) { + + final var fileName = path.getFileName(); + if (Objects.isNull(fileName)) { + return false; + } + + final var extension = FilenameUtils.getExtension(fileName.toString()).toLowerCase(); + if (StringUtils.isBlank(extension)) { + return false; + } + + return allowedExtensions.contains(extension); + } + } +} diff --git a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/workspaces/stages/ResolvePipelineStage.java b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/workspaces/stages/ResolvePipelineStage.java index bbecddc7..6ccb7dc6 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/workspaces/stages/ResolvePipelineStage.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/workspaces/stages/ResolvePipelineStage.java @@ -9,16 +9,14 @@ import p.studio.compiler.workspaces.PipelineStage; import p.studio.utilities.logs.LogAggregator; import java.io.IOException; -import java.nio.file.Path; import java.nio.file.Paths; @Slf4j public class ResolvePipelineStage implements PipelineStage { @Override public BuildingIssues run(final BuilderPipelineContext ctx, LogAggregator logs) { - final Path rootCanonPath; try { - rootCanonPath = Paths.get(ctx.config.rootProjectPath()).toRealPath(); + ctx.rootProjectPathCanon = Paths.get(ctx.config.rootProjectPath()).toRealPath(); } catch (IOException e) { return BuildingIssues.empty() .add(builder -> builder @@ -26,9 +24,8 @@ public class ResolvePipelineStage implements PipelineStage { .message("[BUILD]: root project directory no found: " + ctx.config.rootProjectPath()) .exception(e)); } - final var dependencyConfig = new DependencyConfig(ctx.config.explain(), rootCanonPath); + final var dependencyConfig = new DependencyConfig(ctx.config.explain(), ctx.rootProjectPathCanon); ctx.resolvedWorkspace = DependencyService.INSTANCE.run(dependencyConfig, logs); - ctx.resolvedWorkspace.topologicalOrder().forEach(pd -> logs.using(log).info("Project [ " + pd.getName() + " ] read")); return BuildingIssues.empty(); } } diff --git a/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/models/ProjectDescriptor.java b/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/models/ProjectDescriptor.java index 63bc8f13..8884f730 100644 --- a/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/models/ProjectDescriptor.java +++ b/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/models/ProjectDescriptor.java @@ -9,9 +9,9 @@ import java.nio.file.Path; @Builder @Getter public final class ProjectDescriptor { - private final Path rootPath; - private final String name; - private final String version; + private final Path rootPath; // canon root path + private final String name; // project name + private final String version; // project version private final ReadOnlyList sourceRoots; private final FrontendSpec frontendSpec; } diff --git a/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/models/SourceHandle.java b/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/models/SourceHandle.java new file mode 100644 index 00000000..b40ab24c --- /dev/null +++ b/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/models/SourceHandle.java @@ -0,0 +1,49 @@ +package p.studio.compiler.models; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import p.studio.compiler.source.identifiers.ProjectId; +import p.studio.compiler.utilities.SourceProvider; +import p.studio.compiler.utilities.SourceProviderFactory; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; + +@EqualsAndHashCode(onlyExplicitlyIncluded = true) +public class SourceHandle { + @Getter + private final ProjectId projectId; + @EqualsAndHashCode.Include + @Getter + private final Path path; // canon path to file + @Getter + private final String filename; + @Getter + private final long size; + @Getter + private final long lastModified; + private final SourceProvider provider; + + public SourceHandle( + final ProjectId projectId, + final Path path, + final long size, + final long lastModified, + final SourceProviderFactory factory) { + this.projectId = projectId; + this.path = path; + this.filename = path.getFileName().toString(); + this.size = size; + this.lastModified = lastModified; + this.provider = factory.create(path); + } + + public byte[] readBytes() throws IOException { + return provider.read(); + } + + public String readUtf8() throws IOException { + return new String(readBytes(), StandardCharsets.UTF_8); + } +} \ No newline at end of file diff --git a/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/source/SourceFile.java b/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/source/SourceFile.java deleted file mode 100644 index 9d8e83e2..00000000 --- a/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/source/SourceFile.java +++ /dev/null @@ -1,15 +0,0 @@ -package p.studio.compiler.source; - -import lombok.Builder; -import lombok.Getter; -import p.studio.compiler.source.identifiers.ProjectId; - -@Builder -@Getter -public class SourceFile { - private final ProjectId projectId; - private final String module; - private final String name; - private final String extension; - private final byte[] content; -} diff --git a/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/source/tables/FileTable.java b/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/source/tables/FileTable.java index b3754884..8b3b573d 100644 --- a/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/source/tables/FileTable.java +++ b/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/source/tables/FileTable.java @@ -1,13 +1,14 @@ package p.studio.compiler.source.tables; import org.apache.commons.collections4.CollectionUtils; -import p.studio.compiler.source.SourceFile; +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; import java.util.*; -public class FileTable extends InternTable { +public class FileTable extends InternTable { private final Map> projectFiles = new HashMap<>(); @@ -16,17 +17,17 @@ public class FileTable extends InternTable { } @Override - public FileId register(final SourceFile value) { + public FileId register(final SourceHandle value) { final var fileId = super.register(value); projectFiles.computeIfAbsent(value.getProjectId(), ignored -> new HashSet<>()).add(fileId); return fileId; } - public List getSourceFiles(final ProjectId projectId) { + public ReadOnlyList getFiles(final ProjectId projectId) { final var fileIds = projectFiles.get(projectId); if (CollectionUtils.isEmpty(fileIds)) { - return List.of(); + return ReadOnlyList.empty(); } - return fileIds.stream().map(this::get).toList(); + return ReadOnlyList.wrap(fileIds); } } \ No newline at end of file diff --git a/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/utilities/SourceProvider.java b/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/utilities/SourceProvider.java new file mode 100644 index 00000000..2701d83c --- /dev/null +++ b/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/utilities/SourceProvider.java @@ -0,0 +1,8 @@ +package p.studio.compiler.utilities; + +import java.io.IOException; + +@FunctionalInterface +public interface SourceProvider { + byte[] read() throws IOException; +} diff --git a/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/utilities/SourceProviderFactory.java b/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/utilities/SourceProviderFactory.java new file mode 100644 index 00000000..852f0425 --- /dev/null +++ b/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/utilities/SourceProviderFactory.java @@ -0,0 +1,28 @@ +package p.studio.compiler.utilities; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; + +@FunctionalInterface +public interface SourceProviderFactory { + SourceProvider create(Path path); + + static SourceProviderFactory filesystem() { + return path -> new SourceProvider() { + + private volatile byte[] cachedBytes = null; + + @Override + public byte[] read() throws IOException { + if (Objects.isNull(cachedBytes)) { + cachedBytes = Files.readAllBytes(path); + } + return cachedBytes; + } + }; + } + + // more providers can be added here, e.g., in-memory, etc. +} diff --git a/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/models/ResolvedWorkspace.java b/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/models/ResolvedWorkspace.java index d56831de..4c06117b 100644 --- a/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/models/ResolvedWorkspace.java +++ b/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/models/ResolvedWorkspace.java @@ -8,15 +8,16 @@ public record ResolvedWorkspace( ProjectId projectId, WorkspaceGraph graph, BuildStack stack) { + public ProjectDescriptor mainProject() { return graph.projectDescriptor(projectId); } - public Stream topologicalOrder() { - return stack.topologicalOrder.stream().map(graph::projectDescriptor); + public Stream topologicalOrder() { + return stack.topologicalOrder.stream(); } - public Stream reverseTopologicalOrder() { - return stack.reverseTopologicalOrder.stream().map(graph::projectDescriptor); + public Stream reverseTopologicalOrder() { + return stack.reverseTopologicalOrder.stream(); } } diff --git a/prometeu-infra/build.gradle.kts b/prometeu-infra/build.gradle.kts index bac3e7f0..acf08c26 100644 --- a/prometeu-infra/build.gradle.kts +++ b/prometeu-infra/build.gradle.kts @@ -5,5 +5,6 @@ plugins { dependencies { api(libs.jackson.databind) api(libs.apache.commons.lang3) + api(libs.apache.commons.io) api(libs.apache.commons.collections) } \ No newline at end of file diff --git a/prometeu-infra/src/main/java/p/studio/utilities/structures/ReadOnlyList.java b/prometeu-infra/src/main/java/p/studio/utilities/structures/ReadOnlyList.java index 73f47fac..97a55eec 100644 --- a/prometeu-infra/src/main/java/p/studio/utilities/structures/ReadOnlyList.java +++ b/prometeu-infra/src/main/java/p/studio/utilities/structures/ReadOnlyList.java @@ -1,7 +1,5 @@ package p.studio.utilities.structures; -import org.apache.commons.collections4.ListUtils; - import java.util.*; import java.util.stream.Stream; @@ -23,8 +21,8 @@ public class ReadOnlyList extends ReadOnlyCollection { return ReadOnlyList.wrap(new ArrayList<>(c.collection)); } - public static ReadOnlyCollection wrap(final Set set) { - return new ReadOnlyCollection<>(List.copyOf(set)); + public static ReadOnlyList wrap(final Set set) { + return new ReadOnlyList<>(List.copyOf(set)); } @SafeVarargs diff --git a/test-projects/main/src/main.pbs b/test-projects/main/src/main.pbs new file mode 100644 index 00000000..e69de29b diff --git a/test-projects/main/src/module-a/mod.barrel b/test-projects/main/src/module-a/mod.barrel new file mode 100644 index 00000000..e69de29b diff --git a/test-projects/main/src/module-a/source-1.pbs b/test-projects/main/src/module-a/source-1.pbs new file mode 100644 index 00000000..e69de29b diff --git a/test-projects/main/src/module-a/source-2.pbs b/test-projects/main/src/module-a/source-2.pbs new file mode 100644 index 00000000..e69de29b diff --git a/test-projects/main/src/module-a/source-3.pbs b/test-projects/main/src/module-a/source-3.pbs new file mode 100644 index 00000000..e69de29b diff --git a/test-projects/main/src/module-b/mod.barrel b/test-projects/main/src/module-b/mod.barrel new file mode 100644 index 00000000..e69de29b diff --git a/test-projects/main/src/module-b/source-1.pbs b/test-projects/main/src/module-b/source-1.pbs new file mode 100644 index 00000000..e69de29b diff --git a/test-projects/main/src/module-b/source-2.pbs b/test-projects/main/src/module-b/source-2.pbs new file mode 100644 index 00000000..e69de29b diff --git a/test-projects/main/src/module-b/source-3.pbs b/test-projects/main/src/module-b/source-3.pbs new file mode 100644 index 00000000..e69de29b diff --git a/test-projects/main/src/module-c/mod.barrel b/test-projects/main/src/module-c/mod.barrel new file mode 100644 index 00000000..e69de29b diff --git a/test-projects/main/src/module-c/source-1.pbs b/test-projects/main/src/module-c/source-1.pbs new file mode 100644 index 00000000..e69de29b diff --git a/test-projects/main/src/module-c/source-2.pbs b/test-projects/main/src/module-c/source-2.pbs new file mode 100644 index 00000000..e69de29b diff --git a/test-projects/main/src/module-c/source-3.pbs b/test-projects/main/src/module-c/source-3.pbs new file mode 100644 index 00000000..e69de29b