diff --git a/org.eclipse.jdt.apt.core/src/org/eclipse/jdt/apt/core/internal/AptCompilationParticipant.java b/org.eclipse.jdt.apt.core/src/org/eclipse/jdt/apt/core/internal/AptCompilationParticipant.java index 302f0e0524e..488f9598d08 100644 --- a/org.eclipse.jdt.apt.core/src/org/eclipse/jdt/apt/core/internal/AptCompilationParticipant.java +++ b/org.eclipse.jdt.apt.core/src/org/eclipse/jdt/apt/core/internal/AptCompilationParticipant.java @@ -249,7 +249,7 @@ public String[] getGeneratedSourcePaths(IJavaProject project, boolean isTest) { } IFolder folder = generatedSourceFolderManager.getFolder(); - return folder == null ? null : new String[] { folder.getLocation().toOSString() }; + return folder == null || folder.getLocation() == null ? null : new String[] { folder.getLocation().toOSString() }; } @Override diff --git a/org.eclipse.jdt.core.tests.builder/resources/autoValueSnippet/.classpath b/org.eclipse.jdt.core.tests.builder/resources/autoValueSnippet/.classpath new file mode 100644 index 00000000000..e90d2ab51b7 --- /dev/null +++ b/org.eclipse.jdt.core.tests.builder/resources/autoValueSnippet/.classpath @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/org.eclipse.jdt.core.tests.builder/resources/autoValueSnippet/.factorypath b/org.eclipse.jdt.core.tests.builder/resources/autoValueSnippet/.factorypath new file mode 100644 index 00000000000..cf12c6aae49 --- /dev/null +++ b/org.eclipse.jdt.core.tests.builder/resources/autoValueSnippet/.factorypath @@ -0,0 +1,4 @@ + + + + diff --git a/org.eclipse.jdt.core.tests.builder/resources/autoValueSnippet/.project b/org.eclipse.jdt.core.tests.builder/resources/autoValueSnippet/.project new file mode 100644 index 00000000000..7f251d1870a --- /dev/null +++ b/org.eclipse.jdt.core.tests.builder/resources/autoValueSnippet/.project @@ -0,0 +1,19 @@ + + + autoValueSnippet + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + + + diff --git a/org.eclipse.jdt.core.tests.builder/resources/autoValueSnippet/.settings/org.eclipse.jdt.apt.core.prefs b/org.eclipse.jdt.core.tests.builder/resources/autoValueSnippet/.settings/org.eclipse.jdt.apt.core.prefs new file mode 100644 index 00000000000..74a3c0a0761 --- /dev/null +++ b/org.eclipse.jdt.core.tests.builder/resources/autoValueSnippet/.settings/org.eclipse.jdt.apt.core.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.apt.aptEnabled=true +org.eclipse.jdt.apt.genSrcDir=.apt_generated +org.eclipse.jdt.apt.genTestSrcDir=.apt_generated_tests +org.eclipse.jdt.apt.reconcileEnabled=true diff --git a/org.eclipse.jdt.core.tests.builder/resources/autoValueSnippet/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jdt.core.tests.builder/resources/autoValueSnippet/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..13bd0e37f06 --- /dev/null +++ b/org.eclipse.jdt.core.tests.builder/resources/autoValueSnippet/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,16 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=11 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.processAnnotations=enabled +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=11 diff --git a/org.eclipse.jdt.core.tests.builder/resources/autoValueSnippet/libs/auto-value-1.6.5.jar b/org.eclipse.jdt.core.tests.builder/resources/autoValueSnippet/libs/auto-value-1.6.5.jar new file mode 100644 index 00000000000..be17cd17333 Binary files /dev/null and b/org.eclipse.jdt.core.tests.builder/resources/autoValueSnippet/libs/auto-value-1.6.5.jar differ diff --git a/org.eclipse.jdt.core.tests.builder/resources/autoValueSnippet/libs/auto-value-annotations-1.6.5.jar b/org.eclipse.jdt.core.tests.builder/resources/autoValueSnippet/libs/auto-value-annotations-1.6.5.jar new file mode 100644 index 00000000000..f68a0136753 Binary files /dev/null and b/org.eclipse.jdt.core.tests.builder/resources/autoValueSnippet/libs/auto-value-annotations-1.6.5.jar differ diff --git a/org.eclipse.jdt.core.tests.builder/resources/autoValueSnippet/src/Outer.java b/org.eclipse.jdt.core.tests.builder/resources/autoValueSnippet/src/Outer.java new file mode 100644 index 00000000000..335144144a0 --- /dev/null +++ b/org.eclipse.jdt.core.tests.builder/resources/autoValueSnippet/src/Outer.java @@ -0,0 +1,23 @@ +import com.google.auto.value.AutoValue; + +@AutoValue +public abstract class Outer { + + public static Outer create(Inner value) { + return new AutoValue_Outer(value); + } + + public abstract Inner inner(); + + @AutoValue + public abstract static class Inner { + + public static Inner create(String value) { + return new AutoValue_Outer_Inner(value); + } + + public abstract String value(); + + } + +} \ No newline at end of file diff --git a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/BasicBuildTests.java b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/BasicBuildTests.java index 3ae4de016ed..3afe0c31dd4 100644 --- a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/BasicBuildTests.java +++ b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/BasicBuildTests.java @@ -15,13 +15,35 @@ *******************************************************************************/ package org.eclipse.jdt.core.tests.builder; +import java.io.File; +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.StandardCopyOption; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.HashSet; import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; import junit.framework.*; +import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IncrementalProjectBuilder; +import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.jdt.core.compiler.*; import org.eclipse.jdt.core.IJavaProject; @@ -29,11 +51,15 @@ import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.tests.util.Util; import org.eclipse.jdt.internal.compiler.CompilationResult; +import org.eclipse.jdt.internal.compiler.Compiler; +import org.eclipse.jdt.internal.compiler.CompilerConfiguration; +import org.eclipse.jdt.internal.compiler.ICompilerFactory; import org.eclipse.jdt.internal.compiler.ICompilerRequestor; import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy; import org.eclipse.jdt.internal.compiler.IProblemFactory; import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.compiler.env.INameEnvironment; +import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.problem.DefaultProblem; import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities; import org.eclipse.jdt.internal.core.JavaModelManager; @@ -697,11 +723,10 @@ public void testBug549942() throws JavaModelException { } } - public void testCustomCompiler() throws JavaModelException { - final String CUSTOM_COMPILER_KEY = AbstractImageBuilder.class.getSimpleName() + ".compiler"; - final String CUSTOM_COMPILER_VALUE = MockCompiler.class.getName(); + public void testCustomCompilerFactory() throws JavaModelException { + final String CUSTOM_COMPILER_VALUE = MockCompilerFactory.class.getName(); try { - System.setProperty(CUSTOM_COMPILER_KEY, CUSTOM_COMPILER_VALUE); + System.setProperty(AbstractImageBuilder.COMPILER_FACTORY_KEY, CUSTOM_COMPILER_VALUE); IPath projectPath = env.addProject("Project"); //$NON-NLS-1$ env.addExternalJars(projectPath, Util.getJavaClassLibs()); @@ -712,12 +737,14 @@ public void testCustomCompiler() throws JavaModelException { env.setOutputFolder(projectPath, "bin"); //$NON-NLS-1$ IPath path = env.addClass(root, "p1", "Hello", //$NON-NLS-1$ //$NON-NLS-2$ - "package p1;\n"+ //$NON-NLS-1$ - "public class Hello {\n"+ //$NON-NLS-1$ - " public static void main(String args[]) {\n"+ //$NON-NLS-1$ - " System.out.println(\"Hello world\");\n"+ //$NON-NLS-1$ - " }\n"+ //$NON-NLS-1$ - "}\n" //$NON-NLS-1$ + """ + package p1; + public class Hello { + public static void main(String args[]) { + System.out.println(\"Hello world\"); + } + } + """ ); fullBuild(projectPath); @@ -727,15 +754,139 @@ public void testCustomCompiler() throws JavaModelException { "Problem : Compilation error from MockCompiler [ resource : range : <0,1> category : <60> severity : <2>]" ); } finally { - System.clearProperty(CUSTOM_COMPILER_KEY); + System.clearProperty(AbstractImageBuilder.COMPILER_FACTORY_KEY); } } - public void testFallbackForProblematicCompiler() throws JavaModelException { - final String CUSTOM_COMPILER_KEY = AbstractImageBuilder.class.getSimpleName() + ".compiler"; - final String CUSTOM_COMPILER_VALUE = "x.y.NotFoundCompiler"; + public void testCustomerCompilerFactoryWithAP() throws Exception { + final String CUSTOM_COMPILER_VALUE = MockCompilerFactory.class.getName(); + List configs = new ArrayList<>(); + Consumer listener = (compiler) -> { + if (compiler instanceof MockCompiler mockCompiler) { + configs.add(mockCompiler.compilerConfig); + } + }; + try { + MockCompilerFactory.addListener(listener); + System.setProperty(AbstractImageBuilder.COMPILER_FACTORY_KEY, CUSTOM_COMPILER_VALUE); + File projectRoot = copyFiles("autoValueSnippet", true); + IPath dotProjectPath = new org.eclipse.core.runtime.Path(new File(projectRoot, IProjectDescription.DESCRIPTION_FILE_NAME).getAbsolutePath()); + IWorkspace workspace = ResourcesPlugin.getWorkspace(); + IProjectDescription descriptor = workspace.loadProjectDescription(dotProjectPath); + String projectName = descriptor.getName(); + IProject project = workspace.getRoot().getProject(projectName); + project.create(descriptor, new NullProgressMonitor()); + project.open(IResource.NONE, new NullProgressMonitor()); + + // full build + project.build(IncrementalProjectBuilder.FULL_BUILD, null); + + // It creates compiler 4 times (MAIN full build, TEST full build, MAIN incremental build, TEST incremental build) + assertEquals(4, configs.size()); + CompilerConfiguration config = configs.get(0); + List processorPaths = config.annotationProcessorPaths(); + assertEquals(2, processorPaths.size()); + assertTrue(processorPaths.get(0).endsWith("auto-value-1.6.5.jar")); + assertTrue(processorPaths.get(1).endsWith("auto-value-annotations-1.6.5.jar")); + + List generatedSourcePaths = config.generatedSourcePaths(); + assertEquals(1, generatedSourcePaths.size()); + assertTrue(generatedSourcePaths.get(0).endsWith(".apt_generated")); + + List sourcePaths = config.sourcepaths(); + assertEquals(2, sourcePaths.size()); + assertTrue(sourcePaths.get(0).endsWith("src")); + assertTrue(sourcePaths.get(1).endsWith(".apt_generated")); + + List classPaths = config.classpaths(); + assertEquals(2, classPaths.size()); + assertTrue(classPaths.get(0).endsWith("bin")); + assertTrue(classPaths.get(1).endsWith("auto-value-annotations-1.6.5.jar")); + + List moduleSourcePaths = config.moduleSourcepaths(); + assertEquals(0, moduleSourcePaths.size()); + + List modulePaths = config.modulepaths(); + assertEquals(0, modulePaths.size()); + + Map sourceOutputMapping = config.sourceOutputMapping(); + assertEquals(2, sourceOutputMapping.size()); + + IFile aptGeneratedFile = project.getFile(".apt_generated/AutoValue_Outer.java"); + assertFalse("The default APT generation should be disabled for custom compiler", aptGeneratedFile.exists()); + } finally { + MockCompilerFactory.removeListener(listener); + System.clearProperty(AbstractImageBuilder.COMPILER_FACTORY_KEY); + } + } + + private File copyFiles(String path, boolean reimportIfExists) throws IOException { + File from = new File(getSourceProjectDirectory(), path); + File to = new File(getWorkingProjectDirectory(), path); + if (to.exists()) { + if (!reimportIfExists) { + return to; + } + if (to.isFile()) { + Files.delete(to.toPath()); + } else { + deleteDirectory(to.toPath()); + } + } + + if (from.isDirectory()) { + copyDirectory(from.toPath(), to.toPath()); + } else { + Files.copy(from.toPath(), to.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + + return to; + } + + private File getSourceProjectDirectory() { + return new File("resources"); + } + + private File getWorkingProjectDirectory() throws IOException { + File dir = new File("target", "workingProjects"); + if (!dir.exists()) { + dir.mkdirs(); + } + return dir; + } + + private static void deleteDirectory(Path path) throws IOException { + Files.walkFileTree(path, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + Files.delete(dir); + return FileVisitResult.CONTINUE; + } + }); + } + + private static void copyDirectory(Path sourceDirectory, Path targetDirectory) throws IOException { + Files.walk(sourceDirectory) + .forEach(sourcePath -> { + Path targetPath = targetDirectory.resolve(sourceDirectory.relativize(sourcePath)); + try { + Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + // ignore + } + }); + } + + public void testFallbackForProblematicCompilerFactory() throws JavaModelException { + final String CUSTOM_COMPILER_VALUE = "x.y.NotFoundCompilerFactory"; try { - System.setProperty(CUSTOM_COMPILER_KEY, CUSTOM_COMPILER_VALUE); + System.setProperty(AbstractImageBuilder.COMPILER_FACTORY_KEY, CUSTOM_COMPILER_VALUE); IPath projectPath = env.addProject("Project"); //$NON-NLS-1$ env.addExternalJars(projectPath, Util.getJavaClassLibs()); @@ -746,12 +897,14 @@ public void testFallbackForProblematicCompiler() throws JavaModelException { env.setOutputFolder(projectPath, "bin"); //$NON-NLS-1$ IPath path = env.addClass(root, "p1", "Hello", //$NON-NLS-1$ //$NON-NLS-2$ - "package p1;\n"+ //$NON-NLS-1$ - "public class Hello {\n"+ //$NON-NLS-1$ - " public static void main(String args[]) {\n"+ //$NON-NLS-1$ - " int unUsedVarable;\n"+ //$NON-NLS-1$ - " }\n"+ //$NON-NLS-1$ - "}\n" //$NON-NLS-1$ + """ + package p1; + public class Hello { + public static void main(String args[]) { + int unUsedVarable; + } + } + """ ); fullBuild(projectPath); @@ -762,16 +915,39 @@ public void testFallbackForProblematicCompiler() throws JavaModelException { "Problem : The value of the local variable unUsedVarable is not used [ resource : range : <87,100> category : <120> severity : <1>]" ); } finally { - System.clearProperty(CUSTOM_COMPILER_KEY); + System.clearProperty(AbstractImageBuilder.COMPILER_FACTORY_KEY); + } + } + + public static class MockCompilerFactory implements ICompilerFactory { + static Set> listeners = new HashSet<>(); + + @Override + public Compiler newCompiler(INameEnvironment environment, IErrorHandlingPolicy policy, + CompilerConfiguration compilerConfig, ICompilerRequestor requestor, IProblemFactory problemFactory) { + Compiler compiler = new MockCompiler(environment, policy, compilerConfig, requestor, problemFactory); + for (Consumer listener : listeners) { + listener.accept(compiler); + } + + return compiler; + } + + static void addListener(Consumer listener) { + listeners.add(listener); + } + + static void removeListener(Consumer listener) { + listeners.remove(listener); } } - public static class MockCompiler extends org.eclipse.jdt.internal.compiler.Compiler { - private CompilerConfiguration compilerConfig; + static class MockCompiler extends org.eclipse.jdt.internal.compiler.Compiler { + CompilerConfiguration compilerConfig; public MockCompiler(INameEnvironment environment, IErrorHandlingPolicy policy, CompilerConfiguration compilerConfig, ICompilerRequestor requestor, IProblemFactory problemFactory) { - super(environment, policy, compilerConfig.getOptions(), requestor, problemFactory); + super(environment, policy, new CompilerOptions(compilerConfig.compilerOptions()), requestor, problemFactory); this.compilerConfig = compilerConfig; } diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/compiler/CompilerConfiguration.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/compiler/CompilerConfiguration.java deleted file mode 100644 index 358c8d7df59..00000000000 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/compiler/CompilerConfiguration.java +++ /dev/null @@ -1,169 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2024 Microsoft Corporation and others. -* All rights reserved. This program and the accompanying materials -* are made available under the terms of the Eclipse Public License 2.0 -* which accompanies this distribution, and is available at -* https://www.eclipse.org/legal/epl-2.0/ -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* Microsoft Corporation - initial API and implementation -*******************************************************************************/ - -package org.eclipse.jdt.core.compiler; - -import java.io.File; -import java.util.List; -import java.util.Map; - -import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; - -/** - * This class encapsulates the standard compiler options that can be - * used to compile Java files. It provides methods to set and retrieve - * various compiler options, including source paths, class paths, - * output directories, annotation processing options, and other compiler - * arguments. - * - * Clients typically use this class when opting for an alternative compiler - * like Javac to compile Java files. - * - * @since 3.38 - * @noextend This class is not intended to be subclassed by clients. - */ -public class CompilerConfiguration { - List sourcepaths; - List moduleSourcepaths; - List classpaths; - List modulepaths; - Map sourceOutputMapping; - CompilerOptions options; - // Location to search for annotation processors. - List annotationProcessorPaths; - // Locations to place generated source files. - List generatedSourcePaths; - - /** - * Returns where to find user class files and annotation processors. - */ - public List getClasspaths() { - return this.classpaths; - } - - /** - * Sets where to find user class files and annotation processors. - * @param classpaths the list of class paths - */ - public void setClasspaths(List classpaths) { - this.classpaths = classpaths; - } - - /** - * Returns where to find modules. - */ - public List getModulepaths() { - return this.modulepaths; - } - - /** - * Sets where to find modules. - * @param modulepaths the list of module paths - */ - public void setModulepaths(List modulepaths) { - this.modulepaths = modulepaths; - } - - /** - * Returns the source code path to search for class or interface definitions. - */ - public List getSourcepaths() { - return this.sourcepaths; - } - - /** - * Sets the source code path to search for class or interface definitions. - * @param sourcepaths the list of source paths - */ - public void setSourcepaths(List sourcepaths) { - this.sourcepaths = sourcepaths; - } - - /** - * Returns where to find source files when compiling modules. - */ - public List getModuleSourcepaths() { - return this.moduleSourcepaths; - } - - /** - * Sets where to find source files when compiling modules. - * @param moduleSourcepaths the list of module source paths - */ - public void setModuleSourcepaths(List moduleSourcepaths) { - this.moduleSourcepaths = moduleSourcepaths; - } - - /** - * Returns the mapping of source files to output directories. - */ - public Map getSourceOutputMapping() { - return this.sourceOutputMapping; - } - - /** - * Sets the mapping of source files to output directories. - * @param sourceOutputMapping the source output mapping - */ - public void setSourceOutputMapping(Map sourceOutputMapping) { - this.sourceOutputMapping = sourceOutputMapping; - } - - /** - * Returns the compiler options to be used for compilation. - * - * @see {@link org.eclipse.jdt.internal.compiler.impl.CompilerOptions} for a list of available options. - */ - public CompilerOptions getOptions() { - return this.options; - } - - /** - * Sets the compiler options to be used for compilation. - * @param options the compiler options - * @see {@link org.eclipse.jdt.internal.compiler.impl.CompilerOptions} for a list of available options. - */ - public void setOptions(CompilerOptions options) { - this.options = options; - } - - /** - * Returns the locations to search for annotation processors. - */ - public List getAnnotationProcessorPaths() { - return this.annotationProcessorPaths; - } - - /** - * Sets the locations to search for annotation processors. - * @param annotationProcessorPaths the list of annotation processor paths - */ - public void setAnnotationProcessorPaths(List annotationProcessorPaths) { - this.annotationProcessorPaths = annotationProcessorPaths; - } - - /** - * Returns the locations to place generated source files. - */ - public List getGeneratedSourcePaths() { - return this.generatedSourcePaths; - } - - /** - * Sets the locations to place generated source files. - * @param generatedSourcePaths the list of generated source paths - */ - public void setGeneratedSourcePaths(List generatedSourcePaths) { - this.generatedSourcePaths = generatedSourcePaths; - } -} diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/CompilerConfiguration.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/CompilerConfiguration.java new file mode 100644 index 00000000000..ec2b4f3c5fa --- /dev/null +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/CompilerConfiguration.java @@ -0,0 +1,67 @@ +/******************************************************************************* +* Copyright (c) 2024 Microsoft Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License 2.0 +* which accompanies this distribution, and is available at +* https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Microsoft Corporation - initial API and implementation +*******************************************************************************/ + +package org.eclipse.jdt.internal.compiler; + +import java.io.File; +import java.util.List; +import java.util.Map; + +/** + * This class encapsulates the standard compiler options that can be + * used to compile Java files. It provides methods to set and retrieve + * various compiler options, including source paths, class paths, + * output directories, annotation processing options, and other compiler + * arguments. + * + * Clients typically use this class when opting for an alternative compiler + * like Javac to compile Java files. + * + * @since 3.38 + * @noextend This class is not intended to be subclassed by clients. + */ +public record CompilerConfiguration( + /** + * List of file paths where the compiler can find source files. + */ + List sourcepaths, + /** + * List of file paths where the compiler can find source files for modules. + */ + List moduleSourcepaths, + /** + * List of file paths where the compiler can find user class files and annotation processors. + */ + List classpaths, + /** + * List of file paths where the compiler can find modules. + */ + List modulepaths, + /** + * Location to search for annotation processors. + */ + List annotationProcessorPaths, + /** + * Locations to place generated source files. + */ + List generatedSourcePaths, + /** + * The mapping of source files to output directories. + */ + Map sourceOutputMapping, + /** + * The compiler options used to control the compilation behavior. + * See {@link org.eclipse.jdt.internal.compiler.impl.CompilerOptions} for a list of available options. + */ + Map compilerOptions) { +} diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/DefaultCompilerFactory.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/DefaultCompilerFactory.java new file mode 100644 index 00000000000..4f32dea736d --- /dev/null +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/DefaultCompilerFactory.java @@ -0,0 +1,27 @@ +/******************************************************************************* +* Copyright (c) 2024 Microsoft Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License 2.0 +* which accompanies this distribution, and is available at +* https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Microsoft Corporation - initial API and implementation +*******************************************************************************/ + +package org.eclipse.jdt.internal.compiler; + +import org.eclipse.jdt.internal.compiler.env.INameEnvironment; +import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; + +public class DefaultCompilerFactory implements ICompilerFactory { + + @Override + public Compiler newCompiler(INameEnvironment environment, IErrorHandlingPolicy policy, + CompilerConfiguration compilerConfig, ICompilerRequestor requestor, IProblemFactory problemFactory) { + return new Compiler(environment, policy, new CompilerOptions(compilerConfig.compilerOptions()), + requestor, problemFactory); + } +} diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/ICompilerFactory.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/ICompilerFactory.java new file mode 100644 index 00000000000..96ad2a9e281 --- /dev/null +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/ICompilerFactory.java @@ -0,0 +1,38 @@ +/******************************************************************************* +* Copyright (c) 2024 Microsoft Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License 2.0 +* which accompanies this distribution, and is available at +* https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Microsoft Corporation - initial API and implementation +*******************************************************************************/ + +package org.eclipse.jdt.internal.compiler; + +import org.eclipse.jdt.internal.compiler.env.INameEnvironment; + +/** + * A factory used to produce a compiler to compile the Java files. + */ +public interface ICompilerFactory { + + /** + * Create a new compiler using the given name environment and compiler options. + * + * @param environment - the type system environment used for resolving types and packages + * @param policy - the error handling policy + * @param compilerConfig - the configuration to control the compiler behavior + * @param requestor - the requestor to receive and persist compilation results + * @param problemFactory - the factory to create problem descriptors + * @return the new compiler instance + */ + public Compiler newCompiler(final INameEnvironment environment, + final IErrorHandlingPolicy policy, + final CompilerConfiguration compilerConfig, + final ICompilerRequestor requestor, + final IProblemFactory problemFactory); +} diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/AbstractImageBuilder.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/AbstractImageBuilder.java index 09c66ece56f..bc6703ca78b 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/AbstractImageBuilder.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/AbstractImageBuilder.java @@ -23,7 +23,6 @@ import org.eclipse.jdt.internal.compiler.Compiler; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; -import org.eclipse.jdt.internal.compiler.env.INameEnvironment; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; @@ -64,7 +63,6 @@ public abstract class AbstractImageBuilder implements ICompilerRequestor, ICompi protected boolean compiledAllAtOnce; private boolean inCompiler; -private boolean useDefaultCompiler = true; protected boolean keepStoringProblemMarkers; protected Map filesWithAnnotations = null; @@ -88,6 +86,7 @@ public abstract class AbstractImageBuilder implements ICompilerRequestor, ICompi public final static Integer P_HIGH = Integer.valueOf(IMarker.PRIORITY_HIGH); public final static Integer P_NORMAL = Integer.valueOf(IMarker.PRIORITY_NORMAL); public final static Integer P_LOW = Integer.valueOf(IMarker.PRIORITY_LOW); +public final static String COMPILER_FACTORY_KEY = "AbstractImageBuilder.compilerFactory"; //$NON-NLS-1$ private final CompilationGroup compilationGroup; protected AbstractImageBuilder(JavaBuilder javaBuilder, boolean buildStarting, State newState, CompilationGroup compilationGroup) { @@ -576,39 +575,34 @@ protected Compiler newCompiler() { CompilerOptions compilerOptions = new CompilerOptions(projectOptions); compilerOptions.performMethodsFullRecovery = true; compilerOptions.performStatementsRecovery = true; - Compiler newCompiler = null; - String compilerClassName = System.getProperty(AbstractImageBuilder.class.getSimpleName() + ".compiler"); //$NON-NLS-1$ - if (compilerClassName != null) { + + ICompilerFactory compilerFactory = null; + String compilerFactoryClassName = System.getProperty(COMPILER_FACTORY_KEY); + if (compilerFactoryClassName != null) { try { - Class compilerClass = (Class) Class.forName(compilerClassName); - Constructor constructor = compilerClass.getDeclaredConstructor( - INameEnvironment.class, - IErrorHandlingPolicy.class, - CompilerConfiguration.class, - ICompilerRequestor.class, - IProblemFactory.class); - newCompiler = constructor.newInstance(this.nameEnvironment, - DefaultErrorHandlingPolicies.proceedWithAllProblems(), - prepareCompilerConfiguration(compilerOptions), - this, - ProblemFactory.getProblemFactory(Locale.getDefault())); - this.useDefaultCompiler = false; + Class compilerFactoryClass = (Class) Class.forName(compilerFactoryClassName); + Constructor constructor = compilerFactoryClass.getDeclaredConstructor(); + compilerFactory = constructor.newInstance(); } catch (ClassNotFoundException e) { - ILog.get().error("Could not load class " + compilerClassName, e); //$NON-NLS-1$ + ILog.get().error("Could not load class " + compilerFactoryClassName, e); //$NON-NLS-1$ } catch (NoSuchMethodException e) { - ILog.get().error("Couldn't find compatible constructor " + compilerClassName); //$NON-NLS-1$ + ILog.get().error("Couldn't find compatible constructor " + compilerFactoryClassName); //$NON-NLS-1$ } catch (IllegalAccessException | IllegalArgumentException | InstantiationException | InvocationTargetException e) { - ILog.get().error("Failed invoking constructor " + compilerClassName); //$NON-NLS-1$ + ILog.get().error("Failed invoking constructor " + compilerFactoryClassName); //$NON-NLS-1$ } } - if (newCompiler == null) { - newCompiler = new Compiler( + + if (compilerFactory == null) { + compilerFactory = new DefaultCompilerFactory(); + } + + Compiler newCompiler = compilerFactory.newCompiler( this.nameEnvironment, DefaultErrorHandlingPolicies.proceedWithAllProblems(), - compilerOptions, + prepareCompilerConfiguration(compilerOptions), this, ProblemFactory.getProblemFactory(Locale.getDefault())); - } + CompilerOptions options = newCompiler.options; // temporary code to allow the compiler to revert to a single thread String setting = System.getProperty("jdt.compiler.useSingleThread"); //$NON-NLS-1$ @@ -627,69 +621,90 @@ protected Compiler newCompiler() { } private CompilerConfiguration prepareCompilerConfiguration(CompilerOptions options) { - CompilerConfiguration configuration = new CompilerConfiguration(); - configuration.setOptions(options); - List annotationProcessorPaths = new ArrayList<>(); - List generatedSourcePaths = new ArrayList<>(); - boolean isTest = this.compilationGroup == CompilationGroup.TEST; - if (this.javaBuilder.participants != null) { - for (CompilationParticipant participant : this.javaBuilder.participants) { - if (participant.isAnnotationProcessor()) { - String[] paths = participant.getAnnotationProcessorPaths(this.javaBuilder.javaProject, isTest); - if (paths != null) { - annotationProcessorPaths.addAll(Arrays.asList(paths)); + try { + List annotationProcessorPaths = new ArrayList<>(); + List generatedSourcePaths = new ArrayList<>(); + boolean isTest = this.compilationGroup == CompilationGroup.TEST; + if (this.javaBuilder.participants != null) { + for (CompilationParticipant participant : this.javaBuilder.participants) { + if (participant.isAnnotationProcessor()) { + String[] paths = participant.getAnnotationProcessorPaths(this.javaBuilder.javaProject, isTest); + if (paths != null) { + annotationProcessorPaths.addAll(Arrays.asList(paths)); + } + String[] generatedSrc = participant.getGeneratedSourcePaths(this.javaBuilder.javaProject, isTest); + if (generatedSrc != null) { + generatedSourcePaths.addAll(Arrays.asList(generatedSrc)); + } + } + } + } + + ClasspathLocation[] classpathLocations = this.nameEnvironment.binaryLocations; + Set classpaths = new LinkedHashSet<>(); + Set modulepaths = new LinkedHashSet<>(); + for (ClasspathLocation location : classpathLocations) { + if (location instanceof ClasspathDirectory cpDirectory) { + IPath binaryLocation = cpDirectory.binaryFolder.getLocation(); + if (binaryLocation == null) { + continue; } - String[] generatedSrc = participant.getGeneratedSourcePaths(this.javaBuilder.javaProject, isTest); - if (generatedSrc != null) { - generatedSourcePaths.addAll(Arrays.asList(generatedSrc)); + + String filepath = binaryLocation.toFile().getAbsolutePath(); + if (cpDirectory.isOnModulePath) { + modulepaths.add(filepath); + } else { + classpaths.add(filepath); + } + } else if (location instanceof ClasspathJar cpJar) { + String filepath = cpJar.zipFilename; + if (cpJar.isOnModulePath) { + modulepaths.add(filepath); + } else { + classpaths.add(filepath); } } } - } - configuration.setAnnotationProcessorPaths(annotationProcessorPaths); - configuration.setGeneratedSourcePaths(generatedSourcePaths); - ClasspathLocation[] classpathLocations = this.nameEnvironment.binaryLocations; - List classpaths = new ArrayList<>(); - List modulepaths = new ArrayList<>(); - for (ClasspathLocation location : classpathLocations) { - if (location instanceof ClasspathDirectory cpDirectory) { - String filepath = cpDirectory.binaryFolder.getLocation().toFile().getAbsolutePath(); - if (cpDirectory.isOnModulePath && !modulepaths.contains(filepath)) { - modulepaths.add(filepath); - } else if (!cpDirectory.isOnModulePath && !classpaths.contains(filepath)) { - classpaths.add(filepath); + + Map sourceOutputMapping = new HashMap<>(); + Set sourcepaths = new LinkedHashSet<>(); + Set moduleSourcepaths = new LinkedHashSet<>(); + ClasspathMultiDirectory[] srcLocations = this.nameEnvironment.sourceLocations; + for (ClasspathMultiDirectory sourceLocation : srcLocations) { + IPath sourcepath = sourceLocation.sourceFolder.getLocation(); + if (sourcepath == null) { + continue; } - } else if (location instanceof ClasspathJar cpJar) { - String filepath = cpJar.zipFilename; - if (cpJar.isOnModulePath && !modulepaths.contains(filepath)) { - modulepaths.add(filepath); - } else if (!cpJar.isOnModulePath && !classpaths.contains(filepath)) { - classpaths.add(filepath); + + File sourceFolder = sourcepath.toFile(); + IPath binarypath = sourceLocation.binaryFolder.getLocation(); + if (binarypath != null) { + File outputFolder = binarypath.toFile(); + sourceOutputMapping.put(sourceFolder, outputFolder); + } + + String absoluteSourcepath = sourceFolder.getAbsolutePath(); + if (sourceLocation.isOnModulePath) { + moduleSourcepaths.add(absoluteSourcepath); + } else { + sourcepaths.add(absoluteSourcepath); } } + + return new CompilerConfiguration( + new ArrayList<>(sourcepaths), + new ArrayList<>(moduleSourcepaths), + new ArrayList<>(classpaths), + new ArrayList<>(modulepaths), + annotationProcessorPaths, + generatedSourcePaths, + sourceOutputMapping, + options.getMap()); + } catch (Exception e) { + ILog.get().error("Failed computing compiler configuration " + e); //$NON-NLS-1$ + return new CompilerConfiguration( + null, null, null, null, null, null, null, options.getMap()); } - configuration.setClasspaths(classpaths); - configuration.setModulepaths(modulepaths); - - Map sourceOutputMapping = new HashMap<>(); - List sourcepaths = new ArrayList<>(); - List moduleSourcepaths = new ArrayList<>(); - ClasspathMultiDirectory[] srcLocations = this.nameEnvironment.sourceLocations; - for (ClasspathMultiDirectory sourceLocation : srcLocations) { - File sourceFolder = sourceLocation.sourceFolder.getLocation().toFile(); - File outputFolder = sourceLocation.binaryFolder.getLocation().toFile(); - sourceOutputMapping.put(sourceFolder, outputFolder); - String sourcepath = sourceFolder.getAbsolutePath(); - if (sourceLocation.isOnModulePath && !moduleSourcepaths.contains(sourcepath)) { - moduleSourcepaths.add(sourcepath); - } else if (!sourceLocation.isOnModulePath && !sourcepaths.contains(sourcepath)) { - sourcepaths.add(sourcepath); - } - } - configuration.setSourceOutputMapping(sourceOutputMapping); - configuration.setSourcepaths(sourcepaths); - configuration.setModuleSourcepaths(moduleSourcepaths); - return configuration; } protected CompilationParticipantResult[] notifyParticipants(SourceFile[] unitsAboutToCompile) { @@ -762,9 +777,10 @@ protected void processAnnotations(CompilationParticipantResult[] results) { results[i].reset(foundAnnotations ? this.filesWithAnnotations.get(results[i].sourceFile) : null); } + boolean isEcjUsed = Compiler.class.equals(this.compiler.getClass()); // even if no files have annotations, must still tell every annotation processor in case the file used to have them for (CompilationParticipant participant : this.javaBuilder.participants) - if (this.useDefaultCompiler && participant.isAnnotationProcessor()) + if (isEcjUsed && participant.isAnnotationProcessor()) participant.processAnnotations(results); processAnnotationResults(results); }