summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--BaseTools/Source/Python/AutoGen/AutoGen.py10
-rw-r--r--BaseTools/Source/Python/Common/LongFilePathOs.py4
-rw-r--r--BaseTools/Source/Python/Common/Misc.py55
3 files changed, 64 insertions, 5 deletions
diff --git a/BaseTools/Source/Python/AutoGen/AutoGen.py b/BaseTools/Source/Python/AutoGen/AutoGen.py
index bedd871136..f50941d422 100644
--- a/BaseTools/Source/Python/AutoGen/AutoGen.py
+++ b/BaseTools/Source/Python/AutoGen/AutoGen.py
@@ -3925,11 +3925,11 @@ class ModuleAutoGen(AutoGen):
CreateDirectory (FileDir)
HashFile = path.join(self.BuildDir, self.Name + '.hash')
if os.path.exists(HashFile):
- shutil.copy2(HashFile, FileDir)
+ CopyFileOnChange(HashFile, FileDir)
if not self.IsLibrary:
ModuleFile = path.join(self.OutputDir, self.Name + '.inf')
if os.path.exists(ModuleFile):
- shutil.copy2(ModuleFile, FileDir)
+ CopyFileOnChange(ModuleFile, FileDir)
else:
OutputDir = self.OutputDir.replace('\\', '/').strip('/')
DebugDir = self.DebugDir.replace('\\', '/').strip('/')
@@ -3949,7 +3949,7 @@ class ModuleAutoGen(AutoGen):
destination_file = os.path.join(FileDir, sub_dir)
destination_dir = os.path.dirname(destination_file)
CreateDirectory(destination_dir)
- shutil.copy2(File, destination_dir)
+ CopyFileOnChange(File, destination_dir)
def AttemptModuleCacheCopy(self):
# If library or Module is binary do not skip by hash
@@ -3971,14 +3971,14 @@ class ModuleAutoGen(AutoGen):
for root, dir, files in os.walk(FileDir):
for f in files:
if self.Name + '.hash' in f:
- shutil.copy(HashFile, self.BuildDir)
+ CopyFileOnChange(HashFile, self.BuildDir)
else:
File = path.join(root, f)
sub_dir = os.path.relpath(File, FileDir)
destination_file = os.path.join(self.OutputDir, sub_dir)
destination_dir = os.path.dirname(destination_file)
CreateDirectory(destination_dir)
- shutil.copy(File, destination_dir)
+ CopyFileOnChange(File, destination_dir)
if self.Name == "PcdPeim" or self.Name == "PcdDxe":
CreatePcdDatabaseCode(self, TemplateString(), TemplateString())
return True
diff --git a/BaseTools/Source/Python/Common/LongFilePathOs.py b/BaseTools/Source/Python/Common/LongFilePathOs.py
index ae8a68e4ad..190f36d7ec 100644
--- a/BaseTools/Source/Python/Common/LongFilePathOs.py
+++ b/BaseTools/Source/Python/Common/LongFilePathOs.py
@@ -60,6 +60,10 @@ def listdir(path):
List.append(Item)
return List
+if hasattr(os, 'replace'):
+ def replace(src, dst):
+ return os.replace(LongFilePath(src), LongFilePath(dst))
+
environ = os.environ
getcwd = os.getcwd
chdir = os.chdir
diff --git a/BaseTools/Source/Python/Common/Misc.py b/BaseTools/Source/Python/Common/Misc.py
index d082c58bef..9a63463913 100644
--- a/BaseTools/Source/Python/Common/Misc.py
+++ b/BaseTools/Source/Python/Common/Misc.py
@@ -18,6 +18,7 @@ import re
import pickle
import array
import shutil
+import filecmp
from random import sample
from struct import pack
import uuid
@@ -502,6 +503,60 @@ def SaveFileOnChange(File, Content, IsBinaryFile=True):
return True
+## Copy source file only if it is different from the destination file
+#
+# This method is used to copy file only if the source file and destination
+# file content are different. This is quite useful to avoid duplicated
+# file writing.
+#
+# @param SrcFile The path of source file
+# @param Dst The path of destination file or folder
+#
+# @retval True The two files content are different and the file is copied
+# @retval False No copy really happen
+#
+def CopyFileOnChange(SrcFile, Dst):
+ if not os.path.exists(SrcFile):
+ return False
+
+ if os.path.isdir(Dst):
+ DstFile = os.path.join(Dst, os.path.basename(SrcFile))
+ else:
+ DstFile = Dst
+
+ if os.path.exists(DstFile) and filecmp.cmp(SrcFile, DstFile, shallow=False):
+ return False
+
+ DirName = os.path.dirname(DstFile)
+ if not CreateDirectory(DirName):
+ EdkLogger.error(None, FILE_CREATE_FAILURE, "Could not create directory %s" % DirName)
+ else:
+ if DirName == '':
+ DirName = os.getcwd()
+ if not os.access(DirName, os.W_OK):
+ EdkLogger.error(None, PERMISSION_FAILURE, "Do not have write permission on directory %s" % DirName)
+
+ # os.replace and os.rename are the atomic operations in python 3 and 2.
+ # we use these two atomic operations to ensure the file copy is atomic:
+ # copy the src to a temp file in the dst same folder firstly, then
+ # replace or rename the temp file to the destination file.
+ with tempfile.NamedTemporaryFile(dir=DirName, delete=False) as tf:
+ shutil.copy(SrcFile, tf.name)
+ tempname = tf.name
+ try:
+ if hasattr(os, 'replace'):
+ os.replace(tempname, DstFile)
+ else:
+ # os.rename reqire to remove the dst on Windows, otherwise OSError will be raised.
+ if GlobalData.gIsWindows and os.path.exists(DstFile):
+ os.remove(DstFile)
+ os.rename(tempname, DstFile)
+
+ except IOError as X:
+ EdkLogger.error(None, FILE_COPY_FAILURE, ExtraData='IOError %s' % X)
+
+ return True
+
## Retrieve and cache the real path name in file system
#
# @param Root The root directory of path relative to