Tried to fix EDKT149. Since the issue happened randomly, only long time test can...
[mirror_edk2.git] / Tools / Source / FrameworkTasks / org / tianocore / framework / tasks / MakeDeps.java
1 /** @file
2 This file is to wrap MakeDeps.exe tool as ANT task, which is used to generate
3 dependency files for source code.
4
5 Copyright (c) 2006, Intel Corporation
6 All rights reserved. This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15 package org.tianocore.framework.tasks;
16
17 import java.io.File;
18 import java.io.FileReader;
19 import java.io.FileWriter;
20 import java.io.IOException;
21 import java.io.LineNumberReader;
22 import java.util.ArrayList;
23 import java.util.HashSet;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Set;
27 import java.util.StringTokenizer;
28 import java.util.regex.Matcher;
29 import java.util.regex.Pattern;
30
31 import org.apache.tools.ant.BuildException;
32 import org.apache.tools.ant.Project;
33 import org.apache.tools.ant.Task;
34 import org.apache.tools.ant.taskdefs.Execute;
35 import org.apache.tools.ant.taskdefs.LogStreamHandler;
36 import org.apache.tools.ant.types.Commandline;
37 import org.apache.tools.ant.types.Path;
38 import org.tianocore.logger.EdkLog;
39
40 /**
41 Class MakeDeps is used to wrap MakeDeps.exe as an ANT task.
42 **/
43 public class MakeDeps extends Task {
44
45 //
46 // private members, use set/get to access them
47 //
48 private static final String cmdName = "MakeDeps";
49 private static final String target = "dummy";
50 private String includePath = null;
51 private String depsFile = null;
52 private String subDir = null;
53 private boolean quietMode = true;
54 private boolean ignoreError = true;
55 private String extraDeps = "";
56 private List<IncludePath> includePathList = new ArrayList<IncludePath>();
57 private List<Input> inputFileList = new ArrayList<Input>();
58
59 public MakeDeps() {
60
61 }
62
63 /**
64 The Standard execute method for ANT task. It will check if it's necessary
65 to generate the dependency list file. If no file is found or the dependency
66 is changed, it will compose the command line and call MakeDeps.exe to
67 generate the dependency list file.
68
69 @throws BuildException
70 **/
71 public void execute() throws BuildException {
72 ///
73 /// check if the dependency list file is uptodate or not
74 ///
75 if (isUptodate()) {
76 return;
77 }
78
79 Project prj = this.getOwningTarget().getProject();
80 String toolPath = prj.getProperty("env.FRAMEWORK_TOOLS_PATH");
81 FrameworkLogger logger = new FrameworkLogger(prj, "makedeps");
82 EdkLog.setLogLevel(prj.getProperty("env.LOGLEVEL"));
83 EdkLog.setLogger(logger);
84
85 ///
86 /// compose full tool path
87 ///
88 if (toolPath == null || toolPath.length() == 0) {
89 toolPath = "./" + cmdName;
90 } else {
91 if (toolPath.endsWith("/") || toolPath.endsWith("\\")) {
92 toolPath = toolPath + cmdName;
93 } else {
94 toolPath = toolPath + "/" + cmdName;
95 }
96 }
97
98 ///
99 /// compose tool arguments
100 ///
101 StringBuffer args = new StringBuffer(4096);
102 if (ignoreError) {
103 args.append(" -ignorenotfound");
104 }
105 if (quietMode) {
106 args.append(" -q");
107 }
108 if (subDir != null && subDir.length() > 0) {
109 args.append(" -s ");
110 args.append(subDir);
111 }
112
113 ///
114 /// if there's no source files, we can do nothing about dependency
115 ///
116 if (inputFileList.size() == 0) {
117 throw new BuildException("No source files specified to scan");
118 }
119
120 ///
121 /// compose source file arguments
122 ///
123 Iterator iterator = inputFileList.iterator();
124 while (iterator.hasNext()) {
125 Input inputFile = (Input)iterator.next();
126 String inputFileString = cleanupPathName(inputFile.getFile());
127 args.append(" -f ");
128 args.append(inputFileString);
129 }
130
131 ///
132 /// compose search pathes argument
133 ///
134 StringBuffer includePathArg = new StringBuffer(4096);
135 if (includePath != null && includePath.length() > 0) {
136 StringTokenizer pathTokens = new StringTokenizer(includePath, ";");
137 while (pathTokens.hasMoreTokens()) {
138 String tmpPath = pathTokens.nextToken().trim();
139 if (tmpPath.length() == 0) {
140 continue;
141 }
142
143 includePathArg.append(" -i ");
144 includePathArg.append(cleanupPathName(tmpPath));
145 }
146 }
147 iterator = includePathList.iterator();
148 while (iterator.hasNext()) {
149 IncludePath path = (IncludePath)iterator.next();
150 includePathArg.append(cleanupPathName(path.getPath()));
151 }
152 args.append(includePathArg);
153
154 ///
155 /// We don't need a real target. So just a "dummy" is given
156 ///
157 args.append(" -target dummy");
158 args.append(" -o ");
159 args.append(cleanupPathName(depsFile));
160
161 ///
162 /// prepare to execute the tool
163 ///
164 Commandline cmd = new Commandline();
165 cmd.setExecutable(toolPath);
166 cmd.createArgument().setLine(args.toString());
167
168 LogStreamHandler streamHandler = new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_WARN);
169 Execute runner = new Execute(streamHandler, null);
170
171 runner.setAntRun(prj);
172 runner.setCommandline(cmd.getCommandline());
173
174 EdkLog.log(EdkLog.EDK_VERBOSE, Commandline.toString(cmd.getCommandline()));
175
176 int result = 0;
177 try {
178 result = runner.execute();
179 } catch (IOException e) {
180 throw new BuildException(e.getMessage());
181 }
182
183 if (result != 0) {
184 EdkLog.log(EdkLog.EDK_INFO, "MakeDeps failed!");
185 return;
186 }
187
188 // change the old DEP file format (makefile compatible) to just file list
189 if (!cleanup()) {
190 throw new BuildException(depsFile + " was not generated");
191 }
192 }
193
194 ///
195 /// Remove any duplicated path separator or inconsistent path separator
196 ///
197 private String cleanupPathName(String path) {
198 String separator = "\\" + File.separator;
199 String duplicateSeparator = separator + "{2}";
200 path = Path.translateFile(path);
201 path = path.replaceAll(duplicateSeparator, separator);
202 return path;
203 }
204
205 /**
206 Set method for "DepsFile" attribute
207
208 @param name The name of dependency list file
209 **/
210 public void setDepsFile(String name) {
211 depsFile = cleanupPathName(name);
212 }
213
214 /**
215 Get method for "DepsFile" attribute
216
217 @returns The name of dependency list file
218 **/
219 public String getDepsFile() {
220 return depsFile;
221 }
222
223 /**
224 Set method for "IgnoreError" attribute
225
226 @param ignore flag to control error handling (true/false)
227 **/
228 public void setIgnoreError(boolean ignore) {
229 ignoreError = ignore;
230 }
231
232 /**
233 Get method for "IgnoreError" attribute
234
235 @returns The value of current IgnoreError flag
236 **/
237 public boolean getIgnoreError() {
238 return ignoreError;
239 }
240
241 /**
242 Set method for "QuietMode" attribute
243
244 @param quiet flag to control the output information (true/false)
245 **/
246 public void setQuietMode(boolean quiet) {
247 quietMode = quiet;
248 }
249
250 /**
251 Get method for "QuietMode" attribute
252
253 @returns value of current QuietMode flag
254 **/
255 public boolean getQuietMode() {
256 return quietMode;
257 }
258
259 /**
260 Set method for "SubDir" attribute
261
262 @param dir The name of sub-directory in which source files will be scanned
263 **/
264 public void setSubDir(String dir) {
265 subDir = dir;
266 }
267
268 /**
269 Get method for "SubDir" attribute
270
271 @returns The name of sub-directory
272 **/
273 public String getSubDir() {
274 return subDir;
275 }
276
277 /**
278 Set method for "IncludePath" attribute
279
280 @param path The name of include path
281 **/
282 public void setIncludePath(String path) {
283 includePath = cleanupPathName(path);
284 }
285
286 /**
287 Get method for "IncludePath" attribute
288
289 @returns The name of include path
290 **/
291 public String getIncludePath() {
292 return includePath;
293 }
294
295 /**
296 Set method for "ExtraDeps" attribute
297
298 @param deps The name of dependency file specified separately
299 **/
300 public void setExtraDeps(String deps) {
301 extraDeps = deps;
302 }
303
304 /**
305 Get method for "ExtraDeps" attribute
306
307 @returns The name of dependency file specified separately
308 **/
309 public String getExtraDeps () {
310 return extraDeps;
311 }
312
313 /**
314 Add method for "IncludePath" nested element
315
316 @param path The IncludePath object from nested IncludePath type of element
317 **/
318 public void addIncludepath(IncludePath path) {
319 includePathList.add(path);
320 }
321
322 /**
323 Add method for "Input" nested element
324
325 @param input The Input object from nested Input type of element
326 **/
327 public void addInput(Input inputFile) {
328 inputFileList.add(inputFile);
329 }
330
331 /**
332 The original file generated by MakeDeps.exe is for makefile uses. The target
333 part (before :) is not useful for ANT. This method will do the removal.
334
335 @returns true if cleaned files is saved successfully
336 @returns false if error occurs in file I/O system
337 **/
338 private boolean cleanup() {
339 File df = new File(depsFile);
340
341 if (!df.exists()) {
342 return false;
343 }
344
345 LineNumberReader lineReader = null;
346 FileReader fileReader = null;
347 Set<String> lineSet = new HashSet<String>(100); // used to remove duplicated lines
348 try {
349 fileReader = new FileReader(df);
350 lineReader = new LineNumberReader(fileReader);
351
352 ///
353 /// clean-up each line in deps file
354 //
355 String line = null;
356 while ((line = lineReader.readLine()) != null) {
357 String[] filePath = line.split(" : ");
358 if (filePath.length == 2) {
359 ///
360 /// keep the file name after ":"
361 ///
362 lineSet.add(cleanupPathName(filePath[1]));
363 }
364 }
365 lineReader.close();
366 fileReader.close();
367
368 ///
369 /// we may have explicitly specified dependency files
370 ///
371 StringTokenizer fileTokens = new StringTokenizer(extraDeps, ";");
372 while (fileTokens.hasMoreTokens()) {
373 lineSet.add(cleanupPathName(fileTokens.nextToken()));
374 }
375
376 ///
377 /// compose the final file content
378 ///
379 StringBuffer cleanedLines = new StringBuffer(40960);
380 Iterator<String> it = lineSet.iterator();
381 while (it.hasNext()) {
382 String filePath = it.next();
383 cleanedLines.append(filePath);
384 cleanedLines.append("\n");
385 }
386 ///
387 /// overwrite old dep file with new content
388 ///
389 FileWriter fileWriter = null;
390 fileWriter = new FileWriter(df);
391 fileWriter.write(cleanedLines.toString());
392 fileWriter.close();
393 } catch (IOException e) {
394 log (e.getMessage());
395 }
396
397 return true;
398 }
399
400 /**
401 Check if the dependency list file should be (re-)generated or not.
402
403 @returns true The dependency list file is uptodate. No re-generation is needed.
404 @returns false The dependency list file is outofdate. Re-generation is needed.
405 **/
406 private boolean isUptodate() {
407 File df = new File(depsFile);
408 if (!df.exists()) {
409 return false;
410 }
411
412 ///
413 /// If the source file(s) is newer than dependency list file, we need to
414 /// re-generate the dependency list file
415 ///
416 long depsFileTimeStamp = df.lastModified();
417 Iterator iterator = inputFileList.iterator();
418 while (iterator.hasNext()) {
419 Input inputFile = (Input)iterator.next();
420 File sf = new File(inputFile.getFile());
421 if (sf.lastModified() > depsFileTimeStamp) {
422 return false;
423 }
424 }
425
426 ///
427 /// If the source files haven't been changed since last time the dependency
428 /// list file was generated, we need to check each file in the file list to
429 /// see if any of them is changed or not. If anyone of them is newer than
430 /// the dependency list file, MakeDeps.exe is needed to run again.
431 ///
432 LineNumberReader lineReader = null;
433 FileReader fileReader = null;
434 boolean ret = true;
435 try {
436 fileReader = new FileReader(df);
437 lineReader = new LineNumberReader(fileReader);
438
439 String line = null;
440 while ((line = lineReader.readLine()) != null) {
441 File sourceFile = new File(line);
442 if (sourceFile.lastModified() > depsFileTimeStamp) {
443 ret = false;
444 break;
445 }
446 }
447 lineReader.close();
448 fileReader.close();
449 } catch (IOException e) {
450 log (e.getMessage());
451 }
452
453 return ret;
454 }
455 }
456