/* * 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<>(FileUtil.FILE_HASHING_STRATEGY); for (String path : extension.getFilesToEnhance()) { roots.add(new File(FileUtil.toSystemDependentName(path))); } final List<String> pathsToProcess = new ArrayList<>(); 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<>(); 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<>(); 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.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)); } } }