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.types;
21  
22  import java.io.File;
23  
24  import org.apache.tools.ant.BuildException;
25  import org.apache.tools.ant.DirectoryScanner;
26  import org.apache.tools.ant.Project;
27  import org.apache.tools.ant.types.DataType;
28  import org.apache.tools.ant.types.FileSet;
29  import org.apache.tools.ant.types.PatternSet;
30  
31  import com.github.maven_nar.cpptasks.CUtil;
32  import com.github.maven_nar.cpptasks.FileVisitor;
33  import com.github.maven_nar.cpptasks.compiler.Linker;
34  
35  /**
36   * A set of library names. Libraries can also be added to a link by specifying
37   * them in a fileset.
38   *
39   * For most Unix-like compilers, libset will result in a series of -l and -L
40   * linker arguments. For Windows compilers, the library names will be used to
41   * locate the appropriate library files which will be added to the linkers
42   * input file list as if they had been specified in a fileset.
43   *
44   * @author Mark A Russell <a
45   *         href="mailto:mark_russell@csgsystems.com">mark_russell@csg_systems.
46   *         com
47   *         </a>
48   * @author Adam Murdoch
49   * @author Curt Arnold
50   */
51  public class LibrarySet extends DataType {
52    private String dataset;
53    private boolean explicitCaseSensitive;
54    private String ifCond;
55    private String[] libnames;
56    private final FileSet set = new FileSet();
57    private String unlessCond;
58    private LibraryTypeEnum libraryType;
59  
60    public LibrarySet() {
61      this.libnames = new String[0];
62    }
63  
64    public void execute() throws org.apache.tools.ant.BuildException {
65      throw new org.apache.tools.ant.BuildException("Not an actual task, but looks like one for documentation purposes");
66    }
67  
68    /**
69     * Gets the dataset. Used on OS390 if the libs are in a dataset.
70     * 
71     * @return Returns a String
72     */
73    public String getDataset() {
74      if (isReference()) {
75        final LibrarySet master = (LibrarySet) getCheckedRef(LibrarySet.class, "LibrarySet");
76        return master.getDataset();
77      }
78      return this.dataset;
79    }
80  
81    public File getDir(final Project project) {
82      if (isReference()) {
83        final LibrarySet master = (LibrarySet) getCheckedRef(LibrarySet.class, "LibrarySet");
84        return master.getDir(project);
85      }
86      return this.set.getDir(project);
87    }
88  
89    protected FileSet getFileSet() {
90      if (isReference()) {
91        final LibrarySet master = (LibrarySet) getCheckedRef(LibrarySet.class, "LibrarySet");
92        return master.getFileSet();
93      }
94      return this.set;
95    }
96  
97    public String[] getLibs() {
98      if (isReference()) {
99        final LibrarySet master = (LibrarySet) getCheckedRef(LibrarySet.class, "LibrarySet");
100       return master.getLibs();
101     }
102     final String[] retval = this.libnames.clone();
103     return retval;
104   }
105 
106   /**
107    * Gets preferred library type
108    * 
109    * @return library type, may be null.
110    */
111   public LibraryTypeEnum getType() {
112     if (isReference()) {
113       final LibrarySet master = (LibrarySet) getCheckedRef(LibrarySet.class, "LibrarySet");
114       return master.getType();
115     }
116     return this.libraryType;
117   }
118 
119   /**
120    * Returns true if the define's if and unless conditions (if any) are
121    * satisfied.
122    */
123   public boolean isActive(final org.apache.tools.ant.Project p) {
124     if (p == null) {
125       throw new NullPointerException("p");
126     }
127     if (this.ifCond != null) {
128       final String ifValue = p.getProperty(this.ifCond);
129       if (ifValue != null) {
130         if (ifValue.equals("no") || ifValue.equals("false")) {
131           throw new BuildException("property " + this.ifCond + " used as if condition has value " + ifValue
132               + " which suggests a misunderstanding of if attributes");
133         }
134       } else {
135         return false;
136       }
137     }
138     if (this.unlessCond != null) {
139       final String unlessValue = p.getProperty(this.unlessCond);
140       if (unlessValue != null) {
141         if (unlessValue.equals("no") || unlessValue.equals("false")) {
142           throw new BuildException("property " + this.unlessCond + " used as unless condition has value " + unlessValue
143               + " which suggests a misunderstanding of unless attributes");
144         }
145         return false;
146       }
147     }
148     if (isReference()) {
149       final LibrarySet master = (LibrarySet) getCheckedRef(LibrarySet.class, "LibrarySet");
150       return master.isActive(this.project);
151     }
152     if (this.libnames.length == 0) {
153       p.log("libnames not specified or empty.", Project.MSG_WARN);
154       return false;
155     }
156     return true;
157   }
158 
159   /**
160    * Sets case sensitivity of the file system. If not set, will default to
161    * the linker's case sensitivity.
162    * 
163    * @param isCaseSensitive
164    *          "true"|"on"|"yes" if file system is case sensitive,
165    *          "false"|"off"|"no" when not.
166    */
167   public void setCaseSensitive(final boolean isCaseSensitive) {
168     if (isReference()) {
169       throw tooManyAttributes();
170     }
171     this.explicitCaseSensitive = true;
172     this.set.setCaseSensitive(isCaseSensitive);
173   }
174 
175   /**
176    * Sets the dataset. Used on OS390 if the libs are in a dataset.
177    * 
178    * @param dataset
179    *          The dataset to set
180    */
181   public void setDataset(final String dataset) {
182     if (isReference()) {
183       throw tooManyAttributes();
184     }
185     this.dataset = dataset;
186   }
187 
188   /**
189    * Library directory.
190    * 
191    * @param dir
192    *          library directory
193    * 
194    */
195   public void setDir(final File dir) throws BuildException {
196     if (isReference()) {
197       throw tooManyAttributes();
198     }
199     this.set.setDir(dir);
200   }
201 
202   /**
203    * Sets the property name for the 'if' condition.
204    * 
205    * The library set will be ignored unless the property is defined.
206    * 
207    * The value of the property is insignificant, but values that would imply
208    * misinterpretation ("false", "no") will throw an exception when
209    * evaluated.
210    * 
211    * @param propName
212    *          property name
213    */
214   public void setIf(final String propName) {
215     this.ifCond = propName;
216   }
217 
218   /**
219    * Comma-separated list of library names without leading prefixes, such as
220    * "lib", or extensions, such as ".so" or ".a".
221    * 
222    */
223   public void setLibs(final CUtil.StringArrayBuilder libs) throws BuildException {
224     if (isReference()) {
225       throw tooManyAttributes();
226     }
227     this.libnames = libs.getValue();
228     //
229     // earlier implementations would warn of suspicious library names
230     // (like libpthread for pthread or kernel.lib for kernel).
231     // visitLibraries now provides better feedback and ld type linkers
232     // should provide adequate feedback so the check here is not necessary.
233   }
234 
235   @Override
236   public void setProject(final Project project) {
237     this.set.setProject(project);
238     super.setProject(project);
239   }
240 
241   /**
242    * Sets the preferred library type. Supported values "shared", "static", and
243    * "framework". "framework" is equivalent to "shared" on non-Darwin platforms.
244    */
245   public void setType(final LibraryTypeEnum type) {
246     if (isReference()) {
247       throw tooManyAttributes();
248     }
249     this.libraryType = type;
250   }
251 
252   /**
253    * Set the property name for the 'unless' condition.
254    * 
255    * If named property is set, the library set will be ignored.
256    * 
257    * The value of the property is insignificant, but values that would imply
258    * misinterpretation ("false", "no") of the behavior will throw an
259    * exception when evaluated.
260    * 
261    * @param propName
262    *          name of property
263    */
264   public void setUnless(final String propName) {
265     this.unlessCond = propName;
266   }
267 
268   public void
269       visitLibraries(final Project project, final Linker linker, final File[] libpath, final FileVisitor visitor)
270           throws BuildException {
271     if (isReference()) {
272       final LibrarySet master = (LibrarySet) getCheckedRef(LibrarySet.class, "LibrarySet");
273       master.visitLibraries(project, linker, libpath, visitor);
274     }
275     //
276     // if there was a libs attribute then
277     // add the corresponding patterns to the FileSet
278     //
279     if (this.libnames != null) {
280       for (final String libname : this.libnames) {
281         final String[] patterns = linker.getLibraryPatterns(new String[] {
282           libname
283         }, this.libraryType);
284         if (patterns.length > 0) {
285           final FileSet localSet = (FileSet) this.set.clone();
286           //
287           // unless explicitly set
288           // will default to the linker case sensitivity
289           //
290           if (!this.explicitCaseSensitive) {
291             final boolean linkerCaseSensitive = linker.isCaseSensitive();
292             localSet.setCaseSensitive(linkerCaseSensitive);
293           }
294           //
295           // add all the patterns for this libname
296           //
297           for (final String pattern : patterns) {
298             final PatternSet.NameEntry entry = localSet.createInclude();
299             entry.setName(pattern);
300           }
301           int matches = 0;
302           //
303           // if there was no specified directory then
304           // run through the libpath backwards
305           //
306           if (localSet.getDir(project) == null) {
307             //
308             // scan libpath in reverse order
309             // to give earlier entries priority
310             //
311             for (int j = libpath.length - 1; j >= 0; j--) {
312               final FileSet clone = (FileSet) localSet.clone();
313               clone.setDir(libpath[j]);
314               final DirectoryScanner scanner = clone.getDirectoryScanner(project);
315               final File basedir = scanner.getBasedir();
316               final String[] files = scanner.getIncludedFiles();
317               matches += files.length;
318               for (final String file : files) {
319                 visitor.visit(basedir, file);
320               }
321             }
322           } else {
323             final DirectoryScanner scanner = localSet.getDirectoryScanner(project);
324             final File basedir = scanner.getBasedir();
325             final String[] files = scanner.getIncludedFiles();
326             matches += files.length;
327             for (final String file : files) {
328               visitor.visit(basedir, file);
329             }
330           }
331           //
332           // TODO: following section works well for Windows
333           // style linkers but unnecessary fails
334           // Unix style linkers. Will need to revisit.
335           //
336           if (matches == 0 ) {
337             final StringBuffer msg = new StringBuffer("No file matching ");
338             if (patterns.length == 1) {
339               msg.append("pattern (");
340               msg.append(patterns[0]);
341               msg.append(")");
342             } else {
343               msg.append("patterns (\"");
344               msg.append(patterns[0]);
345               for (int k = 1; k < patterns.length; k++) {
346                 msg.append(", ");
347                 msg.append(patterns[k]);
348               }
349               msg.append(")");
350             }
351             msg.append(" for library name \"");
352             msg.append(libname);
353             msg.append("\" was found.");
354             // TODO: raising the message in the log rather 
355             //throw new BuildException(msg.toString());
356             project.log(msg.toString(), Project.MSG_WARN);
357           }
358         }
359       }
360     }
361   }
362 }