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;
21  
22  import java.io.File;
23  import java.lang.reflect.Method;
24  import java.util.Vector;
25  
26  import org.apache.tools.ant.BuildException;
27  import org.apache.tools.ant.DirectoryScanner;
28  import org.apache.tools.ant.Project;
29  import org.apache.tools.ant.types.DataType;
30  import org.apache.tools.ant.types.Environment;
31  import org.apache.tools.ant.types.Reference;
32  
33  import com.github.maven_nar.cpptasks.compiler.LinkType;
34  import com.github.maven_nar.cpptasks.compiler.Processor;
35  import com.github.maven_nar.cpptasks.compiler.ProcessorConfiguration;
36  import com.github.maven_nar.cpptasks.types.CommandLineArgument;
37  import com.github.maven_nar.cpptasks.types.ConditionalFileSet;
38  
39  /**
40   * An abstract compiler/linker definition.
41   *
42   * @author Curt Arnold
43   */
44  public abstract class ProcessorDef extends DataType {
45    /**
46     * Returns the equivalent Boolean object for the specified value
47     * 
48     * Equivalent to Boolean.valueOf in JDK 1.4
49     * 
50     * @param val
51     *          boolean value
52     * @return Boolean.TRUE or Boolean.FALSE
53     */
54    protected static Boolean booleanValueOf(final boolean val) {
55      if (val) {
56        return Boolean.TRUE;
57      }
58      return Boolean.FALSE;
59    }
60  
61    /**
62     * if true, targets will be built for debugging
63     */
64    private Boolean debug;
65    private Environment env = null;
66    /**
67     * Reference for "extends" processor definition
68     */
69    private Reference extendsRef = null;
70    /**
71     * Name of property that must be present or definition will be ignored. May
72     * be null.
73     */
74    private String ifProp;
75    /**
76     * if true, processor definition inherits values from containing cc
77     * element
78     */
79    private boolean inherit;
80    private Boolean libtool = null;
81    protected boolean newEnvironment = false;
82    /**
83     * Processor.
84     */
85    private Processor processor;
86    /**
87     * Collection of <compilerarg>or <linkerarg>contained by definition
88     */
89    private final Vector processorArgs = new Vector();
90    /**
91     * Collection of <compilerparam>or <linkerparam>contained by definition
92     */
93    private final Vector processorParams = new Vector();
94    /**
95     * if true, all targets will be unconditionally rebuilt
96     */
97    private Boolean rebuild;
98    /**
99     * Collection of <fileset>contained by definition
100    */
101   private final Vector srcSets = new Vector();
102   /**
103    * Name of property that if present will cause definition to be ignored.
104    * May be null.
105    */
106   private String unlessProp;
107 
108   /**
109    * Constructor
110    * 
111    */
112   protected ProcessorDef() throws NullPointerException {
113     this.inherit = true;
114   }
115 
116   /**
117    * Adds a <compilerarg>or <linkerarg>
118    * 
119    * @param arg
120    *          command line argument, must not be null
121    * @throws NullPointerException
122    *           if arg is null
123    * @throws BuildException
124    *           if this definition is a reference
125    */
126   protected void addConfiguredProcessorArg(final CommandLineArgument arg) throws NullPointerException, BuildException {
127     if (arg == null) {
128       throw new NullPointerException("arg");
129     }
130     if (isReference()) {
131       throw noChildrenAllowed();
132     }
133     this.processorArgs.addElement(arg);
134   }
135 
136   /**
137    * Adds a <compilerarg>or <linkerarg>
138    * 
139    * @param param
140    *          command line argument, must not be null
141    * @throws NullPointerException
142    *           if arg is null
143    * @throws BuildException
144    *           if this definition is a reference
145    */
146   protected void addConfiguredProcessorParam(final ProcessorParam param) throws NullPointerException, BuildException {
147     if (param == null) {
148       throw new NullPointerException("param");
149     }
150     if (isReference()) {
151       throw noChildrenAllowed();
152     }
153     this.processorParams.addElement(param);
154   }
155 
156   /**
157    * Add an environment variable to the launched process.
158    */
159   public void addEnv(final Environment.Variable var) {
160     if (this.env == null) {
161       this.env = new Environment();
162       this.newEnvironment = true;
163       if (this.processor != null) {
164         // Change the environment in the processor
165         setProcessor(this.processor);
166       }
167     }
168     this.env.addVariable(var);
169   }
170 
171   /**
172    * Adds a source file set.
173    * 
174    * Files in these set will be processed by this configuration and will not
175    * participate in the auction.
176    * 
177    * @param srcSet
178    *          Fileset identifying files that should be processed by this
179    *          processor
180    * @throws BuildException
181    *           if processor definition is a reference
182    */
183   public void addFileset(final ConditionalFileSet srcSet) throws BuildException {
184     if (isReference()) {
185       throw noChildrenAllowed();
186     }
187     srcSet.setProject(getProject());
188     this.srcSets.addElement(srcSet);
189   }
190 
191   /**
192    * Creates a configuration
193    * 
194    * @param baseDef
195    *          reference to def from containing cc element, may be null
196    * @return configuration
197    * 
198    */
199   public ProcessorConfiguration createConfiguration(final CCTask task, final LinkType linkType,
200       final ProcessorDef baseDef, final TargetDef targetPlatform, final VersionInfo versionInfo) {
201     if (isReference()) {
202       return ((ProcessorDef) getCheckedRef(ProcessorDef.class, "ProcessorDef")).createConfiguration(task, linkType,
203           baseDef, targetPlatform, versionInfo);
204     }
205     final ProcessorDef[] defaultProviders = getDefaultProviders(baseDef);
206     final Processor proc = getProcessor(linkType);
207     return proc.createConfiguration(task, linkType, defaultProviders, this, targetPlatform, versionInfo);
208   }
209 
210   /**
211    * Prepares list of processor arguments ( compilerarg, linkerarg ) that
212    * are active for the current project settings.
213    * 
214    * @return active compiler arguments
215    */
216   public CommandLineArgument[] getActiveProcessorArgs() {
217     final Project p = getProject();
218     if (p == null) {
219       throw new java.lang.IllegalStateException("project must be set");
220     }
221     if (isReference()) {
222       return ((ProcessorDef) getCheckedRef(ProcessorDef.class, "ProcessorDef")).getActiveProcessorArgs();
223     }
224     final Vector activeArgs = new Vector(this.processorArgs.size());
225     for (int i = 0; i < this.processorArgs.size(); i++) {
226       final CommandLineArgument arg = (CommandLineArgument) this.processorArgs.elementAt(i);
227       if (arg.isActive(p)) {
228         activeArgs.addElement(arg);
229       }
230     }
231     final CommandLineArgument[] array = new CommandLineArgument[activeArgs.size()];
232     activeArgs.copyInto(array);
233     return array;
234   }
235 
236   /**
237    * Prepares list of processor arguments ( compilerarg, linkerarg) that
238    * are active for the current project settings.
239    * 
240    * @return active compiler arguments
241    */
242   public ProcessorParam[] getActiveProcessorParams() {
243     final Project p = getProject();
244     if (p == null) {
245       throw new java.lang.IllegalStateException("project must be set");
246     }
247     if (isReference()) {
248       return ((ProcessorDef) getCheckedRef(ProcessorDef.class, "ProcessorDef")).getActiveProcessorParams();
249     }
250     final Vector activeParams = new Vector(this.processorParams.size());
251     for (int i = 0; i < this.processorParams.size(); i++) {
252       final ProcessorParam param = (ProcessorParam) this.processorParams.elementAt(i);
253       if (param.isActive(p)) {
254         activeParams.addElement(param);
255       }
256     }
257     final ProcessorParam[] array = new ProcessorParam[activeParams.size()];
258     activeParams.copyInto(array);
259     return array;
260   }
261 
262   /**
263    * Gets boolean indicating debug build
264    * 
265    * @param defaultProviders
266    *          array of ProcessorDef's in descending priority
267    * @param index
268    *          index to first element in array that should be considered
269    * @return if true, built targets for debugging
270    */
271   public boolean getDebug(final ProcessorDef[] defaultProviders, final int index) {
272     if (isReference()) {
273       return ((ProcessorDef) getCheckedRef(ProcessorDef.class, "ProcessorDef")).getDebug(defaultProviders, index);
274     }
275     if (this.debug != null) {
276       return this.debug.booleanValue();
277     } else {
278       if (defaultProviders != null && index < defaultProviders.length) {
279         return defaultProviders[index].getDebug(defaultProviders, index + 1);
280       }
281     }
282     return false;
283   }
284 
285   /**
286    * Creates an chain of objects which provide default values in descending
287    * order of significance.
288    * 
289    * @param baseDef
290    *          corresponding ProcessorDef from CCTask, will be last element
291    *          in array unless inherit = false
292    * @return default provider array
293    * 
294    */
295   protected final ProcessorDef[] getDefaultProviders(final ProcessorDef baseDef) {
296     ProcessorDef extendsDef = getExtends();
297     final Vector chain = new Vector();
298     while (extendsDef != null && !chain.contains(extendsDef)) {
299       chain.addElement(extendsDef);
300       extendsDef = extendsDef.getExtends();
301     }
302     if (baseDef != null && getInherit()) {
303       chain.addElement(baseDef);
304     }
305     final ProcessorDef[] defaultProviders = new ProcessorDef[chain.size()];
306     chain.copyInto(defaultProviders);
307     return defaultProviders;
308   }
309 
310   /**
311    * Because linkers have multiples and none of them share the environment
312    * settings here is a hack to give direct access to copy it just before
313    * running
314    * 
315    * @return
316    */
317   public Environment getEnv() {
318     return this.env;
319   }
320 
321   /**
322    * Gets the ProcessorDef specified by the extends attribute
323    * 
324    * @return Base ProcessorDef, null if extends is not specified
325    * @throws BuildException
326    *           if reference is not same type object
327    */
328   public ProcessorDef getExtends() throws BuildException {
329     if (this.extendsRef != null) {
330       final Object obj = this.extendsRef.getReferencedObject(getProject());
331       if (!getClass().isInstance(obj)) {
332         throw new BuildException("Referenced object " + this.extendsRef.getRefId() + " not correct type, is "
333             + obj.getClass().getName() + " should be " + getClass().getName());
334       }
335       return (ProcessorDef) obj;
336     }
337     return null;
338   }
339 
340   /**
341    * Gets the inherit attribute. If the inherit value is true, this processor
342    * definition will inherit default values from the containing cc element.
343    * 
344    * @return if true then properties from the containing <cc>element are
345    *         used.
346    */
347   public final boolean getInherit() {
348     return this.inherit;
349   }
350 
351   public boolean getLibtool() {
352     if (this.libtool != null) {
353       return this.libtool.booleanValue();
354     }
355     if (isReference()) {
356       return ((ProcessorDef) getCheckedRef(ProcessorDef.class, "ProcessorDef")).getLibtool();
357     }
358     final ProcessorDef extendsDef = getExtends();
359     if (extendsDef != null) {
360       return extendsDef.getLibtool();
361     }
362     return false;
363   }
364 
365   /**
366    * Obtains the appropriate processor (compiler, linker)
367    * 
368    * @return processor
369    */
370   protected Processor getProcessor() {
371     if (isReference()) {
372       return ((ProcessorDef) getCheckedRef(ProcessorDef.class, "ProcessorDef")).getProcessor();
373     }
374     //
375     // if a processor has not been explicitly set
376     // then may be set by an extended definition
377     if (this.processor == null) {
378       final ProcessorDef extendsDef = getExtends();
379       if (extendsDef != null) {
380         return extendsDef.getProcessor();
381       }
382     }
383     return this.processor;
384   }
385 
386   /**
387    * Obtains the appropriate processor (compiler, linker) based on the
388    * LinkType.
389    *
390    * @return processor
391    */
392   protected Processor getProcessor(final LinkType linkType) {
393     // by default ignore the linkType.
394     return getProcessor();
395   }
396 
397   /**
398    * Gets a boolean value indicating whether all targets must be rebuilt
399    * regardless of dependency analysis.
400    * 
401    * @param defaultProviders
402    *          array of ProcessorDef's in descending priority
403    * @param index
404    *          index to first element in array that should be considered
405    * @return true if all targets should be rebuilt.
406    */
407   public boolean getRebuild(final ProcessorDef[] defaultProviders, final int index) {
408     if (isReference()) {
409       return ((ProcessorDef) getCheckedRef(ProcessorDef.class, "ProcessorDef")).getRebuild(defaultProviders, index);
410     }
411     if (this.rebuild != null) {
412       return this.rebuild.booleanValue();
413     } else {
414       if (defaultProviders != null && index < defaultProviders.length) {
415         return defaultProviders[index].getRebuild(defaultProviders, index + 1);
416       }
417     }
418     return false;
419   }
420 
421   /**
422    * Returns true if the processor definition contains embedded file set
423    * definitions
424    * 
425    * @return true if processor definition contains embedded filesets
426    */
427   public boolean hasFileSets() {
428     if (isReference()) {
429       return ((ProcessorDef) getCheckedRef(ProcessorDef.class, "ProcessorDef")).hasFileSets();
430     }
431     return this.srcSets.size() > 0;
432   }
433 
434   /**
435    * Determine if this def should be used.
436    * 
437    * Definition will be active if the "if" variable (if specified) is set and
438    * the "unless" variable (if specified) is not set and that all reference
439    * or extended definitions are active
440    * 
441    * @return true if processor is active
442    * @throws IllegalStateException
443    *           if not properly initialized
444    * @throws BuildException
445    *           if "if" or "unless" variable contains suspicious values
446    *           "false" or "no" which indicates possible confusion
447    */
448   public boolean isActive() throws BuildException, IllegalStateException {
449     final Project project = getProject();
450     if (!CUtil.isActive(project, this.ifProp, this.unlessProp)) {
451       return false;
452     }
453     if (isReference()) {
454       if (!((ProcessorDef) getCheckedRef(ProcessorDef.class, "ProcessorDef")).isActive()) {
455         return false;
456       }
457     }
458     //
459     // walk through any extended definitions
460     //
461     final ProcessorDef[] defaultProviders = getDefaultProviders(null);
462     for (final ProcessorDef defaultProvider : defaultProviders) {
463       if (!defaultProvider.isActive()) {
464         return false;
465       }
466     }
467     return true;
468   }
469 
470   /**
471    * Sets the class name for the adapter. Use the "name" attribute when the
472    * tool is supported.
473    * 
474    * @param className
475    *          full class name
476    * 
477    */
478   public void setClassname(final String className) throws BuildException {
479     Object proc = null;
480     try {
481       final Class implClass = ProcessorDef.class.getClassLoader().loadClass(className);
482       try {
483         final Method getInstance = implClass.getMethod("getInstance");
484         proc = getInstance.invoke(null);
485       } catch (final Exception ex) {
486         proc = implClass.newInstance();
487       }
488     } catch (final Exception ex) {
489       throw new BuildException(ex);
490     }
491     setProcessor((Processor) proc);
492   }
493 
494   /**
495    * If set true, all targets will be built for debugging.
496    * 
497    * @param debug
498    *          true if targets should be built for debugging
499    * @throws BuildException
500    *           if processor definition is a reference
501    */
502   public void setDebug(final boolean debug) throws BuildException {
503     if (isReference()) {
504       throw tooManyAttributes();
505     }
506     this.debug = booleanValueOf(debug);
507   }
508 
509   /**
510    * Sets a description of the current data type.
511    */
512   @Override
513   public void setDescription(final String desc) {
514     super.setDescription(desc);
515   }
516 
517   /**
518    * Specifies that this element extends the element with id attribute with a
519    * matching value. The configuration will be constructed from the settings
520    * of this element, element referenced by extends, and the containing cc
521    * element.
522    * 
523    * @param extendsRef
524    *          Reference to the extended processor definition.
525    * @throws BuildException
526    *           if this processor definition is a reference
527    */
528   public void setExtends(final Reference extendsRef) throws BuildException {
529     if (isReference()) {
530       throw tooManyAttributes();
531     }
532     this.extendsRef = extendsRef;
533   }
534 
535   /**
536    * Sets an id that can be used to reference this element.
537    * 
538    * @param id
539    *          id
540    */
541   public void setId(final String id) {
542     //
543     // this is actually accomplished by a different
544     // mechanism, but we can document it
545     //
546   }
547 
548   /**
549    * Sets the property name for the 'if' condition.
550    * 
551    * The configuration will be ignored unless the property is defined.
552    * 
553    * The value of the property is insignificant, but values that would imply
554    * misinterpretation ("false", "no") will throw an exception when
555    * evaluated.
556    * 
557    * @param propName
558    *          name of property
559    */
560   public void setIf(final String propName) {
561     this.ifProp = propName;
562   }
563 
564   /**
565    * If inherit has the default value of true, defines, includes and other
566    * settings from the containing cc element will be inherited.
567    * 
568    * @param inherit
569    *          new value
570    * @throws BuildException
571    *           if processor definition is a reference
572    */
573   public void setInherit(final boolean inherit) throws BuildException {
574     if (isReference()) {
575       throw super.tooManyAttributes();
576     }
577     this.inherit = inherit;
578   }
579 
580   /**
581    * Set use of libtool.
582    * 
583    * If set to true, the "libtool " will be prepended to the command line
584    * 
585    * @param libtool
586    *          If true, use libtool.
587    */
588   public void setLibtool(final boolean libtool) {
589     if (isReference()) {
590       throw tooManyAttributes();
591     }
592     this.libtool = booleanValueOf(libtool);
593   }
594 
595   /**
596    * Do not propagate old environment when new environment variables are
597    * specified.
598    */
599   public void setNewenvironment(final boolean newenv) {
600     this.newEnvironment = newenv;
601   }
602   public boolean isNewEnvironment() {
603     return this.newEnvironment;
604   }
605 
606   /**
607    * Sets the processor
608    * 
609    * @param processor
610    *          processor, may not be null.
611    * @throws BuildException
612    *           if ProcessorDef is a reference
613    * @throws NullPointerException
614    *           if processor is null
615    */
616   protected void setProcessor(final Processor processor) throws BuildException, NullPointerException {
617     if (processor == null) {
618       throw new NullPointerException("processor");
619     }
620     if (isReference()) {
621       throw super.tooManyAttributes();
622     }
623     if (this.env == null && !this.newEnvironment) {
624       this.processor = processor;
625     } else {
626       this.processor = processor.changeEnvironment(this.newEnvironment, this.env);
627     }
628   }
629 
630   /**
631    * If set true, all targets will be unconditionally rebuilt.
632    * 
633    * @param rebuild
634    *          if true, rebuild all targets.
635    * @throws BuildException
636    *           if processor definition is a reference
637    */
638   public void setRebuild(final boolean rebuild) throws BuildException {
639     if (isReference()) {
640       throw tooManyAttributes();
641     }
642     this.rebuild = booleanValueOf(rebuild);
643   }
644 
645   /**
646    * Specifies that this element should behave as if the content of the
647    * element with the matching id attribute was inserted at this location. If
648    * specified, no other attributes or child content should be specified,
649    * other than "if", "unless" and "description".
650    * 
651    * @param ref
652    *          Reference to other element
653    * 
654    */
655   @Override
656   public void setRefid(final org.apache.tools.ant.types.Reference ref) {
657     super.setRefid(ref);
658   }
659 
660   /**
661    * Set the property name for the 'unless' condition.
662    * 
663    * If named property is set, the configuration will be ignored.
664    * 
665    * The value of the property is insignificant, but values that would imply
666    * misinterpretation ("false", "no") of the behavior will throw an
667    * exception when evaluated.
668    * 
669    * @param propName
670    *          name of property
671    */
672   public void setUnless(final String propName) {
673     this.unlessProp = propName;
674   }
675 
676   /**
677    * This method calls the FileVistor's visit function for every file in the
678    * processors definition
679    * 
680    * @param visitor
681    *          object whose visit method is called for every file
682    */
683   public void visitFiles(final FileVisitor visitor) {
684     final Project p = getProject();
685     if (p == null) {
686       throw new java.lang.IllegalStateException("project must be set before this call");
687     }
688     if (isReference()) {
689       ((ProcessorDef) getCheckedRef(ProcessorDef.class, "ProcessorDef")).visitFiles(visitor);
690     }
691     //
692     // if this processor extends another,
693     // visit its files first
694     //
695     final ProcessorDef extendsDef = getExtends();
696     if (extendsDef != null) {
697       extendsDef.visitFiles(visitor);
698     }
699 
700     for (int i = 0; i < this.srcSets.size(); i++) {
701       final ConditionalFileSet srcSet = (ConditionalFileSet) this.srcSets.elementAt(i);
702       if (srcSet.isActive()) {
703         // Find matching source files
704         final DirectoryScanner scanner = srcSet.getDirectoryScanner(p);
705         // Check each source file - see if it needs compilation
706         final String[] fileNames = scanner.getIncludedFiles();
707         final File parentDir = scanner.getBasedir();
708         for (final String currentFile : fileNames) {
709           visitor.visit(parentDir, currentFile);
710         }
711       }
712     }
713   }
714 }