},\r
"""\r
\r
- ReModifyFile = re.compile(r'[B-Q,S-Z]+[\d]*\t(.*)')\r
FindModifyFile = re.compile(r'\+\+\+ b\/(.*)')\r
LineScopePattern = (r'@@ -\d*\,*\d* \+\d*\,*\d* @@.*')\r
LineNumRange = re.compile(r'@@ -\d*\,*\d* \+(\d*)\,*(\d*) @@.*')\r
os.path.join(basetools_path, "Source", "Python", "Ecc", "exception.xml"),\r
os.path.join(temp_path, "exception.xml")\r
)\r
+ # Output file to use for git diff operations\r
+ temp_diff_output = os.path.join (temp_path, 'diff.txt')\r
\r
self.ApplyConfig(pkgconfig, temp_path, packagename)\r
- modify_dir_list = self.GetModifyDir(packagename)\r
- patch = self.GetDiff(packagename)\r
+ modify_dir_list = self.GetModifyDir(packagename, temp_diff_output)\r
+ patch = self.GetDiff(packagename, temp_diff_output)\r
ecc_diff_range = self.GetDiffRange(patch, packagename, temp_path)\r
#\r
# Use temp_path as working directory when running ECC tool\r
raise\r
return 1\r
\r
- def GetDiff(self, pkg: str) -> List[str]:\r
- return_buffer = StringIO()\r
- params = "diff --unified=0 origin/master HEAD"\r
- RunCmd("git", params, outstream=return_buffer)\r
- p = return_buffer.getvalue().strip()\r
- patch = p.split("\n")\r
- return_buffer.close()\r
-\r
+ def GetDiff(self, pkg: str, temp_diff_output: str) -> List[str]:\r
+ patch = []\r
+ #\r
+ # Generate unified diff between origin/master and HEAD.\r
+ #\r
+ params = "diff --output={} --unified=0 origin/master HEAD".format(temp_diff_output)\r
+ RunCmd("git", params)\r
+ with open(temp_diff_output) as file:\r
+ patch = file.read().strip().split('\n')\r
return patch\r
\r
- def GetModifyDir(self, pkg: str) -> List[str]:\r
- return_buffer = StringIO()\r
- params = "diff --name-status" + ' HEAD' + ' origin/master'\r
- RunCmd("git", params, outstream=return_buffer)\r
- p1 = return_buffer.getvalue().strip()\r
- dir_list = p1.split("\n")\r
- return_buffer.close()\r
+ def GetModifyDir(self, pkg: str, temp_diff_output: str) -> List[str]:\r
+ #\r
+ # Generate diff between origin/master and HEAD using --diff-filter to\r
+ # exclude deleted and renamed files that do not need to be scanned by\r
+ # ECC. Also use --name-status to only generate the names of the files\r
+ # with differences. The output format of this git diff command is a\r
+ # list of files with the change status and the filename. The filename\r
+ # is always at the end of the line. Examples:\r
+ #\r
+ # M MdeModulePkg/Application/CapsuleApp/CapsuleApp.h\r
+ # M MdeModulePkg/Application/UiApp/FrontPage.h\r
+ #\r
+ params = "diff --output={} --diff-filter=dr --name-status origin/master HEAD".format(temp_diff_output)\r
+ RunCmd("git", params)\r
+ dir_list = []\r
+ with open(temp_diff_output) as file:\r
+ dir_list = file.read().strip().split('\n')\r
+\r
modify_dir_list = []\r
for modify_dir in dir_list:\r
- file_path = self.ReModifyFile.findall(modify_dir)\r
- if file_path:\r
- file_dir = os.path.dirname(file_path[0])\r
- else:\r
+ #\r
+ # Parse file name from the end of the line\r
+ #\r
+ file_path = modify_dir.strip().split()\r
+ #\r
+ # Skip lines that do not have at least 2 elements (status and file name)\r
+ #\r
+ if len(file_path) < 2:\r
continue\r
- if pkg in file_dir and file_dir != pkg:\r
- modify_dir_list.append('%s' % file_dir)\r
- else:\r
+ #\r
+ # Parse the directory name from the file name\r
+ #\r
+ file_dir = os.path.dirname(file_path[-1])\r
+ #\r
+ # Skip directory names that do not start with the package being scanned.\r
+ #\r
+ if file_dir.split('/')[0] != pkg:\r
+ continue\r
+ #\r
+ # Skip directory names that are identical to the package being scanned.\r
+ # The assumption here is that there are no source files at the package\r
+ # root. Instead, the only expected files in the package root are\r
+ # EDK II meta data files (DEC, DSC, FDF).\r
+ #\r
+ if file_dir == pkg:\r
continue\r
+ #\r
+ # Skip directory names that are already in the modified dir list\r
+ #\r
+ if file_dir in modify_dir_list:\r
+ continue\r
+ #\r
+ # Add the candidate directory to scan to the modified dir list\r
+ #\r
+ modify_dir_list.append(file_dir)\r
\r
- modify_dir_list = list(set(modify_dir_list))\r
- return modify_dir_list\r
+ #\r
+ # Remove duplicates from modify_dir_list\r
+ # Given a folder path, ECC performs a recursive scan of that folder.\r
+ # If a parent and child folder are both present in modify_dir_list,\r
+ # then ECC will perform redudanct scans of source files. In order\r
+ # to prevent redundant scans, if a parent and child folder are both\r
+ # present, then remove all the child folders.\r
+ #\r
+ # For example, if modified_dir_list contains the following elements:\r
+ # MdeModulePkg/Core/Dxe\r
+ # MdeModulePkg/Core/Dxe/Hand\r
+ # MdeModulePkg/Core/Dxe/Mem\r
+ #\r
+ # Then MdeModulePkg/Core/Dxe/Hand and MdeModulePkg/Core/Dxe/Mem should\r
+ # be removed because the files in those folders are covered by a scan\r
+ # of MdeModulePkg/Core/Dxe.\r
+ #\r
+ filtered_list = []\r
+ for dir1 in modify_dir_list:\r
+ Append = True\r
+ for dir2 in modify_dir_list:\r
+ if dir1 == dir2:\r
+ continue\r
+ common = os.path.commonpath([dir1, dir2])\r
+ if os.path.normpath(common) == os.path.normpath(dir2):\r
+ Append = False\r
+ break\r
+ if Append and dir1 not in filtered_list:\r
+ filtered_list.append(dir1)\r
+ return filtered_list\r
\r
def GetDiffRange(self, patch_diff: List[str], pkg: str, temp_path: str) -> Dict[str, List[Tuple[int, int]]]:\r
IsDelete = True\r