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

A bit of javadoc for codegen #31939

Merged
merged 1 commit into from
Apr 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

import io.quarkus.bootstrap.model.ApplicationModel;

/**
* Code generation context
*/
public class CodeGenContext {
private final ApplicationModel model;
private final Path outDir;
Expand All @@ -15,6 +18,17 @@ public class CodeGenContext {
private final Config config;
private final boolean test;

/**
* Creates a code generation context
*
* @param model application model
* @param outDir target directory for the generated output
* @param workDir working directory, typically the main build directory of the project
* @param inputDir directory containing input content for a code generator
* @param redirectIO whether the code generating process should redirect its IO
* @param config application build time configuration
* @param test indicates whether the code generation is being triggered for tests
*/
public CodeGenContext(ApplicationModel model, Path outDir, Path workDir, Path inputDir, boolean redirectIO,
Config config, boolean test) {
this.model = model;
Expand All @@ -26,30 +40,77 @@ public CodeGenContext(ApplicationModel model, Path outDir, Path workDir, Path in
this.test = test;
}

/**
* Application model
*
* @return application model
*/
public ApplicationModel applicationModel() {
return model;
}

/**
* Target directory for the generated output.
* The directory would typically be resolved as {@code <project.build.directory>/generated-sources/<codegen-provider-id>},
* where {@code <codegen-provider-id> would match the value of {@link CodeGenProvider#providerId()}.
* For example, for a code gen provider {@code foo}, the output directory in a typical Maven project would be
* {@code target/generated-sources/foo}.
*
* @return target directory for the generated output
*/
public Path outDir() {
return outDir;
}

/**
* Working directory, typically the main build directory of the project.
* For a typical Maven project it would be the {@code target} directory.
*
* @return working directory, typically the main build directory of the project
*/
public Path workDir() {
return workDir;
}

/**
* Directory containing input content for a code generator.
* For the main application build of a typical Maven project the input sources directory
* would be {@code <project.basedir>/src/main/<codegen-provider-id>}, while for the tests it would be
* {@code <project.basedir>/src/test/<codegen-provider-id>}, where {@code <codegen-provider-id}
* would match the value of {@link CodeGenProvider#providerId()}.
*
* @return directory containing input content a code generator
*/
public Path inputDir() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolute or relative (to what?).
An example would be nice, something like {@code src/main/foo} for a CodeGenProvider having inputDirectory {@code foo}

return inputDir;
}

/**
* Whether any new processes spawned by a given {@link CodeGenProvider} should inherit the
* launching process' output streams
* or redirect its output and error streams using {@link java.lang.ProcessBuilder.Redirect#PIPE}.
* In the current implementation this is typically set to {@code true} by the framework.
*
* @return whether the code generation process should redirect its error and output streams
*/
public boolean shouldRedirectIO() {
return redirectIO;
}

/**
* Application build time configuration
*
* @return application build time configuration
*/
public Config config() {
return config;
}

/**
* Indicates whether the code generation is being triggered for tests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this return false for dev mode?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessarily. We do generate code for tests when launching dev mode to be able to run continuous testing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I see, thanks for explaining! Then the text is correct.

*
* @return indicates whether the code generation is being triggered for tests
*/
public boolean test() {
return test;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ public interface CodeGenProvider {
String inputExtension();

/**
* Name of the directory containing the input files for the CodeGenProvider
* for <code>foo</code>, <code>src/main/foo</code> for application and <code>src/test/foo</code> for test resources
* Name of the directory containing input files for a given {@link CodeGenProvider} implementation
* relative to a sources root directory. For example, if an input directory is configured as <code>foo</code>,
* for a production build of an application the sources will be looked up at <code>src/main/foo</code> path
* and at <code>src/test/foo</code> for tests.
*
* @return the input directory
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,33 @@

import io.quarkus.deployment.CodeGenProvider;

/**
* Links a {@link CodeGenProvider} instance, an input and output directories for the provider.
*/
public class CodeGenData {
public final CodeGenProvider provider;
public final Path outPath;
public final Path sourceDir;
public final Path buildDir;
public boolean redirectIO;

/**
* @param provider code gen provider
* @param outPath where the generated output should be stored
* @param sourceDir where the input sources are
* @param buildDir base project output directory
*/
public CodeGenData(CodeGenProvider provider, Path outPath, Path sourceDir, Path buildDir) {
this(provider, outPath, sourceDir, buildDir, true);
}

/**
* @param provider code gen provider
* @param outPath where the generated output should be stored
* @param sourceDir where the input sources are
* @param buildDir base project output directory
* @param redirectIO whether to redirect IO, in case a provider is logging something
*/
public CodeGenData(CodeGenProvider provider, Path outPath, Path sourceDir, Path buildDir, boolean redirectIO) {
this.provider = provider;
this.outPath = outPath;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.quarkus.bootstrap.model;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
Expand All @@ -13,31 +12,85 @@
import io.quarkus.maven.dependency.Dependency;
import io.quarkus.maven.dependency.ResolvedDependency;

/**
* Application dependency model. Allows to explore application dependencies,
* Quarkus platforms found in the project configuration and Quarkus platform configuration properties.
*/
public interface ApplicationModel {

/**
* Main application artifact
*
* @return main application artifact
*/
ResolvedDependency getAppArtifact();

/**
* All the dependencies of an application including runtime and build time dependencies.
*
* @return application runtime and build time dependencies
*/
Collection<ResolvedDependency> getDependencies();

/**
* Runtime dependencies of an application
*
* @return runtime dependencies of an application
*/
default Collection<ResolvedDependency> getRuntimeDependencies() {
return getDependencies().stream().filter(Dependency::isRuntimeCp).collect(Collectors.toList());
}

/**
* Quarkus platforms (BOMs) found in the configuration of an application
*
* @return Quarkus platforms (BOMs) found in the configuration of an application
*/
PlatformImports getPlatforms();

/**
* Quarkus platform configuration properties
*
* @return Quarkus platform configuration properties
*/
default Map<String, String> getPlatformProperties() {
final PlatformImports platformImports = getPlatforms();
return platformImports == null ? Collections.emptyMap() : platformImports.getPlatformProperties();
return platformImports == null ? Map.of() : platformImports.getPlatformProperties();
}

/**
* Extension capability requirements collected from the extensions found on the classpath of an application
*
* @return Extension capability requirements collected from the extensions found on the classpath of an application
*/
Collection<ExtensionCapabilities> getExtensionCapabilities();

/**
* Class loading parent-first artifacts
*
* @return class loading parent-first artifacts
*/
Set<ArtifactKey> getParentFirst();

/**
* Class loading runner parent-first artifacts
*
* @return class loading runner parent-first artifacts
*/
Set<ArtifactKey> getRunnerParentFirst();

/**
* Class loading lower priority artifacts
*
* @return class loading lower priority artifacts
*/
Set<ArtifactKey> getLowerPriorityArtifacts();

/**
* Local project dependencies that are live-reloadable in dev mode.
*
* @return local project dependencies that are live-reloadable in dev mode.
*/
Set<ArtifactKey> getReloadableWorkspaceDependencies();

/**
Expand All @@ -47,10 +100,20 @@ default Map<String, String> getPlatformProperties() {
*/
Map<ArtifactKey, Set<String>> getRemovedResources();

/**
* Main workspace module of an application. Could be null, in case the project is not available during the build.
*
* @return main workspace module of an application, could be null, in case the project is not available during the build
*/
default WorkspaceModule getApplicationModule() {
return getAppArtifact().getWorkspaceModule();
}

/**
* All the workspace modules found as dependencies of an application
*
* @return all the workspace modules found as dependencies of an application
*/
default Collection<WorkspaceModule> getWorkspaceModules() {
final Map<WorkspaceModuleId, WorkspaceModule> result = new HashMap<>();
collectModules(getAppArtifact().getWorkspaceModule(), result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
Expand Down Expand Up @@ -35,15 +34,15 @@ static void walk(Path root, Path rootDir, PathFilter pathFilter, Map<String, Str
}

static <T> T process(Path root, Path rootDir, Path path, PathFilter pathFilter, Function<PathVisit, T> func) {
final PathTreeVisit visit = new PathTreeVisit(root, rootDir, pathFilter, Collections.emptyMap());
final PathTreeVisit visit = new PathTreeVisit(root, rootDir, pathFilter, Map.of());
if (visit.setCurrent(path)) {
return func.apply(visit);
}
return func.apply(null);
}

static void consume(Path root, Path rootDir, Path path, PathFilter pathFilter, Consumer<PathVisit> func) {
final PathTreeVisit visit = new PathTreeVisit(root, rootDir, pathFilter, Collections.emptyMap());
final PathTreeVisit visit = new PathTreeVisit(root, rootDir, pathFilter, Map.of());
if (visit.setCurrent(path)) {
func.accept(visit);
} else {
Expand All @@ -64,7 +63,7 @@ private PathTreeVisit(Path root, Path rootDir, PathFilter pathFilter, Map<String
this.root = root;
this.baseDir = rootDir;
this.pathFilter = pathFilter;
this.multiReleaseMapping = multiReleaseMapping == null || multiReleaseMapping.isEmpty() ? Collections.emptyMap()
this.multiReleaseMapping = multiReleaseMapping == null || multiReleaseMapping.isEmpty() ? Map.of()
: new HashMap<>(multiReleaseMapping);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,38 @@
import java.net.URL;
import java.nio.file.Path;

/**
* Provides context for a given path visit
*/
public interface PathVisit {

/**
* The root of the path tree the current path belongs to.
* For a {@link PathTree} created for an archive, this will be the path to the archive file.
* For a {@link PathTree} created for a directory, this will be the path to the directory.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolute or relative (to what)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the caller that provides the value of the root.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For both ArchivePathTree and DirectoryPathTree you seem to be calling Files.walk(rootDir) right after creating PathTreeVisit so the value of rootDir must be de facto absolute. I guess the API users are nor supposed to add new implementations of PathVisit and they mostly interact with it via their custom PathVisitors that they pass to PathTree.walk(PathVisitor). So for the API users, PathVisit instances are supplied by the API and they deserve to know whether the paths returned by PathVisit methods are (de facto) absolute.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you seem to be calling Files.walk(rootDir) right after creating PathTreeVisit so the value of rootDir must be de facto absolute

The rootDir does not have to be an absolute path for Files.walk(rootDir) to work though.

Copy link
Member Author

@aloubyansky aloubyansky Mar 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Peter, based on my comment above, I wouldn't say it's "de facto absolute". It really depends on what the rootDir path is, which is provided by the caller.
Otherwise, from my perspective, this javadoc is pretty much in line with https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/FileVisitor.html Or am I still missing something? Or you'd also have similar remarks for that the FileVisitor javadoc?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Peter, based on my comment above, I wouldn't say it's "de facto absolute". It really depends on what the rootDir path is, which is provided by the caller.

Sorry, "de facto absolute" is perhaps misleading. What I mean is "ready for IO operations" or simply "granted to exist". I admit it is rather trivial to document for getRoot() but it would be really nice to add a sentence to getPath() stating something like

The returned path is granted to exist in the underlying filesystem (unless it was deleted by a parallel process or thread) and therefore it can be used for I/O operations straight away without further resolving e.g. against getRoot().

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added, thanks

*
* @return root of the path tree the current path belongs to
*/
Path getRoot();

/**
* The path being visited. The {@link java.nio.file.FileSystem} the path belongs to
* will depend on the implementation of the {@link PathTree} being visited. For example,
* for an archive it will be a ZIP {@link java.nio.file.FileSystem} implementation.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolute or relative (to what)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might depend on whether the root is absolute or relative. It will be a path that can be read during the visit.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be a path that can be read during the visit.

Exactly, that's a piece of super important info I'd like to find in JavaDoc!

*
* The returned path is granted to exist in the underlying file system (unless it was deleted by a parallel process or
* thread) and therefore it can be used for I/O operations straight away without further resolving, e.g. against
* {@link #getRoot()}
*
* @return path being visited
*/
Path getPath();

/**
* {@link java.net.URL} that can be used to read the content of the path.
*
* @return URL that can be used to read the content of the path
*/
default URL getUrl() {
try {
return getPath().toUri().toURL();
Expand All @@ -18,7 +44,20 @@ default URL getUrl() {
}
}

/**
* Path relative to the root of the tree as a string with a provided path element separator.
* For a {@link PathTree} created for an archive, the returned path will be relative to the root
* of the corresponding {@link java.nio.file.FileSystem} implementation.
* For a {@link PathTree} created for a directory, the returned path will be relative to the directory
* used as the root of the path tree.
*
* @param separator path element separator
* @return path relative to the root of the tree as a string with a provided path element separator
*/
String getRelativePath(String separator);

/**
* Terminates walking over a {@link PathTree} after this visit.
*/
void stopWalking();
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
package io.quarkus.paths;

/**
* {@link PathTree} path visitor
*/
public interface PathVisitor {

/**
* Called to visit a path when walking a path tree or when a caller
* requested to visit a specific path in a tree. In the latter case
* if the requested path does not exist, the {@code visit} argument
* will be null and it'll be up to the caller how to handle that,
* i.e. whether to throw a path not found exception or return silently.
*
* @param visit visit object or null, in case the requested path does not exist
*/
void visitPath(PathVisit visit);
}