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