001: /*
002: * <copyright>
003: *
004: * Copyright 1997-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.tools.build;
028:
029: import java.io.File;
030: import java.io.PrintStream;
031: import java.util.ArrayList;
032: import java.util.Collection;
033: import java.util.Date;
034: import java.util.HashMap;
035: import java.util.Iterator;
036: import java.util.List;
037: import java.util.Map;
038:
039: /**
040: * This class searches for Cougaar source code in subdirectories
041: * of the $COUGAAR_INSTALL_PATH and prints a Bash script to
042: * generate a unified (symbolically-linked) source view.
043: * <p>
044: * The script output of this class is included in the Cougaar
045: * release. This script can also be run by developers to
046: * add new modules, packages, and third-party source
047: * (e.g. the JDK's unzipped "src.zip").
048: * <p>
049: * This class is fairly dumb, so watch out for symbolic link
050: * loops, bad file permissions, and other unexpected errors.
051: * <p>
052: * For example, suppose the current directory contained this
053: * subset of the Cougaar source files (in "cougaar-src.zip"):
054: * <pre>
055: * core/src/org/cougaar/core/agent/Agent.java
056: * core/src/org/cougaar/core/node/Node.java
057: * core/examples/org/cougaar/core/examples/Test.java
058: * util/src/org/cougaar/util/UnaryPredicate.java
059: * util/src/org/cougaar/util/log/Logger.java
060: * util/src/org/cougaar/core/component/Service.java
061: * </pre>
062: * The unified source would look like:
063: * <pre>
064: * src/org/cougaar/core/agent/Agent.java
065: * src/org/cougaar/core/node/Node.java
066: * src/org/cougaar/core/examples/Test.java
067: * src/org/cougaar/util/UnaryPredicate.java
068: * src/org/cougaar/util/log/Logger.java
069: * src/org/cougaar/core/component/Service.java
070: * </pre>
071: * To use the minimal number of symbolic links, we would do:
072: * <pre>
073: * core=$COUGAAR_INSTALL_PATH/core
074: * util=$COUGAAR_INSTALL_PATH/util
075: * mkdir src; cd src
076: * mkdir org; cd org
077: * mkdir cougaar; cd cougaar
078: * mkdir core; cd core
079: * ln -s $core/src/org/cougaar/core/agent
080: * ln -s $util/src/org/cougaar/core/component
081: * ln -s $core/examples/org/cougaar/core/examples
082: * ln -s $core/src/org/cougaar/core/node
083: * cd ..
084: * ln -s $util/src/org/cougaar/util
085: * cd ..
086: * cd ..
087: * cd ..
088: * </pre>
089: * This class does exactly that -- it searches the directories
090: * and generates the above script.
091: * <p>
092: * File conflicts are handled with symbolic links that point to
093: * the first matching file. For example, if we had:
094: * <pre>
095: * core/src/foo/A.java
096: * util/src/foo/A.java
097: * </pre>
098: * then the output would look like:
099: * <pre>
100: * ln -s core/src/foo/A.java A.java#core
101: * ln -s util/src/foo/A.java A.java#util
102: * ln -s A.java#core A.java
103: * </pre>
104: */
105: public class UnifySourceScripter {
106:
107: public static final boolean EXCLUDE_CVS = true;
108:
109: private static final String[] CODE_DIRS = { "src", "examples", };
110:
111: public static void main(String[] args) throws Exception {
112: run();
113: }
114:
115: public static void run() throws Exception {
116: PrintStream out = System.out;
117:
118: out.println("#! /bin/sh");
119:
120: printCopywrite(out);
121:
122: out.println("# Generated " + (new Date()) + "\n# By "
123: + UnifySourceScripter.class.getName());
124:
125: printNotes(out);
126:
127: out.println("BASE=$COUGAAR_INSTALL_PATH\n");
128:
129: out.println("if [ -z $BASE ] || [ ! -d $BASE ]; then"
130: + "\n echo \"Missing directory: $BASE\""
131: + "\n exit 1" + "\nfi" + "\n");
132:
133: DirEntry rootDe = new DirEntry("src");
134:
135: File baseDir = new File(".");
136: File[] files = baseDir.listFiles();
137: for (int i = 0; i < files.length; i++) {
138: File f = files[i];
139: if (f.isDirectory()) {
140: String fname = f.getName();
141: out.println(fname + "=$BASE/" + fname);
142: inRootDir(rootDe, f);
143: }
144: }
145:
146: out.println("\nmake_dir () {" + "\n mkdir $1 || exit 1"
147: + "\n}" + "\n" + "\nlink () {"
148: + "\n ln -s $* || exit 1" + "\n}" + "\n");
149:
150: printDirActions(out, rootDe, "");
151:
152: printReadme(out, "src");
153: }
154:
155: /**
156: * Recurse down a root subdirectory for second-level directories
157: * of interest ("src" and "examples").
158: *
159: * E.g. this finds "core/src", "core/examples", "util/src", etc.
160: */
161: private static void inRootDir(DirEntry rootDe, File dir)
162: throws Exception {
163: String path = dir.getPath() + File.separator;
164: for (int i = 0; i < CODE_DIRS.length; i++) {
165: File subdir = new File(path + CODE_DIRS[i]);
166: if (subdir.isDirectory()) {
167: File[] files = subdir.listFiles();
168: for (int j = 0; j < files.length; j++) {
169: File f = files[j];
170: if (f.isDirectory()) {
171: findIn(rootDe, f);
172: }
173: }
174: }
175: }
176: }
177:
178: /**
179: * Recursively find and split directory entries off of
180: * the parent entry.
181: * <p>
182: * This does all the interesting work...
183: * <p>
184: * @note recursive!
185: */
186: public static void findIn(DirEntry parentDe, File dir)
187: throws Exception {
188: String dirname = dir.getName();
189: if (EXCLUDE_CVS
190: && (dirname.equals("CVS") || dirname.equals(".svn"))) {
191: return;
192: }
193: Object obj = parentDe.getSubDir(dirname);
194: if (obj == null) {
195: // new directory, so we'll link
196: parentDe.addSubDir(dirname, dir);
197: return;
198: }
199:
200: DirEntry dirEntry;
201: if (obj instanceof DirEntry) {
202: // already split this directory
203: dirEntry = (DirEntry) obj;
204: } else if (obj instanceof File) {
205: // we must split this directory
206: File oldDir = (File) obj;
207: if (!(oldDir.isDirectory())) {
208: throw new RuntimeException("unexpected non-dir: "
209: + oldDir);
210: }
211: // replace with new dirEntry
212: dirEntry = new DirEntry(dirname);
213: parentDe.addSubDir(dirname, dirEntry);
214: // fill oldDir into my dirEntry
215: File[] files = oldDir.listFiles();
216: for (int i = 0; i < files.length; i++) {
217: File f = files[i];
218: String fname = f.getName();
219: if (f.isDirectory()) {
220: dirEntry.addSubDir(fname, f);
221: } else {
222: dirEntry.addFile(fname, f);
223: }
224: }
225: } else {
226: throw new RuntimeException("unexpected object ("
227: + (obj == null ? "null" : obj.getClass().getName())
228: + "): " + obj);
229: }
230:
231: // add my subdirectories and files
232: File[] files = dir.listFiles();
233: for (int i = 0; i < files.length; i++) {
234: File f = files[i];
235: if (f.isDirectory()) {
236: // recurse!
237: findIn(dirEntry, f);
238: } else {
239: String fname = f.getName();
240: dirEntry.addFile(fname, f);
241: }
242: }
243: }
244:
245: /** Our class that represents a conflicted directory */
246: private static class DirEntry {
247: private final String name;
248: // Map<String, (File|DirEntry)>
249: private final Map<String, Object> subdirs = new HashMap<String, Object>();
250: // Map<String, List<File>>
251: private Map<String, List<File>> files = new HashMap<String, List<File>>();
252:
253: public DirEntry(String name) {
254: this .name = name;
255: }
256:
257: public String getName() {
258: return name;
259: }
260:
261: public Object getSubDir(String dirname) {
262: return subdirs.get(dirname);
263: }
264:
265: public Iterator iterateSubDirs() {
266: return subdirs.entrySet().iterator();
267: }
268:
269: public void addSubDir(String dirname, Object obj) {
270: // assert (obj instanceof DirEntry ||
271: // (obj instanceof File &&
272: // ((File) obj).isDirectory()));
273: subdirs.put(dirname, obj);
274: }
275:
276: public List<File> getFiles(String fname) {
277: return files.get(fname);
278: }
279:
280: public Collection<String> getFilenames() {
281: return files.keySet();
282: }
283:
284: public void addFile(String fname, File f) {
285: List<File> l = files.get(fname);
286: if (l == null) {
287: l = new ArrayList<File>(2);
288: files.put(fname, l);
289: }
290: l.add(f);
291: }
292: }
293:
294: private static void printCopywrite(PrintStream out) {
295: out
296: .println("\n# <copyright>"
297: + "\n# "
298: + "\n# Copyright 1997-2004 BBNT Solutions, LLC"
299: + "\n# under sponsorship of the Defense Advanced Research Projects"
300: + "\n# Agency (DARPA)."
301: + "\n# "
302: + "\n# You can redistribute this software and/or modify it under the"
303: + "\n# terms of the Cougaar Open Source License as published on the"
304: + "\n# Cougaar Open Source Website (www.cougaar.org)."
305: + "\n# "
306: + "\n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS"
307: + "\n# \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT"
308: + "\n# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR"
309: + "\n# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT"
310: + "\n# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,"
311: + "\n# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT"
312: + "\n# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,"
313: + "\n# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY"
314: + "\n# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT"
315: + "\n# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE"
316: + "\n# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
317: + "\n# " + "\n# </copyright>" + "\n");
318: }
319:
320: private static void printNotes(PrintStream out) {
321: out
322: .println("\n# This script creates a soft-linked unified view of the Cougaar"
323: + "\n# source code. This is very handy for navigating the source,"
324: + "\n# running IDEs, and running debuggers such as OptimizeIt."
325: + "\n#"
326: + "\n# This script uses symbolic links (ln -s). Bash and a compatible"
327: + "\n# operating system are required (e.g. Linux). Windows Cygwin users"
328: + "\n# of Emacs should see the symbolic link utilities at:"
329: + "\n# http://centaur.maths.qmul.ac.uk/Emacs/files/w32-symlinks.el"
330: + "\n# or, for VI and other non-Cygwin applications, see:"
331: + "\n# http://hermitte.free.fr/cygwin/#Win32"
332: + "\n#"
333: + "\n# Run this script in the $COUGAAR_INSTALL_PATH. It will create"
334: + "\n# a \"src/\" directory with links to the source contained in the"
335: + "\n# \"cougaar-src.zip\". Note that this script has little error"
336: + "\n# checking, so use at your own risk..."
337: + "\n#"
338: + "\n# This script was machine-generated by the Java class:"
339: + "\n# "
340: + UnifySourceScripter.class.getName()
341: + "\n# which is included in the Cougaar release \"lib/build.jar\"."
342: + "\n# The class can be used to generate a new script that includes"
343: + "\n# your local development packages and third-party source"
344: + "\n# (e.g. the JDK's unzipped \"$JAVA_HOME/src.zip\")"
345: + "\n");
346: }
347:
348: private static void printReadme(PrintStream out, String dir) {
349: out
350: .println("\nif [ ! -f "
351: + dir
352: + "/README ]; then"
353: + "\ncat > "
354: + dir
355: + "/README << EOF"
356: + "\nThis is a symbolically-linked source view, generated by:"
357: + "\n cd \\$COUGAAR_INSTALL_PATH" + "\n $0"
358: + "\n" + "\nSee the above script for details."
359: + "\nEOF" + "\nfi");
360: }
361:
362: /**
363: * Print the script actions for a completed DirEntry, and recurse
364: * into the sub-dir-entries.
365: * <p>
366: * @note recursive!
367: */
368: private static void printDirActions(PrintStream out, DirEntry de,
369: String prefix) {
370: out.println(prefix + "make_dir " + de.getName() + "; cd "
371: + de.getName());
372: for (Iterator iter = de.iterateSubDirs(); iter.hasNext();) {
373: Map.Entry me = (Map.Entry) iter.next();
374: Object val = me.getValue();
375: if (val instanceof DirEntry) {
376: DirEntry sde = (DirEntry) val;
377: // recurse!
378: printDirActions(out, sde, prefix + " ");
379: } else if (val instanceof File) {
380: File f = (File) val;
381: String fpath = f.getPath();
382: fpath = "$" + fpath.substring(2);
383: out.println(prefix + " link " + fpath);
384: }
385: }
386:
387: for (String fn : de.getFilenames()) {
388: String fname = null;
389: String firstAlias = null;
390: for (File f : de.getFiles(fn)) {
391: if (fname == null) {
392: fname = f.getName();
393: }
394: String fpath = f.getPath();
395: fpath = "$" + fpath.substring(2);
396: int sep = fpath.indexOf(File.separator);
397: String falias = fname + "#"
398: + (sep > 0 ? fpath.substring(1, sep) : fpath);
399: // check if (de.getFile(falias) != null) ?
400: out.println(prefix + " link " + fpath + " " + falias);
401: if (firstAlias == null) {
402: firstAlias = falias;
403: }
404: }
405: out.println(prefix + " link " + firstAlias + " " + fname);
406: }
407: out.println(prefix + "cd ..");
408: }
409: }
|