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