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