]> git.proxmox.com Git - mirror_edk2.git/blobdiff - Tools/Java/Source/FrameworkTasks/org/tianocore/framework/tasks/MakeDeps.java
Removed the need of external MakeDeps.exe. Now parsing include files is built in...
[mirror_edk2.git] / Tools / Java / Source / FrameworkTasks / org / tianocore / framework / tasks / MakeDeps.java
index 8f5664f01dcee3055c7e6073bbeca4bc3d27bc25..27e0ed7be5b7476b9dc4349e117395c77faac8d1 100644 (file)
@@ -14,11 +14,22 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 **/\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
@@ -26,9 +37,8 @@ import org.apache.tools.ant.Task;
 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
@@ -38,14 +48,17 @@ public class MakeDeps extends Task {
     //\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
@@ -60,63 +73,59 @@ public class MakeDeps extends Task {
      @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
@@ -125,7 +134,7 @@ public class MakeDeps extends Task {
      @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
@@ -134,65 +143,7 @@ public class MakeDeps extends Task {
      @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
@@ -220,10 +171,9 @@ public class MakeDeps extends Task {
      @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
@@ -231,7 +181,7 @@ public class MakeDeps extends Task {
         // 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
@@ -288,17 +238,124 @@ public class MakeDeps extends Task {
             // 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