diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacCompiler.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacCompiler.java index 7a8ad43b48e..7650c377767 100644 --- a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacCompiler.java +++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacCompiler.java @@ -11,15 +11,14 @@ package org.eclipse.jdt.internal.javac; import java.nio.charset.Charset; -import java.util.Objects; import java.util.stream.Stream; -import javax.tools.DiagnosticListener; import javax.tools.JavaFileObject; import javax.tools.JavaFileObject.Kind; import org.eclipse.core.resources.IResource; import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.compiler.CompilerConfiguration; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.Compiler; import org.eclipse.jdt.internal.compiler.ICompilerRequestor; @@ -27,7 +26,6 @@ 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.tool.EclipseFileObject; import org.eclipse.jdt.internal.core.JavaProject; import org.eclipse.jdt.internal.core.builder.SourceFile; @@ -37,10 +35,12 @@ import com.sun.tools.javac.util.List; public class JavacCompiler extends Compiler { + CompilerConfiguration compilerConfig; - public JavacCompiler(INameEnvironment environment, IErrorHandlingPolicy policy, CompilerOptions options, + public JavacCompiler(INameEnvironment environment, IErrorHandlingPolicy policy, CompilerConfiguration compilerConfig, ICompilerRequestor requestor, IProblemFactory problemFactory) { - super(environment, policy, options, requestor, problemFactory); + super(environment, policy, compilerConfig.getOptions(), requestor, problemFactory); + this.compilerConfig = compilerConfig; } @Override @@ -56,7 +56,7 @@ public void compile(ICompilationUnit[] sourceUnits) { // res.setProblems(newProblems); // } // }); - JavacUtils.configureJavacContext(javacContext, this.options.getMap(), Stream.of(sourceUnits) + JavacUtils.configureJavacContext(javacContext, this.compilerConfig, Stream.of(sourceUnits) .filter(SourceFile.class::isInstance) .map(SourceFile.class::cast) .map(source -> source.resource) diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacUtils.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacUtils.java index 5158b0dc6c5..59cdad9ec78 100644 --- a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacUtils.java +++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacUtils.java @@ -25,6 +25,7 @@ import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.compiler.CompilerConfiguration; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.core.JavaProject; @@ -37,20 +38,37 @@ public class JavacUtils { public static void configureJavacContext(Context context, Map compilerOptions, IJavaProject javaProject) { + configureJavacContext(context, compilerOptions, javaProject, null); + } + + public static void configureJavacContext(Context context, CompilerConfiguration compilerConfig, IJavaProject javaProject) { + configureJavacContext(context, compilerConfig.getOptions().getMap(), javaProject, compilerConfig); + } + + private static void configureJavacContext(Context context, Map compilerOptions, IJavaProject javaProject, CompilerConfiguration compilerConfig) { Options options = Options.instance(context); options.put(Option.XLINT, Boolean.TRUE.toString()); // TODO refine according to compilerOptions if (Boolean.parseBoolean(compilerOptions.get(CompilerOptions.OPTION_EnablePreviews))) { options.put(Option.PREVIEW, Boolean.toString(true)); } String release = compilerOptions.get(CompilerOptions.OPTION_Release); - if (release != null) { - options.put(Option.RELEASE, release); + String compliance = compilerOptions.get(CompilerOptions.OPTION_Compliance); + if (CompilerOptions.ENABLED.equals(release) && compliance != null && !compliance.isEmpty()) { + options.put(Option.RELEASE, compliance); + } + String source = compilerOptions.get(CompilerOptions.OPTION_Source); + if (source != null && !source.isEmpty()) { + options.put(Option.SOURCE, source); + } + String target = compilerOptions.get(CompilerOptions.OPTION_TargetPlatform); + if (target != null && !target.isEmpty()) { + options.put(Option.TARGET, target); } options.put(Option.XLINT_CUSTOM, "all"); // TODO refine according to compilerOptions // TODO populate more from compilerOptions and/or project settings JavacFileManager.preRegister(context); if (javaProject instanceof JavaProject internal) { - configurePaths(internal, context); + configurePaths(internal, context, compilerConfig); } Todo.instance(context); // initialize early com.sun.tools.javac.main.JavaCompiler javac = new com.sun.tools.javac.main.JavaCompiler(context); @@ -59,23 +77,69 @@ public static void configureJavacContext(Context context, Map co javac.lineDebugInfo = true; } - private static void configurePaths(JavaProject javaProject, Context context) { + private static void configurePaths(JavaProject javaProject, Context context, CompilerConfiguration compilerConfig) { JavacFileManager fileManager = (JavacFileManager)context.get(JavaFileManager.class); try { - if (javaProject.getJavaProject() != null) { + if (compilerConfig != null && !compilerConfig.getSourceOutputMapping().isEmpty()) { + fileManager.setLocation(StandardLocation.CLASS_OUTPUT, compilerConfig.getSourceOutputMapping().values().stream().distinct().toList()); + } else if (javaProject.getJavaProject() != null) { IResource member = javaProject.getProject().getParent().findMember(javaProject.getOutputLocation()); if( member != null ) { File f = member.getLocation().toFile(); fileManager.setLocation(StandardLocation.CLASS_OUTPUT, List.of(f)); } } - fileManager.setLocation(StandardLocation.SOURCE_PATH, classpathEntriesToFiles(javaProject, entry -> entry.getEntryKind() == IClasspathEntry.CPE_SOURCE)); - fileManager.setLocation(StandardLocation.CLASS_PATH, classpathEntriesToFiles(javaProject, entry -> entry.getEntryKind() != IClasspathEntry.CPE_SOURCE)); + + boolean sourcePathEnabled = false; + if (compilerConfig != null && !isEmpty(compilerConfig.getSourcepaths())) { + fileManager.setLocation(StandardLocation.SOURCE_PATH, + compilerConfig.getSourcepaths() + .stream() + .map(File::new) + .toList()); + sourcePathEnabled = true; + } + if (compilerConfig != null && !isEmpty(compilerConfig.getModuleSourcepaths())) { + fileManager.setLocation(StandardLocation.MODULE_SOURCE_PATH, + compilerConfig.getModuleSourcepaths() + .stream() + .map(File::new) + .toList()); + sourcePathEnabled = true; + } + if (!sourcePathEnabled) { + fileManager.setLocation(StandardLocation.SOURCE_PATH, classpathEntriesToFiles(javaProject, entry -> entry.getEntryKind() == IClasspathEntry.CPE_SOURCE)); + } + + boolean classpathEnabled = false; + if (compilerConfig != null && !isEmpty(compilerConfig.getClasspaths())) { + fileManager.setLocation(StandardLocation.CLASS_PATH, + compilerConfig.getClasspaths() + .stream() + .map(File::new) + .toList()); + classpathEnabled = true; + } + if (compilerConfig != null && !isEmpty(compilerConfig.getModulepaths())) { + fileManager.setLocation(StandardLocation.MODULE_PATH, + compilerConfig.getModulepaths() + .stream() + .map(File::new) + .toList()); + classpathEnabled = true; + } + if (!classpathEnabled) { + fileManager.setLocation(StandardLocation.CLASS_PATH, classpathEntriesToFiles(javaProject, entry -> entry.getEntryKind() != IClasspathEntry.CPE_SOURCE)); + } } catch (Exception ex) { ILog.get().error(ex.getMessage(), ex); } } + private static boolean isEmpty(List list) { + return list == null || list.isEmpty(); + } + private static List classpathEntriesToFiles(JavaProject project, Predicate select) { try { IClasspathEntry[] selected = Arrays.stream(project.getRawClasspath()) 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 new file mode 100644 index 00000000000..56d626c9a97 --- /dev/null +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/compiler/CompilerConfiguration.java @@ -0,0 +1,77 @@ +/******************************************************************************* +* 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; + +public class CompilerConfiguration { + List sourcepaths; + List moduleSourcepaths; + List classpaths; + List modulepaths; + Map sourceOutputMapping; + CompilerOptions options; + + public List getClasspaths() { + return this.classpaths; + } + + public void setClasspaths(List classpaths) { + this.classpaths = classpaths; + } + + public List getModulepaths() { + return this.modulepaths; + } + + public void setModulepaths(List modulepaths) { + this.modulepaths = modulepaths; + } + + public List getSourcepaths() { + return this.sourcepaths; + } + + public void setSourcepaths(List sourcepaths) { + this.sourcepaths = sourcepaths; + } + + public List getModuleSourcepaths() { + return this.moduleSourcepaths; + } + + public void setModuleSourcepaths(List moduleSourcepaths) { + this.moduleSourcepaths = moduleSourcepaths; + } + + public Map getSourceOutputMapping() { + return this.sourceOutputMapping; + } + + public void setSourceOutputMapping(Map sourceOutputMapping) { + this.sourceOutputMapping = sourceOutputMapping; + } + + public CompilerOptions getOptions() { + return this.options; + } + + public void setOptions(CompilerOptions options) { + this.options = options; + } +} 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 afc1371b394..14a28e92830 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 @@ -583,12 +583,12 @@ protected Compiler newCompiler() { Constructor constructor = compilerClass.getDeclaredConstructor( INameEnvironment.class, IErrorHandlingPolicy.class, - CompilerOptions.class, + CompilerConfiguration.class, ICompilerRequestor.class, IProblemFactory.class); newCompiler = constructor.newInstance(this.nameEnvironment, DefaultErrorHandlingPolicies.proceedWithAllProblems(), - compilerOptions, + prepareCompilerConfiguration(compilerOptions), this, ProblemFactory.getProblemFactory(Locale.getDefault())); } catch (ClassNotFoundException e) { @@ -624,6 +624,54 @@ protected Compiler newCompiler() { return newCompiler; } +private CompilerConfiguration prepareCompilerConfiguration(CompilerOptions options) { + CompilerConfiguration configuration = new CompilerConfiguration(); + configuration.setOptions(options); + + 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); + } + } 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); + } + } + } + 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) { CompilationParticipantResult[] results = new CompilationParticipantResult[unitsAboutToCompile.length]; for (int i = unitsAboutToCompile.length; --i >= 0;)