View Javadoc

1   /*
2    * #%L
3    * Native ARchive plugin for Maven
4    * %%
5    * Copyright (C) 2002 - 2014 NAR Maven Plugin developers.
6    * %%
7    * Licensed under the Apache License, Version 2.0 (the "License");
8    * you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   * 
11   * http://www.apache.org/licenses/LICENSE-2.0
12   * 
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   * #L%
19   */
20  package com.github.maven_nar.cpptasks.compiler;
21  
22  import java.io.BufferedReader;
23  import java.io.File;
24  import java.io.FileReader;
25  import java.io.IOException;
26  import java.io.Reader;
27  import java.util.Vector;
28  
29  import org.apache.commons.io.FilenameUtils;
30  
31  import com.github.maven_nar.cpptasks.CCTask;
32  import com.github.maven_nar.cpptasks.CUtil;
33  import com.github.maven_nar.cpptasks.CompilerDef;
34  import com.github.maven_nar.cpptasks.DependencyInfo;
35  import com.github.maven_nar.cpptasks.ProcessorDef;
36  import com.github.maven_nar.cpptasks.TargetDef;
37  import com.github.maven_nar.cpptasks.VersionInfo;
38  import com.github.maven_nar.cpptasks.parser.Parser;
39  
40  /**
41   * An abstract compiler implementation.
42   *
43   * @author Adam Murdoch
44   * @author Curt Arnold
45   */
46  public abstract class AbstractCompiler extends AbstractProcessor implements Compiler {
47    private static final String[] emptyIncludeArray = new String[0];
48    private final String outputSuffix;
49    protected File workDir;
50    protected File objDir;
51  
52    protected AbstractCompiler(final String[] sourceExtensions, final String[] headerExtensions, final String outputSuffix) {
53      super(sourceExtensions, headerExtensions);
54      this.outputSuffix = outputSuffix;
55    }
56  
57    /**
58     * Checks file name to see if parse should be attempted
59     *
60     * Default implementation returns false for files with extensions '.dll',
61     * 'tlb', '.res'
62     *
63     */
64    protected boolean canParse(final File sourceFile) {
65      final String sourceName = sourceFile.toString();
66      final int lastPeriod = sourceName.lastIndexOf('.');
67      if (lastPeriod >= 0 && lastPeriod == sourceName.length() - 4) {
68        final String ext = sourceName.substring(lastPeriod).toUpperCase();
69        if (ext.equals(".DLL") || ext.equals(".TLB") || ext.equals(".RES")) {
70          return false;
71        }
72      }
73      return true;
74    }
75  
76    abstract protected CompilerConfiguration createConfiguration(CCTask task, LinkType linkType,
77        ProcessorDef[] baseConfigs, CompilerDef specificConfig, TargetDef targetPlatform, VersionInfo versionInfo);
78  
79    @Override
80    public ProcessorConfiguration createConfiguration(final CCTask task, final LinkType linkType,
81        final ProcessorDef[] baseConfigs, final ProcessorDef specificConfig, final TargetDef targetPlatform,
82        final VersionInfo versionInfo) {
83      if (specificConfig == null) {
84        throw new NullPointerException("specificConfig");
85      }
86      return createConfiguration(task, linkType, baseConfigs, (CompilerDef) specificConfig, targetPlatform, versionInfo);
87    }
88  
89    abstract protected Parser createParser(File sourceFile);
90    
91    protected String getBaseOutputName(final String inputFile) {
92      return FilenameUtils.getBaseName(inputFile);
93    }
94  
95    @Override
96    public String[] getOutputFileNames(final String inputFile, final VersionInfo versionInfo) {
97      //
98      // if a recognized input file
99      //
100     if (bid(inputFile) > 1) {
101       final String baseName = getBaseOutputName(inputFile);
102       return new String[] {
103         baseName + this.outputSuffix
104       };
105     }
106     return new String[0];
107   }
108 
109   /**
110    * Returns dependency info for the specified source file
111    *
112    * @param task
113    *          task for any diagnostic output
114    * @param source
115    *          file to be parsed
116    * @param includePath
117    *          include path to be used to resolve included files
118    *
119    * @param sysIncludePath
120    *          sysinclude path from build file, files resolved using
121    *          sysInclude path will not participate in dependency analysis
122    *
123    * @param envIncludePath
124    *          include path from environment variable, files resolved with
125    *          envIncludePath will not participate in dependency analysis
126    *
127    * @param baseDir
128    *          used to produce relative paths in DependencyInfo
129    * @param includePathIdentifier
130    *          used to distinguish DependencyInfo's from different include
131    *          path settings
132    *
133    */
134   public final DependencyInfo parseIncludes(final CCTask task, final File source, final File[] includePath,
135       final File[] sysIncludePath, final File[] envIncludePath, final File baseDir, final String includePathIdentifier) {
136     //
137     // if any of the include files can not be identified
138     // change the sourceLastModified to Long.MAX_VALUE to
139     // force recompilation of anything that depends on it
140     long sourceLastModified = source.lastModified();
141     final File[] sourcePath = new File[1];
142     sourcePath[0] = new File(source.getParent());
143     final Vector onIncludePath = new Vector();
144     final Vector onSysIncludePath = new Vector();
145     String baseDirPath;
146     try {
147       baseDirPath = baseDir.getCanonicalPath();
148     } catch (final IOException ex) {
149       baseDirPath = baseDir.toString();
150     }
151     final String relativeSource = CUtil.getRelativePath(baseDirPath, source);
152     String[] includes = emptyIncludeArray;
153     if (canParse(source)) {
154       final Parser parser = createParser(source);
155       try {
156         final Reader reader = new BufferedReader(new FileReader(source));
157         parser.parse(reader);
158         includes = parser.getIncludes();
159       } catch (final IOException ex) {
160         task.log("Error parsing " + source.toString() + ":" + ex.toString());
161         includes = new String[0];
162       }
163     }
164     for (final String include : includes) {
165       if (!resolveInclude(include, sourcePath, onIncludePath)) {
166         if (!resolveInclude(include, includePath, onIncludePath)) {
167           if (!resolveInclude(include, sysIncludePath, onSysIncludePath)) {
168             if (!resolveInclude(include, envIncludePath, onSysIncludePath)) {
169               //
170               // this should be enough to require us to reparse
171               // the file with the missing include for dependency
172               // information without forcing a rebuild
173               sourceLastModified += 2 * CUtil.FILETIME_EPSILON;
174             }
175           }
176         }
177       }
178     }
179     for (int i = 0; i < onIncludePath.size(); i++) {
180       final String relativeInclude = CUtil.getRelativePath(baseDirPath, (File) onIncludePath.elementAt(i));
181       onIncludePath.setElementAt(relativeInclude, i);
182     }
183     for (int i = 0; i < onSysIncludePath.size(); i++) {
184       final String relativeInclude = CUtil.getRelativePath(baseDirPath, (File) onSysIncludePath.elementAt(i));
185       onSysIncludePath.setElementAt(relativeInclude, i);
186     }
187     return new DependencyInfo(includePathIdentifier, relativeSource, sourceLastModified, onIncludePath,
188         onSysIncludePath);
189   }
190 
191   protected boolean resolveInclude(final String includeName, final File[] includePath, final Vector onThisPath) {
192     for (final File element : includePath) {
193       final File includeFile = new File(element, includeName);
194       if (includeFile.exists()) {
195         onThisPath.addElement(includeFile);
196         return true;
197       }
198     }
199     return false;
200   }
201 
202   public final String getOutputSuffix() {
203     return this.outputSuffix;
204   }
205 
206   public void setWorkDir(final File workDir) {
207     this.workDir = workDir;
208   }
209 
210   public void setObjDir(final File objDir) {
211     this.objDir = objDir;
212   }
213 }