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