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