001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018: package org.apache.ivy.ant;
019:
020: import java.io.File;
021: import java.util.ArrayList;
022: import java.util.Collection;
023: import java.util.Collections;
024: import java.util.HashMap;
025: import java.util.Iterator;
026: import java.util.LinkedHashMap;
027: import java.util.LinkedHashSet;
028: import java.util.List;
029: import java.util.ListIterator;
030: import java.util.Map;
031: import java.util.Set;
032: import java.util.StringTokenizer;
033:
034: import org.apache.ivy.Ivy;
035: import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
036: import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
037: import org.apache.ivy.core.module.id.ModuleId;
038: import org.apache.ivy.core.settings.IvySettings;
039: import org.apache.ivy.core.sort.WarningNonMatchingVersionReporter;
040: import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry;
041: import org.apache.ivy.util.Message;
042: import org.apache.tools.ant.BuildException;
043: import org.apache.tools.ant.DirectoryScanner;
044: import org.apache.tools.ant.types.FileSet;
045: import org.apache.tools.ant.types.Path;
046:
047: /**
048: * Creates an ant filelist of files (usually build.xml) ordered according to the dependencies
049: * declared in ivy files.
050: */
051: public class IvyBuildList extends IvyTask {
052: private List buildFileSets = new ArrayList(); // List (FileSet)
053:
054: private String reference;
055:
056: private boolean haltOnError = true;
057:
058: private boolean skipBuildWithoutIvy = false;
059:
060: private boolean reverse = false;
061:
062: private String ivyFilePath;
063:
064: private String root = "*";
065:
066: private boolean excludeRoot = false;
067:
068: private String leaf = "*";
069:
070: private String delimiter = ",";
071:
072: private boolean excludeLeaf = false;
073:
074: private boolean onlydirectdep = false;
075:
076: private String restartFrom = "*";
077:
078: public void addFileset(FileSet buildFiles) {
079: buildFileSets.add(buildFiles);
080: }
081:
082: public String getReference() {
083: return reference;
084: }
085:
086: public void setReference(String reference) {
087: this .reference = reference;
088: }
089:
090: public String getRoot() {
091: return root;
092: }
093:
094: public void setRoot(String root) {
095: this .root = root;
096: }
097:
098: public boolean isExcludeRoot() {
099: return excludeRoot;
100: }
101:
102: public void setExcludeRoot(boolean root) {
103: excludeRoot = root;
104: }
105:
106: public String getLeaf() {
107: return leaf;
108: }
109:
110: public void setLeaf(String leaf) {
111: this .leaf = leaf;
112: }
113:
114: public boolean isExcludeLeaf() {
115: return excludeLeaf;
116: }
117:
118: public void setExcludeLeaf(boolean excludeLeaf) {
119: this .excludeLeaf = excludeLeaf;
120: }
121:
122: public String getDelimiter() {
123: return delimiter;
124: }
125:
126: public void setDelimiter(String delimiter) {
127: this .delimiter = delimiter;
128: }
129:
130: public boolean getOnlydirectdep() {
131: return onlydirectdep;
132: }
133:
134: public void setOnlydirectdep(boolean onlydirectdep) {
135: this .onlydirectdep = onlydirectdep;
136: }
137:
138: public void doExecute() throws BuildException {
139: if (reference == null) {
140: throw new BuildException(
141: "reference should be provided in ivy build list");
142: }
143: if (buildFileSets.isEmpty()) {
144: throw new BuildException(
145: "at least one nested fileset should be provided in ivy build list");
146: }
147:
148: Ivy ivy = getIvyInstance();
149: IvySettings settings = ivy.getSettings();
150:
151: ivyFilePath = getProperty(ivyFilePath, settings,
152: "ivy.buildlist.ivyfilepath");
153:
154: Path path = new Path(getProject());
155:
156: Map buildFiles = new HashMap(); // Map (ModuleDescriptor -> File buildFile)
157: Map mdsMap = new LinkedHashMap(); // Map (String moduleName -> ModuleDescriptor)
158: List independent = new ArrayList();
159:
160: Set rootModuleNames = new LinkedHashSet();
161: if (!"*".equals(root)) {
162: StringTokenizer st = new StringTokenizer(root, delimiter);
163: while (st.hasMoreTokens()) {
164: rootModuleNames.add(st.nextToken());
165: }
166: }
167:
168: Set leafModuleNames = new LinkedHashSet();
169: if (!"*".equals(leaf)) {
170: StringTokenizer st = new StringTokenizer(leaf, delimiter);
171: while (st.hasMoreTokens()) {
172: leafModuleNames.add(st.nextToken());
173: }
174: }
175:
176: Set restartFromModuleNames = new LinkedHashSet();
177: if (!"*".equals(restartFrom)) {
178: StringTokenizer st = new StringTokenizer(restartFrom,
179: delimiter);
180: // Only accept one (first) module
181: restartFromModuleNames.add(st.nextToken());
182: }
183:
184: for (ListIterator iter = buildFileSets.listIterator(); iter
185: .hasNext();) {
186: FileSet fs = (FileSet) iter.next();
187: DirectoryScanner ds = fs.getDirectoryScanner(getProject());
188: String[] builds = ds.getIncludedFiles();
189: for (int i = 0; i < builds.length; i++) {
190: File buildFile = new File(ds.getBasedir(), builds[i]);
191: File ivyFile = getIvyFileFor(buildFile);
192: if (!ivyFile.exists()) {
193: if (skipBuildWithoutIvy) {
194: Message.debug("skipping " + buildFile
195: + ": ivy file " + ivyFile
196: + " doesn't exist");
197: } else {
198: Message
199: .verbose("no ivy file for "
200: + buildFile
201: + ": ivyfile="
202: + ivyFile
203: + ": adding it at the beginning of the path");
204: Message
205: .verbose("\t(set skipbuildwithoutivy to true if you don't want this"
206: + " file to be added to the path)");
207: independent.add(buildFile);
208: }
209: } else {
210: try {
211: ModuleDescriptor md = ModuleDescriptorParserRegistry
212: .getInstance().parseDescriptor(
213: settings, ivyFile.toURL(),
214: doValidate(settings));
215: buildFiles.put(md, buildFile);
216: mdsMap.put(md.getModuleRevisionId()
217: .getModuleId().getName(), md);
218: Message.debug("Add "
219: + md.getModuleRevisionId()
220: .getModuleId());
221: } catch (Exception ex) {
222: if (haltOnError) {
223: throw new BuildException(
224: "impossible to parse ivy file for "
225: + buildFile + ": ivyfile="
226: + ivyFile + " exception="
227: + ex, ex);
228: } else {
229: Message
230: .warn("impossible to parse ivy file for "
231: + buildFile
232: + ": ivyfile="
233: + ivyFile
234: + " exception="
235: + ex.getMessage());
236: Message
237: .info("\t=> adding it at the beginning of the path");
238: independent.add(buildFile);
239: }
240: }
241: }
242: }
243: }
244:
245: List leafModuleDescriptors = convertModuleNamesToModuleDescriptors(
246: mdsMap, leafModuleNames, "leaf");
247: List rootModuleDescriptors = convertModuleNamesToModuleDescriptors(
248: mdsMap, rootModuleNames, "root");
249: List restartFromModuleDescriptors = convertModuleNamesToModuleDescriptors(
250: mdsMap, restartFromModuleNames, "restartFrom");
251:
252: Collection mds = new ArrayList(mdsMap.values());
253: if (!rootModuleDescriptors.isEmpty()) {
254: Message.info("Filtering modules based on roots "
255: + rootModuleNames);
256: mds = filterModulesFromRoot(mds, rootModuleDescriptors);
257: }
258: if (!leafModuleDescriptors.isEmpty()) {
259: Message.info("Filtering modules based on leafs "
260: + leafModuleNames);
261: mds = filterModulesFromLeaf(mds, leafModuleDescriptors);
262: }
263:
264: WarningNonMatchingVersionReporter nonMatchingVersionReporter = new WarningNonMatchingVersionReporter();
265: List sortedModules = ivy.sortModuleDescriptors(mds,
266: nonMatchingVersionReporter);
267:
268: for (ListIterator iter = independent.listIterator(); iter
269: .hasNext();) {
270: File buildFile = (File) iter.next();
271: addBuildFile(path, buildFile);
272: }
273: if (isReverse()) {
274: Collections.reverse(sortedModules);
275: }
276: // Remove modules that are before the restartFrom point
277: // Independant modules (without valid ivy file) can not be addressed
278: // so they are not removed from build path.
279: if (!restartFromModuleDescriptors.isEmpty()) {
280: boolean foundRestartFrom = false;
281: List keptModules = new ArrayList();
282: ModuleDescriptor restartFromModuleDescriptor = (ModuleDescriptor) restartFromModuleDescriptors
283: .get(0);
284: for (ListIterator iter = sortedModules.listIterator(); iter
285: .hasNext();) {
286: ModuleDescriptor md = (ModuleDescriptor) iter.next();
287: if (md.equals(restartFromModuleDescriptor)) {
288: foundRestartFrom = true;
289: }
290: if (foundRestartFrom) {
291: keptModules.add(md);
292: }
293: }
294: sortedModules = keptModules;
295: }
296: StringBuffer order = new StringBuffer();
297: for (ListIterator iter = sortedModules.listIterator(); iter
298: .hasNext();) {
299: ModuleDescriptor md = (ModuleDescriptor) iter.next();
300: order.append(md.getModuleRevisionId().getModuleId());
301: if (iter.hasNext()) {
302: order.append(", ");
303: }
304: File buildFile = (File) buildFiles.get(md);
305: addBuildFile(path, buildFile);
306: }
307:
308: getProject().addReference(getReference(), path);
309: getProject()
310: .setProperty("ivy.sorted.modules", order.toString());
311: }
312:
313: private List convertModuleNamesToModuleDescriptors(Map mdsMap,
314: Set moduleNames, String kind) {
315: List mds = new ArrayList();
316: for (Iterator iter = moduleNames.iterator(); iter.hasNext();) {
317: String name = (String) iter.next();
318: ModuleDescriptor md = (ModuleDescriptor) mdsMap.get(name);
319: if (md == null) {
320: throw new BuildException("unable to find " + kind
321: + " module " + name + " in build fileset");
322: }
323: mds.add(md);
324: }
325: return mds;
326: }
327:
328: /**
329: * Returns a collection of ModuleDescriptors that are conatined in the input collection of
330: * ModuleDescriptors and upon which the root module depends
331: *
332: * @param mds
333: * input collection of ModuleDescriptors
334: * @param rootmd
335: * root module
336: * @return filtered list of modules
337: */
338: private Collection filterModulesFromRoot(Collection mds,
339: List rootmds) {
340: // Make a map of ModuleId objects -> ModuleDescriptors
341: Map moduleIdMap = new HashMap();
342: for (Iterator iter = mds.iterator(); iter.hasNext();) {
343: ModuleDescriptor md = ((ModuleDescriptor) iter.next());
344: moduleIdMap.put(md.getModuleRevisionId().getModuleId(), md);
345: }
346:
347: // recursively process the nodes
348: Set toKeep = new LinkedHashSet();
349:
350: Iterator it = rootmds.iterator();
351: while (it.hasNext()) {
352: ModuleDescriptor rootmd = (ModuleDescriptor) it.next();
353: processFilterNodeFromRoot(rootmd, toKeep, moduleIdMap);
354: // With the excluderoot attribute set to true, take the rootmd out of the toKeep set.
355: if (excludeRoot) {
356: // Only for logging purposes
357: Message.verbose("Excluded module "
358: + rootmd.getModuleRevisionId().getModuleId()
359: .getName());
360: } else {
361: toKeep.add(rootmd);
362: }
363: }
364:
365: // just for logging
366: for (Iterator iter = toKeep.iterator(); iter.hasNext();) {
367: ModuleDescriptor md = ((ModuleDescriptor) iter.next());
368: Message.verbose("Kept module "
369: + md.getModuleRevisionId().getModuleId().getName());
370: }
371:
372: return toKeep;
373: }
374:
375: /**
376: * Adds the current node to the toKeep collection and then processes the each of the direct
377: * dependencies of this node that appear in the moduleIdMap (indicating that the dependency is
378: * part of this BuildList)
379: *
380: * @param node
381: * the node to be processed
382: * @param toKeep
383: * the set of ModuleDescriptors that should be kept
384: * @param moduleIdMap
385: * reference mapping of moduleId to ModuleDescriptor that are part of the BuildList
386: */
387: private void processFilterNodeFromRoot(ModuleDescriptor node,
388: Set toKeep, Map moduleIdMap) {
389: //toKeep.add(node);
390:
391: DependencyDescriptor[] deps = node.getDependencies();
392: for (int i = 0; i < deps.length; i++) {
393: ModuleId id = deps[i].getDependencyId();
394: if (moduleIdMap.get(id) != null) {
395: toKeep.add(moduleIdMap.get(id));
396: if (!getOnlydirectdep()) {
397: processFilterNodeFromRoot(
398: (ModuleDescriptor) moduleIdMap.get(id),
399: toKeep, moduleIdMap);
400: }
401: }
402: }
403: }
404:
405: /**
406: * Returns a collection of ModuleDescriptors that are conatined in the input collection of
407: * ModuleDescriptors which depends on the leaf module
408: *
409: * @param mds
410: * input collection of ModuleDescriptors
411: * @param leafmd
412: * leaf module
413: * @return filtered list of modules
414: */
415: private Collection filterModulesFromLeaf(Collection mds,
416: List leafmds) {
417: // Make a map of ModuleId objects -> ModuleDescriptors
418: Map moduleIdMap = new HashMap();
419: for (Iterator iter = mds.iterator(); iter.hasNext();) {
420: ModuleDescriptor md = ((ModuleDescriptor) iter.next());
421: moduleIdMap.put(md.getModuleRevisionId().getModuleId(), md);
422: }
423:
424: // recursively process the nodes
425: Set toKeep = new LinkedHashSet();
426: Iterator it = leafmds.iterator();
427: while (it.hasNext()) {
428: ModuleDescriptor leafmd = (ModuleDescriptor) it.next();
429: // With the excludeleaf attribute set to true, take the rootmd out of the toKeep set.
430: if (excludeLeaf) {
431: Message.verbose("Excluded module "
432: + leafmd.getModuleRevisionId().getModuleId()
433: .getName());
434: } else {
435: toKeep.add(leafmd);
436: }
437: processFilterNodeFromLeaf(leafmd, toKeep, moduleIdMap);
438: }
439:
440: // just for logging
441: for (Iterator iter = toKeep.iterator(); iter.hasNext();) {
442: ModuleDescriptor md = ((ModuleDescriptor) iter.next());
443: Message.verbose("Kept module "
444: + md.getModuleRevisionId().getModuleId().getName());
445: }
446:
447: return toKeep;
448: }
449:
450: /**
451: * Search in the moduleIdMap modules depending on node, add them to the toKeep set and process
452: * them recursively.
453: *
454: * @param node
455: * the node to be processed
456: * @param toKeep
457: * the set of ModuleDescriptors that should be kept
458: * @param moduleIdMap
459: * reference mapping of moduleId to ModuleDescriptor that are part of the BuildList
460: */
461: private void processFilterNodeFromLeaf(ModuleDescriptor node,
462: Set toKeep, Map moduleIdMap) {
463: for (Iterator iter = moduleIdMap.values().iterator(); iter
464: .hasNext();) {
465: ModuleDescriptor md = (ModuleDescriptor) iter.next();
466: DependencyDescriptor[] deps = md.getDependencies();
467: for (int i = 0; i < deps.length; i++) {
468: ModuleId id = deps[i].getDependencyId();
469: if (node.getModuleRevisionId().getModuleId().equals(id)
470: && !toKeep.contains(md)) {
471: toKeep.add(md);
472: if (!getOnlydirectdep()) {
473: processFilterNodeFromLeaf(md, toKeep,
474: moduleIdMap);
475: }
476: }
477: }
478: }
479: }
480:
481: private void addBuildFile(Path path, File buildFile) {
482: FileSet fs = new FileSet();
483: fs.setFile(buildFile);
484: path.addFileset(fs);
485: }
486:
487: private File getIvyFileFor(File buildFile) {
488: return new File(buildFile.getParentFile(), ivyFilePath);
489: }
490:
491: public boolean isHaltonerror() {
492: return haltOnError;
493: }
494:
495: public void setHaltonerror(boolean haltOnError) {
496: this .haltOnError = haltOnError;
497: }
498:
499: public String getIvyfilepath() {
500: return ivyFilePath;
501: }
502:
503: public void setIvyfilepath(String ivyFilePath) {
504: this .ivyFilePath = ivyFilePath;
505: }
506:
507: public boolean isSkipbuildwithoutivy() {
508: return skipBuildWithoutIvy;
509: }
510:
511: public void setSkipbuildwithoutivy(boolean skipBuildFilesWithoutIvy) {
512: this .skipBuildWithoutIvy = skipBuildFilesWithoutIvy;
513: }
514:
515: public boolean isReverse() {
516: return reverse;
517: }
518:
519: public void setReverse(boolean reverse) {
520: this .reverse = reverse;
521: }
522:
523: public String getRestartFrom() {
524: return restartFrom;
525: }
526:
527: public void setRestartFrom(String restartFrom) {
528: this.restartFrom = restartFrom;
529: }
530:
531: }
|