**/\r
package org.tianocore.framework.tasks;\r
\r
+import java.io.BufferedReader;\r
+import java.io.BufferedWriter;\r
import java.io.File;\r
import java.io.FileReader;\r
+import java.io.FileWriter;\r
import java.io.IOException;\r
import java.io.LineNumberReader;\r
+import java.util.HashMap;\r
+import java.util.HashSet;\r
+import java.util.Iterator;\r
+import java.util.LinkedHashSet;\r
import java.util.List;\r
+import java.util.Set;\r
+import java.util.Stack;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
\r
import org.apache.tools.ant.BuildException;\r
import org.apache.tools.ant.Project;\r
import org.apache.tools.ant.taskdefs.Execute;\r
import org.apache.tools.ant.taskdefs.LogStreamHandler;\r
import org.apache.tools.ant.types.Commandline;\r
-\r
-import org.tianocore.common.logger.EdkLog;\r
import org.tianocore.common.cache.FileTimeStamp;\r
+import org.tianocore.common.logger.EdkLog;\r
\r
/**\r
Class MakeDeps is used to wrap MakeDeps.exe as an ANT task.\r
//\r
// private members, use set/get to access them\r
//\r
- private static final String toolName = "MakeDeps";\r
- private FileArg depsFile = new FileArg();\r
- private ToolArg subDir = new ToolArg();\r
- private ToolArg quietMode = new ToolArg(" -", "q");\r
- private ToolArg ignoreError = new ToolArg(" -", "ignorenotfound");\r
- private IncludePath includePathList = new IncludePath();\r
- private Input inputFileList = new Input();\r
- private ToolArg target = new FileArg(" -target ", "dummy");\r
+ private String depsFilePath = "";\r
+ private IncludePath includePathList = new IncludePath();\r
+ private Input inputFileList = new Input();\r
+ //\r
+ // cache the including files to speed up dependency check\r
+ // \r
+ private static HashMap<String, Set<String>> includesCache = new HashMap<String, Set<String>>();\r
+ //\r
+ // regular expression for "#include ..." directive\r
+ // \r
+ private static final Pattern incPattern = Pattern.compile("[\n\r \t]*#[ \t]*include[ \t\"<]+([^\n\r\"<>]+)");\r
\r
public MakeDeps() {\r
\r
@throws BuildException\r
**/\r
public void execute() throws BuildException {\r
- ///\r
- /// check if the dependency list file is uptodate or not\r
- ///\r
+ //\r
+ // check if the dependency list file is uptodate or not\r
+ //\r
if (isUptodate()) {\r
return;\r
}\r
\r
- Project prj = this.getOwningTarget().getProject();\r
- String toolPath = prj.getProperty("env.FRAMEWORK_TOOLS_PATH");\r
-\r
- ///\r
- /// compose full tool path\r
- ///\r
- if (toolPath == null || toolPath.length() == 0) {\r
- toolPath = toolName;\r
- } else {\r
- if (toolPath.endsWith("/") || toolPath.endsWith("\\")) {\r
- toolPath = toolPath + toolName;\r
- } else {\r
- toolPath = toolPath + File.separator + toolName;\r
- }\r
+ //\r
+ // if no include path is specified, try locally\r
+ // \r
+ if (includePathList.isEmpty()) {\r
+ includePathList.insPath(".");\r
}\r
\r
- ///\r
- /// compose tool arguments\r
- ///\r
- String argument = "" + inputFileList + includePathList + subDir\r
- + quietMode + ignoreError + target + depsFile;\r
+ Set<String> depFiles = getDependencies(inputFileList.toArray());\r
\r
- ///\r
- /// prepare to execute the tool\r
- ///\r
- Commandline cmd = new Commandline();\r
- cmd.setExecutable(toolPath);\r
- cmd.createArgument().setLine(argument);\r
+ File depsFile = new File(depsFilePath);\r
+ FileWriter fileWriter = null;\r
+ BufferedWriter bufWriter = null;\r
\r
- LogStreamHandler streamHandler = new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_WARN);\r
- Execute runner = new Execute(streamHandler, null);\r
-\r
- runner.setAntRun(prj);\r
- runner.setCommandline(cmd.getCommandline());\r
+ try {\r
+ fileWriter = new FileWriter(depsFile);\r
+ bufWriter = new BufferedWriter(fileWriter);\r
\r
- EdkLog.log(this, EdkLog.EDK_VERBOSE, Commandline.toString(cmd.getCommandline()));\r
\r
- int result = 0;\r
- try {\r
- result = runner.execute();\r
- } catch (IOException e) {\r
+ for (Iterator it = depFiles.iterator(); it.hasNext();) {\r
+ String depFile = (String)it.next();\r
+ bufWriter.write(depFile, 0, depFile.length());\r
+ bufWriter.write("\n", 0, 1);\r
+ }\r
+ //\r
+ // put a "tab" at the end of file as file ending flag\r
+ // \r
+ bufWriter.write("\t", 0, 1);\r
+ } catch (Exception e) {\r
throw new BuildException(e.getMessage());\r
+ } finally {\r
+ try {\r
+ if (bufWriter != null) {\r
+ bufWriter.close();\r
+ }\r
+ if (fileWriter != null) {\r
+ fileWriter.close();\r
+ }\r
+ } catch (Exception e) {\r
+ throw new BuildException(e.getMessage());\r
+ }\r
}\r
\r
- if (result != 0) {\r
- EdkLog.log(this, EdkLog.EDK_INFO, toolName + " failed!");\r
- throw new BuildException(toolName + ": failed to generate dependency file!");\r
- } else {\r
- EdkLog.log(this, EdkLog.EDK_VERBOSE, toolName + " succeeded!");\r
- }\r
+ //\r
+ // update time stamp of dependency file\r
+ // \r
+ FileTimeStamp.update(depsFilePath, depsFile.lastModified());\r
}\r
\r
/**\r
@param name The name of dependency list file\r
**/\r
public void setDepsFile(String name) {\r
- depsFile.setArg(" -o ", name);\r
+ depsFilePath = name;\r
}\r
\r
/**\r
@returns The name of dependency list file\r
**/\r
public String getDepsFile() {\r
- return depsFile.getValue();\r
- }\r
-\r
- /**\r
- Set method for "IgnoreError" attribute\r
-\r
- @param ignore flag to control error handling (true/false)\r
- **/\r
- public void setIgnoreError(boolean ignore) {\r
- if (!ignore) {\r
- ignoreError.setArg(" ", " ");\r
- }\r
- }\r
-\r
- /**\r
- Get method for "IgnoreError" attribute\r
-\r
- @returns The value of current IgnoreError flag\r
- **/\r
- public boolean getIgnoreError() {\r
- return ignoreError.getValue().length() > 0;\r
- }\r
-\r
- /**\r
- Set method for "QuietMode" attribute\r
-\r
- @param quiet flag to control the output information (true/false)\r
- **/\r
- public void setQuietMode(boolean quiet) {\r
- if (!quiet) {\r
- quietMode.setArg(" ", " ");\r
- }\r
- }\r
-\r
- /**\r
- Get method for "QuietMode" attribute\r
-\r
- @returns value of current QuietMode flag\r
- **/\r
- public boolean getQuietMode() {\r
- return quietMode.getValue().length() > 0;\r
- }\r
-\r
- /**\r
- Set method for "SubDir" attribute\r
-\r
- @param dir The name of sub-directory in which source files will be scanned\r
- **/\r
- public void setSubDir(String dir) {\r
- subDir.setArg(" -s ", dir);\r
- }\r
-\r
- /**\r
- Get method for "SubDir" attribute\r
-\r
- @returns The name of sub-directory\r
- **/\r
- public String getSubDir() {\r
- return subDir.getValue();\r
+ return depsFilePath;\r
}\r
\r
/**\r
@returns false The dependency list file is outofdate. Re-generation is needed.\r
**/\r
private boolean isUptodate() {\r
- String dfName = depsFile.getValue();\r
- File df = new File(dfName);\r
+ File df = new File(depsFilePath);\r
if (!df.exists()) {\r
- EdkLog.log(this, EdkLog.EDK_VERBOSE, dfName + " doesn't exist!");\r
+ EdkLog.log(this, EdkLog.EDK_VERBOSE, depsFilePath + " doesn't exist!");\r
return false;\r
}\r
\r
// If the source file(s) is newer than dependency list file, we need to\r
// re-generate the dependency list file\r
//\r
- long depsFileTimeStamp = FileTimeStamp.get(dfName);\r
+ long depsFileTimeStamp = FileTimeStamp.get(depsFilePath);\r
List<String> fileList = inputFileList.getNameList();\r
for (int i = 0, length = fileList.size(); i < length; ++i) {\r
String sf = fileList.get(i);\r
// check if the .dep file is empty\r
// \r
if (lines == 0) {\r
- EdkLog.log(this, EdkLog.EDK_VERBOSE, dfName + " is empty!");\r
+ EdkLog.log(this, EdkLog.EDK_VERBOSE, depsFilePath + " is empty!");\r
ret = false;\r
}\r
-\r
- lineReader.close();\r
- fileReader.close();\r
} catch (IOException e) {\r
throw new BuildException(e.getMessage());\r
+ } finally {\r
+ try {\r
+ if (lineReader != null) {\r
+ lineReader.close();\r
+ }\r
+ if (fileReader != null) {\r
+ fileReader.close();\r
+ }\r
+ } catch (Exception e) {\r
+ throw new BuildException(e.getMessage());\r
+ }\r
}\r
\r
return ret;\r
}\r
+\r
+ //\r
+ // get dependent files list by parsing "#include" directive\r
+ // \r
+ private synchronized Set<String> getDependencies(String[] sourceFiles) {\r
+ Set<String> dependencies = new LinkedHashSet<String>();\r
+ String[] searchPathList = includePathList.toArray();\r
+\r
+ Stack<String> pendingFiles = new Stack<String>();\r
+ for (int i = 0; i < sourceFiles.length; ++i) {\r
+ File srcFile = new File(sourceFiles[i]);\r
+ if (srcFile.exists()) {\r
+ //\r
+ // a file must depend itself\r
+ // \r
+ dependencies.add(srcFile.getAbsolutePath());\r
+ pendingFiles.push(sourceFiles[i]);\r
+ }\r
+ }\r
+\r
+ while (!pendingFiles.empty()) {\r
+ String src = pendingFiles.pop();\r
+ File srcFile = new File(src);\r
+ if (!srcFile.exists()) {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // try cache first\r
+ // \r
+ Set<String> incFiles = includesCache.get(src);\r
+ if (incFiles == null) {\r
+ incFiles = new HashSet<String>();\r
+ FileReader fileReader = null;\r
+ BufferedReader bufReader = null;\r
+ String fileContent = "";\r
+ int fileLength = (int)srcFile.length();\r
+\r
+ try {\r
+ fileReader = new FileReader(srcFile);\r
+ bufReader = new BufferedReader(fileReader);\r
+ char[] buf = new char[fileLength];\r
+\r
+ bufReader.read(buf, 0, fileLength);\r
+ fileContent = new String(buf);\r
+ } catch (IOException e) {\r
+ throw new BuildException(e.getMessage());\r
+ } finally {\r
+ try {\r
+ if (bufReader != null) {\r
+ bufReader.close();\r
+ }\r
+ if (fileReader != null) {\r
+ fileReader.close();\r
+ }\r
+ } catch (Exception e) {\r
+ throw new BuildException(e.getMessage());\r
+ }\r
+ }\r
+\r
+ //\r
+ // find out all "#include" lines\r
+ // \r
+ Matcher matcher = incPattern.matcher(fileContent);\r
+ while (matcher.find()) {\r
+ String incFilePath = fileContent.substring(matcher.start(1), matcher.end(1));\r
+ incFiles.add(incFilePath);\r
+ }\r
+\r
+ //\r
+ // put the includes in cache to avoid re-parsing\r
+ // \r
+ includesCache.put(src, incFiles);\r
+ }\r
+\r
+ //\r
+ // try each include search path to see if the include file exists or not\r
+ // \r
+ for (Iterator<String> it = incFiles.iterator(); it.hasNext();) {\r
+ String depFilePath = it.next();\r
+\r
+ for (int i = 0; i < searchPathList.length; ++i) {\r
+ File depFile = new File(searchPathList[i] + File.separator + depFilePath);\r
+ String filePath = depFile.getAbsolutePath();\r
+ //\r
+ // following check is a must. it can prevent dead loop if two\r
+ // files include each other\r
+ // \r
+ if (depFile.exists() && !dependencies.contains(filePath)) {\r
+ dependencies.add(filePath);\r
+ pendingFiles.push(filePath);\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ return dependencies;\r
+ }\r
}\r
\r