27e0ed7be5b7476b9dc4349e117395c77faac8d1
[people/mcb30/edk2.git] / edk2 / Tools / Java / Source / FrameworkTasks / org / tianocore / framework / tasks / MakeDeps.java
1 /** @file\r
2 This file is to wrap MakeDeps.exe tool as ANT task, which is used to generate\r
3 dependency files for source code.\r
4 \r
5 Copyright (c) 2006, Intel Corporation\r
6 All rights reserved. This program and the accompanying materials\r
7 are licensed and made available under the terms and conditions of the BSD License\r
8 which accompanies this distribution.  The full text of the license may be found at\r
9 http://opensource.org/licenses/bsd-license.php\r
10 \r
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13 \r
14 **/\r
15 package org.tianocore.framework.tasks;\r
16 \r
17 import java.io.BufferedReader;\r
18 import java.io.BufferedWriter;\r
19 import java.io.File;\r
20 import java.io.FileReader;\r
21 import java.io.FileWriter;\r
22 import java.io.IOException;\r
23 import java.io.LineNumberReader;\r
24 import java.util.HashMap;\r
25 import java.util.HashSet;\r
26 import java.util.Iterator;\r
27 import java.util.LinkedHashSet;\r
28 import java.util.List;\r
29 import java.util.Set;\r
30 import java.util.Stack;\r
31 import java.util.regex.Matcher;\r
32 import java.util.regex.Pattern;\r
33 \r
34 import org.apache.tools.ant.BuildException;\r
35 import org.apache.tools.ant.Project;\r
36 import org.apache.tools.ant.Task;\r
37 import org.apache.tools.ant.taskdefs.Execute;\r
38 import org.apache.tools.ant.taskdefs.LogStreamHandler;\r
39 import org.apache.tools.ant.types.Commandline;\r
40 import org.tianocore.common.cache.FileTimeStamp;\r
41 import org.tianocore.common.logger.EdkLog;\r
42 \r
43 /**\r
44  Class MakeDeps is used to wrap MakeDeps.exe as an ANT task.\r
45  **/\r
46 public class MakeDeps extends Task {\r
47 \r
48     //\r
49     // private members, use set/get to access them\r
50     //\r
51     private String      depsFilePath = "";\r
52     private IncludePath includePathList = new IncludePath();\r
53     private Input       inputFileList = new Input();\r
54     //\r
55     // cache the including files to speed up dependency check\r
56     // \r
57     private static HashMap<String, Set<String>> includesCache = new HashMap<String, Set<String>>();\r
58     //\r
59     // regular expression for "#include ..." directive\r
60     // \r
61     private static final Pattern incPattern = Pattern.compile("[\n\r \t]*#[ \t]*include[ \t\"<]+([^\n\r\"<>]+)");\r
62 \r
63     public MakeDeps() {\r
64 \r
65     }\r
66 \r
67     /**\r
68      The Standard execute method for ANT task. It will check if it's necessary\r
69      to generate the dependency list file. If no file is found or the dependency\r
70      is changed, it will compose the command line and call MakeDeps.exe to\r
71      generate the dependency list file.\r
72 \r
73      @throws    BuildException\r
74      **/\r
75     public void execute() throws BuildException {\r
76         //\r
77         // check if the dependency list file is uptodate or not\r
78         //\r
79         if (isUptodate()) {\r
80             return;\r
81         }\r
82 \r
83         //\r
84         // if no include path is specified, try locally\r
85         // \r
86         if (includePathList.isEmpty()) {\r
87             includePathList.insPath(".");\r
88         }\r
89 \r
90         Set<String> depFiles = getDependencies(inputFileList.toArray());\r
91 \r
92         File depsFile = new File(depsFilePath);\r
93         FileWriter fileWriter = null;\r
94         BufferedWriter bufWriter = null;\r
95 \r
96         try {\r
97             fileWriter = new FileWriter(depsFile);\r
98             bufWriter = new BufferedWriter(fileWriter);\r
99 \r
100 \r
101             for (Iterator it = depFiles.iterator(); it.hasNext();) {\r
102                 String depFile = (String)it.next();\r
103                 bufWriter.write(depFile, 0, depFile.length());\r
104                 bufWriter.write("\n", 0, 1);\r
105             }\r
106             //\r
107             // put a "tab" at the end of file as file ending flag\r
108             // \r
109             bufWriter.write("\t", 0, 1);\r
110         } catch (Exception e) {\r
111             throw new BuildException(e.getMessage());\r
112         } finally {\r
113             try {\r
114                 if (bufWriter != null) {\r
115                     bufWriter.close();\r
116                 }\r
117                 if (fileWriter != null) {\r
118                     fileWriter.close();\r
119                 }\r
120             } catch (Exception e) {\r
121                 throw new BuildException(e.getMessage());\r
122             }\r
123         }\r
124 \r
125         //\r
126         // update time stamp of dependency file\r
127         // \r
128         FileTimeStamp.update(depsFilePath, depsFile.lastModified());\r
129     }\r
130 \r
131     /**\r
132      Set method for "DepsFile" attribute\r
133 \r
134      @param     name    The name of dependency list file\r
135      **/\r
136     public void setDepsFile(String name) {\r
137         depsFilePath = name;\r
138     }\r
139 \r
140     /**\r
141      Get method for "DepsFile" attribute\r
142 \r
143      @returns   The name of dependency list file\r
144      **/\r
145     public String getDepsFile() {\r
146         return depsFilePath;\r
147     }\r
148 \r
149     /**\r
150      Add method for "IncludePath" nested element\r
151 \r
152      @param     path    The IncludePath object from nested IncludePath type of element\r
153      **/\r
154     public void addConfiguredIncludepath(IncludePath path) {\r
155         includePathList.insert(path);\r
156     }\r
157 \r
158     /**\r
159      Add method for "Input" nested element\r
160 \r
161      @param     input   The Input object from nested Input type of element\r
162      **/\r
163     public void addConfiguredInput(Input inputFile) {\r
164         inputFileList.insert(inputFile);\r
165     }\r
166 \r
167     /**\r
168      Check if the dependency list file should be (re-)generated or not.\r
169 \r
170      @returns   true    The dependency list file is uptodate. No re-generation is needed.\r
171      @returns   false   The dependency list file is outofdate. Re-generation is needed.\r
172      **/\r
173     private boolean isUptodate() {\r
174         File df = new File(depsFilePath);\r
175         if (!df.exists()) {\r
176             EdkLog.log(this, EdkLog.EDK_VERBOSE, depsFilePath + " doesn't exist!");\r
177             return false;\r
178         }\r
179 \r
180         //\r
181         // If the source file(s) is newer than dependency list file, we need to\r
182         // re-generate the dependency list file\r
183         //\r
184         long depsFileTimeStamp = FileTimeStamp.get(depsFilePath);\r
185         List<String> fileList = inputFileList.getNameList();\r
186         for (int i = 0, length = fileList.size(); i < length; ++i) {\r
187             String sf = fileList.get(i);\r
188             if (FileTimeStamp.get(sf) > depsFileTimeStamp) {\r
189                 EdkLog.log(this, EdkLog.EDK_VERBOSE, sf + " has been changed since last build!");\r
190                 return false;\r
191             }\r
192         }\r
193 \r
194         //\r
195         // If the source files haven't been changed since last time the dependency\r
196         // list file was generated, we need to check each file in the file list to\r
197         // see if any of them is changed or not. If anyone of them is newer than\r
198         // the dependency list file, MakeDeps.exe is needed to run again.\r
199         //\r
200         LineNumberReader    lineReader = null;\r
201         FileReader          fileReader = null;\r
202         boolean             ret = false;\r
203         try {\r
204             fileReader = new FileReader(df);\r
205             lineReader = new LineNumberReader(fileReader);\r
206 \r
207             String line = null;\r
208             int lines = 0;\r
209             while ((line = lineReader.readLine()) != null) {\r
210                 //\r
211                 // check file end flag "\t" to see if the .dep was generated correctly\r
212                 // \r
213                 if (line.equals("\t")) {\r
214                     ret = true;\r
215                     continue;\r
216                 }\r
217                 line = line.trim();\r
218                 //\r
219                 // skip empty line\r
220                 // \r
221                 if (line.length() == 0) {\r
222                     continue;\r
223                 }\r
224                 ++lines;\r
225 \r
226                 //\r
227                 // If a file cannot be found (moved or removed) or newer, regenerate the dep file\r
228                 // \r
229                 File sourceFile = new File(line);\r
230                 if ((!sourceFile.exists()) || (FileTimeStamp.get(line) > depsFileTimeStamp)) {\r
231                     EdkLog.log(this, EdkLog.EDK_VERBOSE, sourceFile.getPath() + " has been (re)moved or changed since last build!");\r
232                     ret = false;\r
233                     break;\r
234                 }\r
235             }\r
236 \r
237             //\r
238             // check if the .dep file is empty\r
239             // \r
240             if (lines == 0) {\r
241                 EdkLog.log(this, EdkLog.EDK_VERBOSE, depsFilePath + " is empty!");\r
242                 ret = false;\r
243             }\r
244         } catch (IOException e) {\r
245             throw new BuildException(e.getMessage());\r
246         } finally {\r
247             try {\r
248                 if (lineReader != null) {\r
249                     lineReader.close();\r
250                 }\r
251                 if (fileReader != null) {\r
252                     fileReader.close();\r
253                 }\r
254             } catch (Exception e) {\r
255                 throw new BuildException(e.getMessage());\r
256             }\r
257         }\r
258 \r
259         return ret;\r
260     }\r
261 \r
262     //\r
263     // get dependent files list by parsing "#include" directive\r
264     // \r
265     private synchronized Set<String> getDependencies(String[] sourceFiles) {\r
266         Set<String> dependencies = new LinkedHashSet<String>();\r
267         String[] searchPathList = includePathList.toArray();\r
268 \r
269         Stack<String> pendingFiles = new Stack<String>();\r
270         for (int i = 0; i < sourceFiles.length; ++i) {\r
271             File srcFile = new File(sourceFiles[i]);\r
272             if (srcFile.exists()) {\r
273                 //\r
274                 // a file must depend itself\r
275                 // \r
276                 dependencies.add(srcFile.getAbsolutePath());\r
277                 pendingFiles.push(sourceFiles[i]);\r
278             }\r
279         }\r
280 \r
281         while (!pendingFiles.empty()) {\r
282             String src = pendingFiles.pop();\r
283             File srcFile = new File(src);\r
284             if (!srcFile.exists()) {\r
285                 continue;\r
286             }\r
287 \r
288             //\r
289             // try cache first\r
290             // \r
291             Set<String> incFiles = includesCache.get(src);\r
292             if (incFiles == null) {\r
293                 incFiles = new HashSet<String>();\r
294                 FileReader fileReader = null;\r
295                 BufferedReader bufReader = null;\r
296                 String fileContent = "";\r
297                 int fileLength = (int)srcFile.length();\r
298 \r
299                 try {\r
300                     fileReader = new FileReader(srcFile);\r
301                     bufReader  = new BufferedReader(fileReader);\r
302                     char[] buf = new char[fileLength];\r
303 \r
304                     bufReader.read(buf, 0, fileLength);\r
305                     fileContent = new String(buf);\r
306                 } catch (IOException e) {\r
307                     throw new BuildException(e.getMessage());\r
308                 } finally {\r
309                     try {\r
310                         if (bufReader != null) {\r
311                             bufReader.close();\r
312                         }\r
313                         if (fileReader != null) {\r
314                             fileReader.close();\r
315                         }\r
316                     } catch (Exception e) {\r
317                         throw new BuildException(e.getMessage());\r
318                     }\r
319                 }\r
320 \r
321                 //\r
322                 // find out all "#include" lines\r
323                 // \r
324                 Matcher matcher = incPattern.matcher(fileContent);\r
325                 while (matcher.find()) {\r
326                     String incFilePath = fileContent.substring(matcher.start(1), matcher.end(1));\r
327                     incFiles.add(incFilePath);\r
328                 }\r
329 \r
330                 //\r
331                 // put the includes in cache to avoid re-parsing\r
332                 // \r
333                 includesCache.put(src, incFiles);\r
334             }\r
335 \r
336             //\r
337             // try each include search path to see if the include file exists or not\r
338             // \r
339             for (Iterator<String> it = incFiles.iterator(); it.hasNext();) {\r
340                 String depFilePath = it.next();\r
341 \r
342                 for (int i = 0; i < searchPathList.length; ++i) {\r
343                     File depFile = new File(searchPathList[i] + File.separator + depFilePath);\r
344                     String filePath = depFile.getAbsolutePath();\r
345                     //\r
346                     // following check is a must. it can prevent dead loop if two\r
347                     // files include each other\r
348                     // \r
349                     if (depFile.exists() && !dependencies.contains(filePath)) {\r
350                         dependencies.add(filePath);\r
351                         pendingFiles.push(filePath);\r
352                         break;\r
353                     }\r
354                 }\r
355             }\r
356         }\r
357 \r
358         return dependencies;\r
359     }\r
360 }\r
361 \r