001/*
002 * Copyright (C) 2022 - 2024, the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *    http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.github.ascopes.jct.compilers;
017
018import io.github.ascopes.jct.containers.ModuleContainerGroup;
019import io.github.ascopes.jct.containers.OutputContainerGroup;
020import io.github.ascopes.jct.containers.PackageContainerGroup;
021import io.github.ascopes.jct.diagnostics.TraceDiagnostic;
022import io.github.ascopes.jct.filemanagers.JctFileManager;
023import java.util.List;
024import java.util.Set;
025import javax.tools.JavaFileObject;
026import javax.tools.StandardLocation;
027import org.apiguardian.api.API;
028import org.apiguardian.api.API.Status;
029import org.jspecify.annotations.Nullable;
030
031/**
032 * The result of a compilation.
033 *
034 * <p>This provides access to a number of useful pieces of information
035 * including the file manager used for the compilation, compiler logs,
036 * and diagnostics.
037 *
038 * @author Ashley Scopes
039 * @since 0.0.1
040 */
041@API(since = "0.0.1", status = Status.STABLE)
042public interface JctCompilation {
043
044  /**
045   * Get the command line arguments that were passed to the compiler.
046   *
047   * @return the command line arguments.
048   */
049  @API(since = "0.5.0", status = Status.STABLE)
050  List<String> getArguments();
051
052  /**
053   * Determine if warnings were treated as errors.
054   *
055   * @return {@code true} if warnings were treated as errors, or {@code false} otherwise.
056   */
057  boolean isFailOnWarnings();
058
059  /**
060   * Determine if the compilation was successful or not.
061   *
062   * @return {@code true} if successful, or {@code false} if not successful.
063   */
064  boolean isSuccessful();
065
066  /**
067   * Determine if the compilation was a failure or not.
068   *
069   * @return {@code true} if not successful, or {@code false} if successful.
070   */
071  default boolean isFailure() {
072    return !isSuccessful();
073  }
074
075  /**
076   * Get the lines of output produced by the compiler, if any were captured.
077   *
078   * <p>This is separate to diagnostics.
079   *
080   * @return the lines of output.
081   */
082  List<String> getOutputLines();
083
084  /**
085   * Get the compilation units used in the compilation.
086   *
087   * @return the compilation units.
088   */
089  Set<JavaFileObject> getCompilationUnits();
090
091  /**
092   * Get the diagnostics that were reported by the compilation, in the order that they
093   * were reported.
094   *
095   * @return the diagnostics
096   */
097  List<TraceDiagnostic<JavaFileObject>> getDiagnostics();
098
099  /**
100   * Get the file manager that was used to store and manage files.
101   *
102   * <p>This can be used to obtain a classloader for any compiled sources,
103   * which can then be used to reflectively test what the compiler produced.
104   *
105   * @return the file manager.
106   */
107  JctFileManager getFileManager();
108
109  /**
110   * Get the output container group for class outputs.
111   *
112   * <p>This usually consists of any {@code *.class} files produced by the compiler,
113   * and is equivalent to {@code target/classes} in a Maven project.
114   *
115   * @return the output container group, or {@code null} if it does not exist.
116   */
117  @Nullable
118  default OutputContainerGroup getClassOutputs() {
119    return getFileManager().getOutputContainerGroup(StandardLocation.CLASS_OUTPUT);
120  }
121
122  /**
123   * Get the output container group for source outputs.
124   *
125   * <p>This consists of any generated source code created by annotation processors,
126   * and is equivalent to {@code target/generated-sources} in a Maven project.
127   *
128   * @return the output container group, or {@code null} if it does not exist.
129   */
130  @Nullable
131  default OutputContainerGroup getSourceOutputs() {
132    return getFileManager().getOutputContainerGroup(StandardLocation.SOURCE_OUTPUT);
133  }
134
135  /**
136   * Get the package container group for the class path.
137   *
138   * <p>This represents the class path used for compilation.
139   *
140   * @return the package container group, or {@code null} if it does not exist.
141   */
142  @Nullable
143  default PackageContainerGroup getClassPath() {
144    return getFileManager().getPackageContainerGroup(StandardLocation.CLASS_PATH);
145  }
146
147  /**
148   * Get the package container group for the source path.
149   *
150   * <p>This is equivalent to {@code src/main/java} and {@code src/main/resources}
151   * in a Maven project.
152   *
153   * @return the package container group, or {@code null} if it does not exist.
154   */
155  @Nullable
156  default PackageContainerGroup getSourcePath() {
157    return getFileManager().getPackageContainerGroup(StandardLocation.SOURCE_PATH);
158  }
159
160  /**
161   * Get the package container group for the annotation processor path.
162   *
163   * @return the package container group, or {@code null} if it does not exist.
164   */
165  @Nullable
166  default PackageContainerGroup getAnnotationProcessorPath() {
167    return getFileManager().getPackageContainerGroup(StandardLocation.ANNOTATION_PROCESSOR_PATH);
168  }
169
170  /**
171   * Get the module container group for the annotation processor module path.
172   *
173   * @return the module container group, or {@code null} if it does not exist.
174   */
175  @Nullable
176  default ModuleContainerGroup getAnnotationProcessorModulePath() {
177    return getFileManager()
178        .getModuleContainerGroup(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH);
179  }
180
181  /**
182   * Get the package container group for the platform class path (a.k.a. the bootstrap class path).
183   *
184   * <p>You generally do not need to use this. The platform class path mechanism has been mostly
185   * replaced by the use of the system modules path as of Java 11. It is simply provided for
186   * backwards compatibility.
187   *
188   * @return the package container group, or {@code null} if it does not exist.
189   */
190  @Nullable
191  default PackageContainerGroup getPlatformClassPath() {
192    return getFileManager().getPackageContainerGroup(StandardLocation.PLATFORM_CLASS_PATH);
193  }
194
195  /**
196   * Get the output container group for the native header file outputs.
197   *
198   * <p>If you invoke {@code javac} with the {@code -H} flag, then this represents the
199   * directory that C/C++ header file stubs for JNI are written to.
200   *
201   * @return the output container group, or {@code null} if it does not exist.
202   */
203  @Nullable
204  default OutputContainerGroup getNativeHeaderOutputs() {
205    return getFileManager().getOutputContainerGroup(StandardLocation.NATIVE_HEADER_OUTPUT);
206  }
207
208  /**
209   * Get the module container group for the module source path.
210   *
211   * <p>Many build tools do not provide a direct equivalent of this mechanism as of now, but
212   * this is a source path introduced in Java 9 that allows specifying multiple named JPMS
213   * modules to compile under a single compilation invocation.
214   *
215   * <p>For example, you may use this in a project to compile an API module, a default
216   * implementation module, and a module containing unit tests all together.
217   *
218   * @return the module container group, or {@code null} if it does not exist.
219   */
220  @Nullable
221  default ModuleContainerGroup getModuleSourcePath() {
222    return getFileManager().getModuleContainerGroup(StandardLocation.MODULE_SOURCE_PATH);
223  }
224
225  /**
226   * Get the module container group for the upgrade module path.
227   *
228   * <p>You generally will not need to use this, as this is a mechanism used to upgrade
229   * modules in-place incrementally with fixes without redistributing the entire application.
230   *
231   * @return the module container group, or {@code null} if it does not exist.
232   */
233  @Nullable
234  default ModuleContainerGroup getUpgradeModulePath() {
235    return getFileManager().getModuleContainerGroup(StandardLocation.UPGRADE_MODULE_PATH);
236  }
237
238  /**
239   * Get the module container group for all system modules that are part of the JDK distribution.
240   *
241   * <p>This will usually just point to the Java standard library.
242   *
243   * @return the module container group, or {@code null} if it does not exist.
244   */
245  @Nullable
246  default ModuleContainerGroup getSystemModules() {
247    return getFileManager().getModuleContainerGroup(StandardLocation.SYSTEM_MODULES);
248  }
249
250  /**
251   * Get the module container group for the module path.
252   *
253   * <p>This is equivalent to the class path, but holds any JPMS modules.
254   *
255   * @return the module container group, or {@code null} if it does not exist.
256   */
257  @Nullable
258  default ModuleContainerGroup getModulePath() {
259    return getFileManager().getModuleContainerGroup(StandardLocation.MODULE_PATH);
260  }
261
262  /**
263   * Get the module container group for the patch module path.
264   *
265   * <p>You generally will not need to use this. It consists of patchable module sources that
266   * can be used to inject additional classes into a module. This can be used for cases like
267   * unit tests where you wish to embed the unit test classes into the existing application
268   * module to exploit features such as package private access.
269   *
270   * @return the module container group, or {@code null} if it does not exist.
271   */
272  @Nullable
273  default ModuleContainerGroup getPatchModulePath() {
274    return getFileManager().getModuleContainerGroup(StandardLocation.PATCH_MODULE_PATH);
275  }
276}