/** * Copyright (C) 2009 STMicroelectronics * * This file is part of "Mind Compiler" is free software: you can redistribute * it and/or modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Contact: mind@ow2.org * * Authors: Matthieu Leclercq * Contributors: Julien Tous */ package org.ow2.mind.compilation.gcc; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.objectweb.fractal.adl.ADLException; import org.objectweb.fractal.adl.util.FractalADLLogManager; import org.ow2.mind.compilation.AbstractAssemblerCommand; import org.ow2.mind.compilation.AbstractCompilerCommand; import org.ow2.mind.compilation.AbstractLinkerCommand; import org.ow2.mind.compilation.AbstractPreprocessorCommand; import org.ow2.mind.compilation.AssemblerCommand; import org.ow2.mind.compilation.CompilerCommand; import org.ow2.mind.compilation.CompilerContextHelper; import org.ow2.mind.compilation.CompilerErrors; import org.ow2.mind.compilation.CompilerWrapper; import org.ow2.mind.compilation.DependencyHelper; import org.ow2.mind.compilation.ExecutionHelper; import org.ow2.mind.compilation.ExecutionHelper.ExecutionResult; import org.ow2.mind.compilation.LinkerCommand; import org.ow2.mind.compilation.PreprocessorCommand; import org.ow2.mind.error.ErrorManager; import org.ow2.mind.io.OutputFileLocator; import com.google.inject.Inject; public class GccCompilerWrapper implements CompilerWrapper { private static final String TEMP_DIR = "$TEMP_DIR"; protected static Logger depLogger = FractalADLLogManager.getLogger("dep"); protected static Logger ioLogger = FractalADLLogManager.getLogger("io"); @Inject protected ErrorManager errorManagerItf; @Inject protected OutputFileLocator outputFileLocatorItf; // --------------------------------------------------------------------------- // Implementation of the CompilerWrapper interface // --------------------------------------------------------------------------- public PreprocessorCommand newPreprocessorCommand( final Map<Object, Object> context) { return new GccPreprocessorCommand(context); } public CompilerCommand newCompilerCommand(final Map<Object, Object> context) { return new GccCompilerCommand(context); } public AssemblerCommand newAssemblerCommand(final Map<Object, Object> context) { return new GccAssemblerCommand(context); } public LinkerCommand newLinkerCommand(final Map<Object, Object> context) { return new GccLinkerCommand(context); } protected class GccPreprocessorCommand extends AbstractPreprocessorCommand { protected GccPreprocessorCommand(final Map<Object, Object> context) { super(CompilerContextHelper.getCompilerCommand(context), context); } public PreprocessorCommand addDebugFlag() { flags.add("-g"); return this; } @Override protected Collection<File> readDependencies() { return readDeps(dependencyOutputFile, outputFile, context); } public boolean exec() throws ADLException, InterruptedException { final List<String> cmd = new ArrayList<String>(); cmd.add(this.cmd); cmd.add("-E"); cmd.addAll(flags); for (final String def : defines) { cmd.add("-D" + def); } for (final File incDir : includeDir) { cmd.add("-I" + incDir.getPath().trim()); } for (final File incFile : includeFile) { cmd.add("-include"); cmd.add(incFile.getPath()); } if (dependencyOutputFile != null) { cmd.add("-MMD"); cmd.add("-MF"); cmd.add(dependencyOutputFile.getPath()); cmd.add("-MT"); cmd.add(outputFile.getPath()); } cmd.add("-o"); cmd.add(outputFile.getPath()); cmd.add(inputFile.getPath()); // save full command for debug and log purposes final StringBuilder sb = new StringBuilder(); for (final String str : cmd) { sb.append(str); sb.append(" "); } fullCmd = sb.toString(); // execute command ExecutionResult result; try { result = ExecutionHelper.exec(getDescription(), cmd); } catch (final IOException e) { errorManagerItf.logError(CompilerErrors.EXECUTION_ERROR, this.cmd); return false; } if (dependencyOutputFile != null && dependencyOutputFile.exists()) { processDependencyOutputFile(dependencyOutputFile, context); } if (result.getExitValue() != 0) { errorManagerItf.logError(CompilerErrors.COMPILER_ERROR, outputFile.getPath(), result.getOutput()); return false; } if (result.getOutput() != null) { // command returns 0 and generates an output (warning) errorManagerItf.logWarning(CompilerErrors.COMPILER_WARNING, outputFile.getPath(), result.getOutput()); } return true; } public String getDescription() { return "CPP: " + outputFile.getPath(); } @Override public String toString() { if (fullCmd == null) return super.toString(); else return fullCmd; } } protected class GccCompilerCommand extends AbstractCompilerCommand { protected GccCompilerCommand(final Map<Object, Object> context) { super(CompilerContextHelper.getCompilerCommand(context), context); } public CompilerCommand addDebugFlag() { flags.add("-g"); return this; } @Override protected Collection<File> readDependencies() { return readDeps(dependencyOutputFile, outputFile, context); } public boolean exec() throws ADLException, InterruptedException { final List<String> cmd = new ArrayList<String>(); cmd.add(this.cmd); cmd.add("-c"); cmd.addAll(flags); for (final String def : defines) { cmd.add("-D" + def); } for (final File incDir : includeDir) { cmd.add("-I" + incDir.getPath().trim()); } for (final File incFile : includeFile) { cmd.add("-include"); cmd.add(incFile.getPath()); } if (dependencyOutputFile != null) { cmd.add("-MMD"); cmd.add("-MF"); cmd.add(dependencyOutputFile.getPath()); cmd.add("-MT"); cmd.add(outputFile.getPath()); } cmd.add("-o"); cmd.add(outputFile.getPath()); cmd.add(inputFile.getPath()); // save full command for debug and log purposes final StringBuilder sb = new StringBuilder(); for (final String str : cmd) { sb.append(str); sb.append(" "); } fullCmd = sb.toString(); // execute command ExecutionResult result; try { result = ExecutionHelper.exec(getDescription(), cmd); } catch (final IOException e) { errorManagerItf.logError(CompilerErrors.EXECUTION_ERROR, this.cmd); return false; } if (dependencyOutputFile != null && dependencyOutputFile.exists()) { processDependencyOutputFile(dependencyOutputFile, context); } if (result.getExitValue() != 0) { errorManagerItf.logError(CompilerErrors.COMPILER_ERROR, outputFile.getPath(), result.getOutput()); return false; } if (result.getOutput() != null) { // command returns 0 and generates an output (warning) errorManagerItf.logWarning(CompilerErrors.COMPILER_WARNING, outputFile.getPath(), result.getOutput()); } return true; } public String getDescription() { return "GCC: " + outputFile.getPath(); } @Override public String toString() { if (fullCmd == null) return super.toString(); else return fullCmd; } } protected class GccAssemblerCommand extends AbstractAssemblerCommand { protected GccAssemblerCommand(final Map<Object, Object> context) { super(CompilerContextHelper.getAssemblerCommand(context), context); } public AssemblerCommand addDebugFlag() { flags.add("-g"); return this; } @Override protected Collection<File> readDependencies() { return readDeps(dependencyOutputFile, outputFile, context); } public boolean exec() throws ADLException, InterruptedException { final List<String> cmd = new ArrayList<String>(); cmd.add(this.cmd); cmd.add("-c"); cmd.addAll(flags); for (final String def : defines) { cmd.add("-D" + def); } for (final File incDir : includeDir) { cmd.add("-I" + incDir.getPath().trim()); } for (final File incFile : includeFile) { cmd.add("-include"); cmd.add(incFile.getPath()); } cmd.add("-o"); cmd.add(outputFile.getPath()); cmd.add(inputFile.getPath()); // save full command for debug and log purposes final StringBuilder sb = new StringBuilder(); for (final String str : cmd) { sb.append(str); sb.append(" "); } fullCmd = sb.toString(); // execute command ExecutionResult result; try { result = ExecutionHelper.exec(getDescription(), cmd); } catch (final IOException e) { errorManagerItf.logError(CompilerErrors.EXECUTION_ERROR, this.cmd); return false; } if (result.getExitValue() != 0) { errorManagerItf.logError(CompilerErrors.COMPILER_ERROR, outputFile.getPath(), result.getOutput()); return false; } if (result.getOutput() != null) { // command returns 0 and generates an output (warning) errorManagerItf.logWarning(CompilerErrors.COMPILER_WARNING, outputFile.getPath(), result.getOutput()); } return true; } public String getDescription() { return "AS: " + outputFile.getPath(); } @Override public String toString() { if (fullCmd == null) return super.toString(); else return fullCmd; } } protected class GccLinkerCommand extends AbstractLinkerCommand { protected GccLinkerCommand(final Map<Object, Object> context) { super(CompilerContextHelper.getLinkerCommand(context), context); } public LinkerCommand addDebugFlag() { flags.add("-g"); return this; } public boolean exec() throws ADLException, InterruptedException { final List<String> cmd = new ArrayList<String>(); cmd.add(this.cmd); cmd.add("-o"); cmd.add(outputFile.getPath()); // archive files (i.e. '.a' files) are added at the end of the command // line. List<String> archiveFiles = null; for (final File inputFile : inputFiles) { final String path = inputFile.getPath(); if (path.endsWith(".a")) { if (archiveFiles == null) archiveFiles = new ArrayList<String>(); archiveFiles.add(path); } else { cmd.add(path); } } if (archiveFiles != null) { for (final String path : archiveFiles) { cmd.add(path); } } if (linkerScript != null) { cmd.add("-T"); cmd.add(linkerScript); } cmd.addAll(flags); // save full command for debug and log purposes final StringBuilder sb = new StringBuilder(); for (final String str : cmd) { sb.append(str); sb.append(" "); } fullCmd = sb.toString(); // execute command ExecutionResult result; try { result = ExecutionHelper.exec(getDescription(), cmd); } catch (final IOException e) { errorManagerItf.logError(CompilerErrors.EXECUTION_ERROR, this.cmd); return false; } if (result.getExitValue() != 0) { errorManagerItf.logError(CompilerErrors.LINKER_ERROR, outputFile.getPath(), result.getOutput()); return false; } if (result.getOutput() != null) { // command returns 0 and generates an output (warning) errorManagerItf.logWarning(CompilerErrors.LINKER_WARNING, outputFile.getPath(), result.getOutput()); } return true; } public String getDescription() { return "LD : " + outputFile.getPath(); } @Override public String toString() { if (fullCmd == null) return super.toString(); else return fullCmd; } } protected void processDependencyOutputFile(final File dependencyOutputFile, final Map<Object, Object> context) throws ADLException { try { final String tempDir = outputFileLocatorItf.getCSourceTemporaryOutputDir( context).getCanonicalPath(); final Map<File, List<File>> deps = DependencyHelper .parseDepFile(dependencyOutputFile); final Map<File, List<File>> newDeps = new HashMap<File, List<File>>( deps.size()); for (final Map.Entry<File, List<File>> dep : deps.entrySet()) { final File target = new File(dep.getKey().getCanonicalPath() .replace(tempDir, TEMP_DIR)); final List<File> depFiles = new ArrayList<File>(dep.getValue().size()); for (final File depFile : dep.getValue()) { depFiles.add(new File(depFile.getCanonicalPath().replace(tempDir, TEMP_DIR))); } newDeps.put(target, depFiles); } DependencyHelper.writeDepFile(dependencyOutputFile, newDeps); } catch (final IOException ioe) { if (depLogger.isLoggable(Level.WARNING)) depLogger .warning("Error while processing dependency file '" + dependencyOutputFile + "' remove it to force future compilation."); if (depLogger.isLoggable(Level.FINE)) depLogger.log(Level.FINE, "Error while processing dependency file '" + dependencyOutputFile + ":", ioe); } } private Collection<File> readDeps(final File dependencyOutputFile, final File outputFile, final Map<Object, Object> context) { if (!dependencyOutputFile.exists()) { if (depLogger.isLoggable(Level.FINE)) depLogger.fine("Dependency file '" + dependencyOutputFile + "' does not exist, force compilation."); return null; } final Map<File, List<File>> depMap = DependencyHelper .parseDepFile(dependencyOutputFile); if (depMap == null) { if (depLogger.isLoggable(Level.FINE)) depLogger.fine("Error in dependency file of '" + outputFile + "', recompile."); return null; } // process depMap to replace $TEMP_DIR occurrences final Map<File, List<File>> filteredDepMap; final String tempDir = outputFileLocatorItf.getCSourceTemporaryOutputDir( context).getPath(); if (tempDir != null) { filteredDepMap = new HashMap<File, List<File>>(depMap.size()); for (final Map.Entry<File, List<File>> entry : depMap.entrySet()) { File key = entry.getKey(); if (key.getPath().contains(TEMP_DIR)) { key = new File(key.getPath().replace(TEMP_DIR, tempDir)); } final List<File> value = new ArrayList<File>(entry.getValue().size()); for (File dep : entry.getValue()) { if (dep.getPath().contains(TEMP_DIR)) { dep = new File(dep.getPath().replace(TEMP_DIR, tempDir)); } value.add(dep); } filteredDepMap.put(key, value); } } else { filteredDepMap = depMap; } if (filteredDepMap.size() == 1) { // Only one rule, assume is it the right one return filteredDepMap.values().iterator().next(); } Collection<File> depFiles = filteredDepMap.get(outputFile); if (depFiles == null) { // try with absolute path depFiles = filteredDepMap.get(outputFile.getAbsoluteFile()); if (depFiles == null) { // try with single file name depFiles = filteredDepMap.get(new File(outputFile.getName())); if (depFiles == null) { // if depFiles is null (i.e. the dependencyFile is invalid), // recompile. if (depLogger.isLoggable(Level.WARNING)) depLogger.warning("Invalid dependency file '" + dependencyOutputFile + "'. Can't find rule for target '" + outputFile + "', recompile."); return null; } } } return depFiles; } }