KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > neu > ccs > jmk > Rule


1 // $Id: Rule.java,v 1.7 2002/02/11 12:09:24 ramsdell Exp $
2

3 // Rules defined by a makefile.
4

5 /*
6  * Copyright 1997 by John D. Ramsdell
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  */

22
23 package edu.neu.ccs.jmk;
24
25 import java.io.File JavaDoc;
26 import java.io.PrintWriter JavaDoc;
27 import java.util.Vector JavaDoc;
28
29 /**
30  * This class implements rules.
31  * @version November 1997
32  * @author John D. Ramsdell
33  */

34 final class Rule
35 {
36   private boolean verbose = false;
37
38   // This method is used to recursively make prerequisites of a rule.
39
private static boolean makeGoals(Rule[] rules, PrintWriter JavaDoc out)
40        throws CommandFailedException
41   {
42     boolean made = false;
43     for (int i = 0; i < rules.length; i++) {
44       Rule rule = rules[i];
45       if (rule.isUpdating())
46     out.println("Circular dependency " + rule + " dropped");
47       else if (rule.make())
48     made = true;
49     }
50     return made;
51   }
52
53   // This method is used to recursively reset prerequisites of a rule.
54
private static void resetGoals(Rule[] rules) {
55     for (int i = 0; i < rules.length; i++)
56       rules[i].reset();
57   }
58
59   private Make make; // Used the test the verbose flag
60
private String JavaDoc target; // and try patterns
61
private File JavaDoc file;
62   private Rule[] prerequisites = new Rule[0];
63   private Command[] commands = new Command[0];
64   private int lineNumber = -1;
65   // match is non-null if a pattern was use to define the rule
66
private String JavaDoc match = null;
67
68   /**
69    * Create a rule.
70    * @param make the make object associated with the rule
71    * @param target the target of the rule
72    */

73   Rule(Make make, String JavaDoc target) {
74     this.make = make;
75     this.target = target;
76     file = new File JavaDoc(StringUtils.localizePaths(target));
77   }
78
79   /**
80    * Merges prerequisites and commands into a rule.
81    * Prerequisites are added to the end of the current list of prerequisites.
82    * The commands are added if the rule has no other commands, otherwise
83    * they are ignored.
84    * @param addedPrerequisites prereqs to be added
85    * @param commands new commands for consideration
86    * @param lineNumber the position of the information in the makefile
87    */

88   void merge(Rule[] addedPrerequisites,
89          Command[] commands,
90          int lineNumber)
91   {
92     if (prerequisites.length == 0)
93       prerequisites = addedPrerequisites;
94     else if (addedPrerequisites.length > 0) {
95       Rule[] old = prerequisites;
96       int len = old.length;
97       prerequisites = new Rule[len + addedPrerequisites.length];
98       for (int i = 0; i < len; i++)
99     prerequisites[i] = old[i];
100       for (int i = 0; i < addedPrerequisites.length; i++)
101     prerequisites[len + i] = addedPrerequisites[i];
102     }
103     if (this.commands.length == 0)
104       this.commands = commands;
105     if (this.lineNumber == -1)
106       this.lineNumber = lineNumber;
107   }
108
109   /**
110    * Get the target of the rule.
111    */

112   String JavaDoc getTarget() {
113     return target;
114   }
115
116   /**
117    * Get the target after localization of separators.
118    * @see edu.neu.ccs.jmk.StringUtils#localizePaths(String)
119    */

120   String JavaDoc getLocalizedTarget() {
121     return file.getPath();
122   }
123
124   /**
125    * Set the match in the case in which a pattern defines this rule.
126    */

127   private void setMatch(String JavaDoc match) {
128     this.match = StringUtils.localizePaths(match);
129   }
130
131   /**
132    * This method checks the patterns to see if one
133    * is applicable to this rule. A rule that has commands
134    * will not check the list of patterns for matches.
135    */

136   private void checkPatterns() {
137     if (commands.length > 0)
138       return;
139     make.tryPatterns(this);
140   }
141
142   /**
143    * Called by make.tryPatterns when a matching pattern is found.
144    */

145   boolean tryPattern(String JavaDoc match, String JavaDoc[] dependencies,
146              Command[] commands, int lineNumber) {
147     setMatch(match);
148     // Add new prerequisites to the FRONT of the list.
149
Rule[] addedPrerequisites = prerequisites;
150     prerequisites = new Rule[dependencies.length];
151     for (int i = 0; i < dependencies.length; i++) // Replace names
152
prerequisites[i] = make.get(dependencies[i]); // by rules
153
merge(addedPrerequisites, commands, lineNumber);
154     return true;
155   }
156
157   // Status flags
158

159   private boolean made = false;
160
161   private boolean isMade() { // Is target made?
162
return made;
163   }
164
165   private boolean used = false; // Has this rule been used? See reset()
166

167   private boolean updating = false;
168
169   boolean isUpdating() { // Is target updating?
170
return updating;
171   }
172
173   private boolean phony = false; // Is target phony? See exists()
174

175   void setPhony(boolean phony) {
176     this.phony = phony;
177   }
178
179   private boolean patternsChecked = false; // Have patterns been checked yet?
180

181   boolean make()
182        throws CommandFailedException
183   {
184     if (made) // If made, nothing to do
185
return made;
186     if (!patternsChecked) { // Just check patterns once
187
checkPatterns();
188       patternsChecked = true;
189     }
190     verbose = make.isVerbose();
191     PrintWriter JavaDoc out = make.getOut();
192     if (verbose) {
193       if (prerequisites.length > 0 || commands.length > 0)
194     out.println(" Making " + getTarget());
195     }
196     used = true;
197     try { // Updating is true while
198
updating = true; // this context is active
199
Rule[] remakeRules;
200       made = makeGoals(prerequisites, out);
201       boolean targetExists = exists();
202       if (targetExists) {
203     // The prerequisites excluded from this list are up to date
204
remakeRules = checkDates();
205     if (verbose) {
206       out.print(" Date ");
207       printRule(out, this);
208       out.println();
209     }
210     if (remakeRules.length == 0) {
211       if (verbose)
212         out.println(" " + getTarget() + " is up to date");
213       return made;
214     }
215       }
216       else {
217     if (verbose) {
218       out.println(" Missing " + getTarget());
219     }
220     remakeRules = prerequisites; // All prerequisites are out of date
221
if (remakeRules.length == 0 && commands.length == 0) {
222       String JavaDoc msg = "No way to make " + getTarget();
223       throw new CommandFailedException(msg);
224     }
225       }
226       made = runCommands(remakeRules, out) || made;
227       // reset cache so created files get the correct last modified time
228
cachedLastModified = 0L; lastModified();
229       if (targetExists && !made) {
230     // Target exists but no update occurred
231
String JavaDoc msg = "Make of " + getTarget() + " failed";
232     throw new CommandFailedException(msg);
233       }
234       if (verbose) {
235     if (made) {
236       out.print(" Made ");
237       printRule(out, this);
238       out.println(" because of");
239       for (int i = 0; i < remakeRules.length; i++) {
240         out.print(" ");
241         printRule(out, remakeRules[i]);
242         out.println();
243       }
244     }
245     else if (prerequisites.length > 0 || commands.length > 0)
246       out.println(" Nothing to do for " + getTarget());
247       }
248       return made;
249     }
250     finally {
251       updating = false;
252     }
253   }
254
255   private boolean exists() {
256     if (phony) // Phony targets are not associated
257
return false; // with a file
258
else
259       return file.exists();
260   }
261
262   private long cachedLastModified = 0L;
263
264   // This routine is only called when the target exists.
265
// file.lastModified() returns 0L only when the file does not exist.
266
private long lastModified() {
267     if (cachedLastModified == 0L)
268       cachedLastModified = file.lastModified();
269     return cachedLastModified;
270   }
271
272   // Returns the list of prerequisites that are out of date with respect
273
// to the target.
274
private Rule[] checkDates() {
275     Vector JavaDoc v = new Vector JavaDoc();
276     // reset cache so target file has the correct last modified time
277
cachedLastModified = 0L;
278     long whenModified = lastModified(); // Not zero by assumption
279
for (int i = 0; i < prerequisites.length; i++) {
280       long dependModified = prerequisites[i].lastModified();
281       if (dependModified == 0L || whenModified < dependModified)
282     v.addElement(prerequisites[i]);
283     }
284     Rule[] rules = new Rule[v.size()];
285     v.copyInto(rules);
286     return rules;
287   }
288
289   /**
290    * Runs the commands.
291    * @return true if at least one command ran
292    */

293   private boolean runCommands(Rule[] newer, PrintWriter JavaDoc out)
294        throws CommandFailedException
295   {
296     Rule first = null;
297     if (prerequisites.length > 0)
298       first = prerequisites[0];
299     for (int i = 0; i < commands.length; i++) {
300       if (make.isInterruptEnabled()) // Poll interrupt status
301
throw new CommandFailedException("make interrupted");
302       commands[i].run(this, first, newer, match,
303               make.isJustPrinting(), out);
304     }
305     return commands.length > 0;
306   }
307
308   /**
309    * For debugging
310    */

311   private void printRule(PrintWriter JavaDoc out, Rule rule) {
312     out.print(rule.getTarget() + " (" + rule.lastModified() + ")");
313   }
314
315   /**
316    * Reset a rule.
317    * This recursively calls reset on its prerequisites if it is used.
318    */

319   void reset() {
320     cachedLastModified = 0L;
321     if (used) {
322       used = false;
323       made = false;
324       updating = false;
325       resetGoals(prerequisites);
326     }
327   }
328 }
329
Popular Tags