blob: e6ab8747c2a39facac4c747e67b4b915125e5510 [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jps.appengine.build;
import com.intellij.appengine.rt.EnhancerRunner;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.execution.ParametersListUtil;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.ModuleChunk;
import org.jetbrains.jps.appengine.model.JpsAppEngineExtensionService;
import org.jetbrains.jps.appengine.model.JpsAppEngineModuleExtension;
import org.jetbrains.jps.appengine.model.PersistenceApi;
import org.jetbrains.jps.builders.DirtyFilesHolder;
import org.jetbrains.jps.builders.FileProcessor;
import org.jetbrains.jps.builders.java.JavaBuilderUtil;
import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor;
import org.jetbrains.jps.builders.logging.ProjectBuilderLogger;
import org.jetbrains.jps.incremental.*;
import org.jetbrains.jps.incremental.messages.BuildMessage;
import org.jetbrains.jps.incremental.messages.CompilerMessage;
import org.jetbrains.jps.incremental.messages.ProgressMessage;
import org.jetbrains.jps.model.JpsDummyElement;
import org.jetbrains.jps.model.java.JpsJavaExtensionService;
import org.jetbrains.jps.model.java.JpsJavaSdkType;
import org.jetbrains.jps.model.library.sdk.JpsSdk;
import org.jetbrains.jps.model.module.JpsModule;
import org.jetbrains.jps.util.JpsPathUtil;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
/**
* @author nik
*/
public class AppEngineEnhancerBuilder extends ModuleLevelBuilder {
public static final String NAME = "Google AppEngine Enhancer";
public AppEngineEnhancerBuilder() {
super(BuilderCategory.CLASS_POST_PROCESSOR);
}
@Override
public ExitCode build(final CompileContext context,
ModuleChunk chunk,
DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder,
OutputConsumer outputConsumer)
throws ProjectBuildException, IOException {
boolean doneSomething = false;
for (final JpsModule module : chunk.getModules()) {
JpsAppEngineModuleExtension extension = JpsAppEngineExtensionService.getInstance().getExtension(module);
if (extension != null && extension.isRunEnhancerOnMake()) {
doneSomething |= processModule(context, dirtyFilesHolder, extension);
}
}
return doneSomething ? ExitCode.OK : ExitCode.NOTHING_DONE;
}
@Override
public List<String> getCompilableFileExtensions() {
return Collections.emptyList();
}
private static boolean processModule(final CompileContext context,
DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder,
JpsAppEngineModuleExtension extension) throws IOException, ProjectBuildException {
final Set<File> roots = new THashSet<File>(FileUtil.FILE_HASHING_STRATEGY);
for (String path : extension.getFilesToEnhance()) {
roots.add(new File(FileUtil.toSystemDependentName(path)));
}
final List<String> pathsToProcess = new ArrayList<String>();
dirtyFilesHolder.processDirtyFiles(new FileProcessor<JavaSourceRootDescriptor, ModuleBuildTarget>() {
@Override
public boolean apply(ModuleBuildTarget target, File file, JavaSourceRootDescriptor root) throws IOException {
if (JpsPathUtil.isUnder(roots, file)) {
Collection<String> outputs = context.getProjectDescriptor().dataManager.getSourceToOutputMap(target).getOutputs(file.getAbsolutePath());
if (outputs != null) {
pathsToProcess.addAll(outputs);
}
}
return true;
}
});
if (pathsToProcess.isEmpty()) {
return false;
}
JpsModule module = extension.getModule();
JpsSdk<JpsDummyElement> sdk = JavaBuilderUtil.ensureModuleHasJdk(module, context, NAME);
context.processMessage(new ProgressMessage("Enhancing classes in module '" + module.getName() + "'..."));
List<String> vmParams = Collections.singletonList("-Xmx256m");
List<String> classpath = new ArrayList<String>();
classpath.add(extension.getToolsApiJarPath());
classpath.add(PathManager.getJarPathForClass(EnhancerRunner.class));
boolean removeOrmJars = Boolean.parseBoolean(System.getProperty("jps.appengine.enhancer.remove.orm.jars", "true"));
for (File file : JpsJavaExtensionService.dependencies(module).recursively().compileOnly().productionOnly().classes().getRoots()) {
if (removeOrmJars && FileUtil.isAncestor(new File(extension.getOrmLibPath()), file, true)) {
continue;
}
classpath.add(file.getAbsolutePath());
}
List<String> programParams = new ArrayList<String>();
final File argsFile = FileUtil.createTempFile("appEngineEnhanceFiles", ".txt");
PrintWriter writer = new PrintWriter(argsFile);
try {
for (String path : pathsToProcess) {
writer.println(FileUtil.toSystemDependentName(path));
}
}
finally {
writer.close();
}
programParams.add(argsFile.getAbsolutePath());
programParams.add("com.google.appengine.tools.enhancer.Enhance");
programParams.add("-api");
PersistenceApi api = extension.getPersistenceApi();
programParams.add(api.getEnhancerApiName());
if (api.getEnhancerVersion() == 2) {
programParams.add("-enhancerVersion");
programParams.add("v2");
}
programParams.add("-v");
List<String> commandLine = ExternalProcessUtil.buildJavaCommandLine(JpsJavaSdkType.getJavaExecutable(sdk), EnhancerRunner.class.getName(),
Collections.<String>emptyList(), classpath, vmParams, programParams);
Process process = new ProcessBuilder(commandLine).start();
ExternalEnhancerProcessHandler handler = new ExternalEnhancerProcessHandler(process, commandLine, context);
handler.startNotify();
handler.waitFor();
ProjectBuilderLogger logger = context.getLoggingManager().getProjectBuilderLogger();
if (logger.isEnabled()) {
logger.logCompiledPaths(pathsToProcess, NAME, "Enhancing classes:");
}
return true;
}
@NotNull
@Override
public String getPresentableName() {
return NAME;
}
private static class ExternalEnhancerProcessHandler extends EnhancerProcessHandlerBase {
private final CompileContext myContext;
public ExternalEnhancerProcessHandler(Process process, List<String> commandLine, CompileContext context) {
super(process, ParametersListUtil.join(commandLine), null);
myContext = context;
}
@Override
protected void reportInfo(String message) {
myContext.processMessage(new CompilerMessage(NAME, BuildMessage.Kind.INFO, message));
}
@Override
protected void reportError(String message) {
myContext.processMessage(new CompilerMessage(NAME, BuildMessage.Kind.ERROR, message));
}
}
}