From 585db98cfa82d2a0b85c214aa931adb47d4e2198 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 26 Jun 2024 16:17:13 +0800 Subject: [PATCH] refresh javac output to make sure Java project system recognize the output changes --- .../jdt/internal/javac/JavacCompiler.java | 7 ++ .../jdt/internal/javac/JavacConfig.java | 9 ++- .../jdt/internal/javac/JavacTaskListener.java | 76 +++++++++++++++++++ 3 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacTaskListener.java 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 224042d750d..b358b9fb770 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 @@ -42,6 +42,7 @@ import org.eclipse.jdt.internal.core.JavaProject; import org.eclipse.jdt.internal.core.builder.SourceFile; +import com.sun.tools.javac.api.MultiTaskListener; import com.sun.tools.javac.comp.*; import com.sun.tools.javac.comp.CompileStates.CompileState; import com.sun.tools.javac.main.JavaCompiler; @@ -100,6 +101,12 @@ public void compile(ICompilationUnit[] sourceUnits) { return true; }) .collect(Collectors.groupingBy(this::computeOutputDirectory)); + + // Register listener to intercept intermediate results from Javac task. + JavacTaskListener resultListener = new JavacTaskListener(this.compilerConfig, outputSourceMapping); + MultiTaskListener mtl = MultiTaskListener.instance(javacContext); + mtl.add(resultListener); + for (Entry> outputSourceSet : outputSourceMapping.entrySet()) { var outputFile = outputSourceSet.getKey(); JavacUtils.configureJavacContext(javacContext, this.compilerConfig, javaProject, outputFile); diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacConfig.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacConfig.java index 97940374875..e81987d4e15 100644 --- a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacConfig.java +++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacConfig.java @@ -57,7 +57,11 @@ public record JavacConfig( * The compiler options used to control the compilation behavior. * See {@link org.eclipse.jdt.internal.compiler.impl.CompilerOptions} for a list of available options. */ - CompilerOptions compilerOptions) { + CompilerOptions compilerOptions, + /** + * A reference to the original config + */ + CompilerConfiguration originalConfig) { static JavacConfig createFrom(CompilerConfiguration config) { return new JavacConfig( @@ -68,6 +72,7 @@ static JavacConfig createFrom(CompilerConfiguration config) { config.annotationProcessorPaths().stream().map(URI::getPath).collect(Collectors.toList()), config.generatedSourcePaths().stream().map(IContainer::getRawLocation).filter(path -> path != null).map(IPath::toOSString).collect(Collectors.toList()), config.sourceOutputMapping().entrySet().stream().collect(Collectors.toMap(e -> e.getKey().getRawLocation().toFile(), e -> e.getValue().getRawLocation().toFile())), - config.compilerOptions()); + config.compilerOptions(), + config); } } diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacTaskListener.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacTaskListener.java new file mode 100644 index 00000000000..6455c4d9717 --- /dev/null +++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacTaskListener.java @@ -0,0 +1,76 @@ +/******************************************************************************* +* 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.javac; + +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +import javax.lang.model.element.TypeElement; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; +import org.eclipse.jdt.internal.compiler.util.SuffixConstants; + +import com.sun.source.util.TaskEvent; +import com.sun.source.util.TaskListener; +import com.sun.tools.javac.code.Symbol.ClassSymbol; + +public class JavacTaskListener implements TaskListener { + private Map sourceOutputMapping = new HashMap<>(); + + public JavacTaskListener(JavacConfig config, Map> outputSourceMapping) { + Map outputs = config.originalConfig().sourceOutputMapping().values().stream() + .distinct().filter(container -> container.getRawLocation() != null) + .collect(Collectors.toMap(container -> container.getRawLocation().toFile(), container -> container)); + for (Entry> entry : outputSourceMapping.entrySet()) { + if (outputs.containsKey(entry.getKey())) { + IContainer currentOutput = outputs.get(entry.getKey()); + entry.getValue().forEach(cu -> sourceOutputMapping.put(cu, currentOutput)); + } + } + } + + @Override + public void finished(TaskEvent e) { + if (e.getKind() == TaskEvent.Kind.GENERATE) { + if (e.getSourceFile() instanceof JavacFileObject sourceFile) { + ICompilationUnit originalUnit = sourceFile.getOriginalUnit(); + IContainer outputDir = this.sourceOutputMapping.get(originalUnit); + if (outputDir != null) { + TypeElement element = e.getTypeElement(); + if (element instanceof ClassSymbol clazz) { + String fileName = clazz.flatName().toString().replace('.', File.separatorChar); + IPath filePath = new Path(fileName); + IFile classFile = outputDir.getFile(filePath.addFileExtension(SuffixConstants.EXTENSION_class)); + try { + // refresh the class file to make sure it is visible in the Eclipse workspace + classFile.refreshLocal(IResource.DEPTH_ZERO, null); + } catch (CoreException e1) { + // TODO error handling + } + } + } + } + } + } +}