package com.siberika.idea.pascal.jps.builder; import com.intellij.execution.process.BaseOSProcessHandler; import com.intellij.execution.process.ProcessAdapter; import com.intellij.openapi.util.io.FileUtil; import com.siberika.idea.pascal.jps.compiler.CompilerMessager; import com.siberika.idea.pascal.jps.compiler.DelphiBackendCompiler; import com.siberika.idea.pascal.jps.compiler.FPCBackendCompiler; import com.siberika.idea.pascal.jps.compiler.PascalBackendCompiler; import com.siberika.idea.pascal.jps.model.JpsPascalSdkType; import com.siberika.idea.pascal.jps.sdk.PascalCompilerFamily; import com.siberika.idea.pascal.jps.sdk.PascalSdkData; import com.siberika.idea.pascal.jps.util.ParamMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.ModuleChunk; 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.incremental.BuilderCategory; import org.jetbrains.jps.incremental.CompileContext; import org.jetbrains.jps.incremental.ModuleBuildTarget; import org.jetbrains.jps.incremental.ModuleLevelBuilder; import org.jetbrains.jps.incremental.ProjectBuildException; import org.jetbrains.jps.incremental.messages.BuildMessage; import org.jetbrains.jps.incremental.messages.CompilerMessage; import org.jetbrains.jps.model.java.JpsJavaExtensionService; import org.jetbrains.jps.model.library.JpsOrderRootType; import org.jetbrains.jps.model.library.sdk.JpsSdk; import org.jetbrains.jps.model.module.JpsModule; import org.jetbrains.jps.model.module.JpsModuleSourceRoot; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Author: George Bakhtadze * Date: 11/02/2014 */ @Deprecated // Target builder should be used instead public class PascalModuleLevelBuilder extends ModuleLevelBuilder { private static final String NAME = "Pascal Builder"; public PascalModuleLevelBuilder() { super(BuilderCategory.OVERWRITING_TRANSLATOR); } @Override public List<String> getCompilableFileExtensions() { return PascalBuilderService.COMPILABLE_EXTENSIONS; } @NotNull @Override public String getPresentableName() { return NAME; } @Override public ExitCode build(final CompileContext context, ModuleChunk chunk, DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder, OutputConsumer outputConsumer) throws ProjectBuildException, IOException { final Map<ModuleBuildTarget, List<File>> files = collectChangedFiles(context, dirtyFilesHolder); if (files.isEmpty() && !JavaBuilderUtil.isForcedRecompilationAllJavaModules(context)) { context.processMessage(new CompilerMessage(getPresentableName(), BuildMessage.Kind.INFO, "No changes detected")); return ExitCode.NOTHING_DONE; } CompilerMessager messager = new PascalCompilerMessager(getPresentableName(), context); for (ModuleBuildTarget target : chunk.getTargets()) { JpsModule module = target.getModule(); JpsSdk<?> sdk = module.getSdk(JpsPascalSdkType.INSTANCE); if (sdk != null) { PascalBackendCompiler compiler = getCompiler(sdk, messager); if (compiler != null) { messager.info("Compiler family:" + compiler.getId(), "", -1l, -1); List<File> sdkFiles = sdk.getParent().getFiles(JpsOrderRootType.COMPILED); sdkFiles.addAll(sdk.getParent().getFiles(JpsOrderRootType.SOURCES)); File outputDir = getBuildOutputDirectory(module, target.isTests(), context); for (File file : files.get(target)) { File compiled = new File(outputDir, FileUtil.getNameWithoutExtension(file) + compiler.getCompiledUnitExt()); messager.info(String.format("Map: %s => %s ", file.getCanonicalPath(), compiled.getCanonicalPath()), null, -1l, -1l); outputConsumer.registerOutputFile(chunk.representativeTarget(), compiled, Collections.singleton(file.getCanonicalPath())); } String[] cmdLine = compiler.createStartupCommand(sdk.getHomePath(), module.getName(), outputDir.getAbsolutePath(), sdkFiles, getFiles(module.getSourceRoots()), files.get(target), ParamMap.getJpsParams(module.getProperties()), JavaBuilderUtil.isForcedRecompilationAllJavaModules(context), ParamMap.getJpsParams(sdk.getSdkProperties())); if (cmdLine != null) { int exitCode = launchCompiler(compiler, messager, cmdLine); if (exitCode != 0) { messager.error("Error. Compiler exit code: " + exitCode, null, -1l, -1l); return ExitCode.ABORT; } } else { return ExitCode.ABORT; } } else { messager.error("Can't determine compiler family", "", -1l, -1l); return ExitCode.ABORT; } } else { log(context, "Pascal SDK is not defined for module " + module.getName()); return ExitCode.ABORT; } } return ExitCode.OK; } @Nullable private PascalBackendCompiler getCompiler(@NotNull JpsSdk<?> sdk, CompilerMessager messager) { ParamMap params = ParamMap.getJpsParams(sdk.getSdkProperties()); String family = params != null ? params.get(PascalSdkData.Keys.COMPILER_FAMILY.getKey()) : null; if (PascalCompilerFamily.FPC.toString().equals(family)) { return new FPCBackendCompiler(messager); } else if (PascalCompilerFamily.DELPHI.toString().equals(family)) { return new DelphiBackendCompiler(messager); } return null; } private int launchCompiler(PascalBackendCompiler compiler, CompilerMessager messager, String[] cmdLine) throws IOException { messager.info("Command line: ", null, -1l, -1l); for (String s : cmdLine) { messager.info(s, null, -1l, -1l); } Process process = Runtime.getRuntime().exec(cmdLine); BaseOSProcessHandler handler = new BaseOSProcessHandler(process, "", Charset.defaultCharset()); ProcessAdapter adapter = compiler.getCompilerProcessAdapter(messager); handler.addProcessListener(adapter); handler.startNotify(); handler.waitFor(); return process.exitValue(); } private List<File> getFiles(List<JpsModuleSourceRoot> sourceRoots) { List<File> result = new ArrayList<File>(); for (JpsModuleSourceRoot root : sourceRoots) { result.add(root.getFile()); } return result; } private static File getBuildOutputDirectory(@NotNull JpsModule module, boolean forTests, @NotNull CompileContext context) throws ProjectBuildException { JpsJavaExtensionService instance = JpsJavaExtensionService.getInstance(); File outputDirectory = instance.getOutputDirectory(module, forTests); if (outputDirectory == null) { context.processMessage(new CompilerMessage(NAME, BuildMessage.Kind.ERROR, "No output dir for module " + module.getName())); } else { if (!outputDirectory.exists()) { FileUtil.createDirectory(outputDirectory); } } return outputDirectory; } private static void log(CompileContext context, String text) { context.processMessage(new CompilerMessage(NAME, BuildMessage.Kind.INFO, text)); } private static Map<ModuleBuildTarget, List<File>> collectChangedFiles(CompileContext context, DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder) throws IOException { final Map<ModuleBuildTarget, List<File>> result = new HashMap<ModuleBuildTarget, List<File>>(); dirtyFilesHolder.processDirtyFiles(new FileProcessor<JavaSourceRootDescriptor, ModuleBuildTarget>() { public boolean apply(ModuleBuildTarget target, File file, JavaSourceRootDescriptor sourceRoot) throws IOException { final String path = file.getPath(); if (isPascalFile(path)) { //todo file type check List<File> toCompile = result.get(target); if (null == toCompile) { toCompile = new ArrayList<File>(); result.put(target, toCompile); } toCompile.add(file); } return true; } }); return result; } private static boolean isPascalFile(String path) { for (String ext : PascalBuilderService.COMPILABLE_EXTENSIONS) { if (path.endsWith("." + ext)) { return true; } } return false; } }