Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit e275d2d

Browse files
author
Victor "Nate" Graf
authored
Fix incremental build by preventing nonchanging writes (#15817)
Fix incremental build by preventing non-changing writes in generated ETW source code
1 parent 240c607 commit e275d2d

File tree

4 files changed

+87
-61
lines changed

4 files changed

+87
-61
lines changed

build.cmd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ if /i "%__BuildNative%"=="1" (
411411

412412
echo %__MsgPrefix%Laying out dynamically generated files consumed by the native build system
413413
echo %__MsgPrefix%Laying out dynamically generated Event test files and etmdummy stub functions
414-
"!PYTHON!" -B -Wall %__SourceDir%\scripts\genEventing.py --inc %__IntermediatesIncDir% --dummy %__IntermediatesIncDir%\etmdummy.h --man %__SourceDir%\vm\ClrEtwAll.man --nonextern || exit /b 1
414+
"!PYTHON!" -B -Wall %__SourceDir%\scripts\genEventing.py --inc %__IntermediatesIncDir% --dummy %__IntermediatesIncDir%\etmdummy.h --man %__SourceDir%\vm\ClrEtwAll.man --nonextern --noxplatheader|| exit /b 1
415415

416416
echo %__MsgPrefix%Laying out dynamically generated EventPipe Implementation
417417
"!PYTHON!" -B -Wall %__SourceDir%\scripts\genEventPipe.py --man %__SourceDir%\vm\ClrEtwAll.man --intermediate %__IntermediatesEventingDir%\eventpipe --nonextern || exit /b 1

src/scripts/genEtwProvider.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import subprocess
1515
import xml.dom.minidom as DOM
1616
from genEventing import parseTemplateNodes
17-
from utilities import open_for_update
17+
from utilities import open_for_update, update_directory
1818

1919
macroheader_filename = "etwmacros.h"
2020
mcheader_filename = "ClrEtwAll.h"
@@ -49,7 +49,7 @@
4949
"""
5050

5151
def genProviderInterface(manifest, intermediate):
52-
provider_dirname = os.path.join(intermediate, etw_dirname)
52+
provider_dirname = os.path.join(intermediate, etw_dirname + "_temp")
5353

5454
if not os.path.exists(provider_dirname):
5555
os.makedirs(provider_dirname)
@@ -64,7 +64,7 @@ def genProviderInterface(manifest, intermediate):
6464
for pattern, replacement in replacements:
6565
header_text = re.sub(pattern, replacement, header_text)
6666

67-
with open_for_update(path.join(provider_dirname, mcheader_filename)) as mcheader_file:
67+
with open(path.join(provider_dirname, mcheader_filename), 'w') as mcheader_file:
6868
mcheader_file.write(header_text)
6969

7070
def genCmake(intermediate):
@@ -218,7 +218,7 @@ def checkConsistency(manifest, exclusion_filename):
218218
raise Exception(sStackSpecificityError)
219219

220220
def genEtwMacroHeader(manifest, exclusion_filename, intermediate):
221-
provider_dirname = os.path.join(intermediate, etw_dirname)
221+
provider_dirname = os.path.join(intermediate, etw_dirname + "_temp")
222222

223223
if not os.path.exists(provider_dirname):
224224
os.makedirs(provider_dirname)
@@ -307,6 +307,15 @@ def main(argv):
307307
checkConsistency(manifest, exclusion_filename)
308308
genFiles(manifest, intermediate, exclusion_filename)
309309

310+
# Update the final directory from temp
311+
provider_temp_dirname = os.path.join(intermediate, etw_dirname + "_temp")
312+
provider_dirname = os.path.join(intermediate, etw_dirname)
313+
if not os.path.exists(provider_dirname):
314+
os.makedirs(provider_dirname)
315+
316+
update_directory(provider_temp_dirname, provider_dirname)
317+
shutil.rmtree(provider_temp_dirname)
318+
310319
if __name__ == '__main__':
311320
return_code = main(sys.argv[1:])
312321
sys.exit(return_code)

src/scripts/genEventing.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,7 @@ def generateEtmDummyHeader(sClrEtwAllMan,clretwdummy):
721721
#pal: create etmdummy.h
722722
Clretwdummy.write(generateclrEtwDummy(eventNodes, allTemplates) + "\n")
723723

724-
def generatePlatformIndependentFiles(sClrEtwAllMan, incDir, etmDummyFile, extern):
724+
def generatePlatformIndependentFiles(sClrEtwAllMan, incDir, etmDummyFile, extern, write_xplatheader):
725725

726726
generateEtmDummyHeader(sClrEtwAllMan,etmDummyFile)
727727
tree = DOM.parse(sClrEtwAllMan)
@@ -751,13 +751,23 @@ def generatePlatformIndependentFiles(sClrEtwAllMan, incDir, etmDummyFile, extern
751751
Clrallevents.write(generateClrallEvents(eventNodes, allTemplates) + "\n")
752752

753753

754-
# Write secondary headers for FireEtXplat* and EventPipe* functions
755-
clrxplatevents = os.path.join(incDir, "clrxplatevents.h")
756754
clreventpipewriteevents = os.path.join(incDir, "clreventpipewriteevents.h")
757-
with open_for_update(clrxplatevents) as Clrxplatevents:
758-
with open_for_update(clreventpipewriteevents) as Clreventpipewriteevents:
755+
with open_for_update(clreventpipewriteevents) as Clreventpipewriteevents:
756+
Clreventpipewriteevents.write(stdprolog + "\n")
757+
758+
for providerNode in tree.getElementsByTagName('provider'):
759+
templateNodes = providerNode.getElementsByTagName('template')
760+
allTemplates = parseTemplateNodes(templateNodes)
761+
eventNodes = providerNode.getElementsByTagName('event')
762+
763+
#eventpipe: create clreventpipewriteevents.h
764+
Clreventpipewriteevents.write(generateClrEventPipeWriteEvents(eventNodes, allTemplates, extern) + "\n")
765+
766+
# Write secondary headers for FireEtXplat* and EventPipe* functions
767+
if write_xplatheader:
768+
clrxplatevents = os.path.join(incDir, "clrxplatevents.h")
769+
with open_for_update(clrxplatevents) as Clrxplatevents:
759770
Clrxplatevents.write(stdprolog + "\n")
760-
Clreventpipewriteevents.write(stdprolog + "\n")
761771

762772
for providerNode in tree.getElementsByTagName('provider'):
763773
templateNodes = providerNode.getElementsByTagName('template')
@@ -767,9 +777,6 @@ def generatePlatformIndependentFiles(sClrEtwAllMan, incDir, etmDummyFile, extern
767777
#pal: create clrallevents.h
768778
Clrxplatevents.write(generateClrXplatEvents(eventNodes, allTemplates, extern) + "\n")
769779

770-
#eventpipe: create clreventpipewriteevents.h
771-
Clreventpipewriteevents.write(generateClrEventPipeWriteEvents(eventNodes, allTemplates, extern) + "\n")
772-
773780
import argparse
774781
import sys
775782

@@ -789,6 +796,8 @@ def main(argv):
789796
help='full path to directory where the test assets will be deployed' )
790797
required.add_argument('--nonextern', action='store_true',
791798
help='if specified, will not generated extern function stub headers' )
799+
required.add_argument('--noxplatheader', action='store_true',
800+
help='if specified, will not write a generated cross-platform header' )
792801
args, unknown = parser.parse_known_args(argv)
793802
if unknown:
794803
print('Unknown argument(s): ', ', '.join(unknown))
@@ -799,8 +808,9 @@ def main(argv):
799808
etmDummyFile = args.dummy
800809
testDir = args.testdir
801810
extern = not args.nonextern
811+
write_xplatheader = not args.noxplatheader
802812

803-
generatePlatformIndependentFiles(sClrEtwAllMan, incdir, etmDummyFile, extern)
813+
generatePlatformIndependentFiles(sClrEtwAllMan, incdir, etmDummyFile, extern, write_xplatheader)
804814
generateSanityTest(sClrEtwAllMan, testDir)
805815

806816
if __name__ == '__main__':

src/scripts/utilities.py

Lines changed: 53 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
##
66
## This file provides utility functions to the adjacent python scripts
77

8-
from filecmp import dircmp
98
from hashlib import sha256
109
from io import StringIO
10+
import filecmp
1111
import shutil
1212
import sys
1313
import os
@@ -68,48 +68,55 @@ def __exit__(self, exc_type, exc_value, traceback):
6868
def open_for_update(filename):
6969
return UpdateFileWriter(filename)
7070

71-
def walk_recursively_and_update(dcmp):
72-
#for different Files Copy from right to left
73-
for name in dcmp.diff_files:
74-
srcpath = dcmp.right + "/" + name
75-
destpath = dcmp.left + "/" + name
76-
print("Updating %s" % (destpath))
77-
if os.path.isfile(srcpath):
78-
shutil.copyfile(srcpath, destpath)
79-
else :
80-
raise Exception("path: " + srcpath + "is neither a file or folder")
81-
82-
#copy right only files
83-
for name in dcmp.right_only:
84-
srcpath = dcmp.right + "/" + name
85-
destpath = dcmp.left + "/" + name
86-
print("Updating %s" % (destpath))
87-
if os.path.isfile(srcpath):
88-
shutil.copyfile(srcpath, destpath)
89-
elif os.path.isdir(srcpath):
90-
shutil.copytree(srcpath, destpath)
91-
else :
92-
raise Exception("path: " + srcpath + "is neither a file or folder")
93-
94-
#delete left only files
95-
for name in dcmp.left_only:
96-
path = dcmp.left + "/" + name
97-
print("Deleting %s" % (path))
98-
if os.path.isfile(path):
99-
os.remove(path)
100-
elif os.path.isdir(path):
101-
shutil.rmtree(path)
102-
else :
103-
raise Exception("path: " + path + "is neither a file or folder")
104-
105-
#call recursively
106-
for sub_dcmp in dcmp.subdirs.values():
107-
walk_recursively_and_update(sub_dcmp)
108-
109-
def UpdateDirectory(destpath,srcpath):
110-
111-
print("Updating %s with %s" % (destpath,srcpath))
112-
if not os.path.exists(destpath):
113-
os.makedirs(destpath)
114-
dcmp = dircmp(destpath,srcpath)
115-
walk_recursively_and_update(dcmp)
71+
def split_entries(entries, directory):
72+
"""Given a list of entries in a directory, listing return a set of file and a set of dirs"""
73+
files = set([entry for entry in entries if os.path.isfile(os.path.join(directory, entry))])
74+
dirs = set([entry for entry in entries if os.path.isdir(os.path.join(directory, entry))])
75+
76+
return files, dirs
77+
78+
def update_directory(srcpath, dstpath, recursive=True, destructive=True, shallow=False):
79+
"""Updates dest directory with files from src directory
80+
81+
Args:
82+
destpath (str): The destination path to sync with the source
83+
srcpath (str): The source path to sync to the destination
84+
recursive(boolean): If True, descend into and update subdirectories (default: True)
85+
destructive(boolean): If True, delete files in the destination which do not exist in the source (default: True)
86+
shallow(boolean): If True, only use os.stat to diff files. Do not examine contents (default: False)
87+
"""
88+
srcfiles, srcdirs = split_entries(os.listdir(srcpath), srcpath)
89+
dstfiles, dstdirs = split_entries(os.listdir(dstpath), dstpath)
90+
91+
92+
# Update files in both src and destination which are different in destination
93+
commonfiles = srcfiles.intersection(dstfiles)
94+
_, mismatches, errors = filecmp.cmpfiles(srcpath, dstpath, commonfiles, shallow=shallow)
95+
96+
if errors:
97+
raise RuntimeError("Comparison failed for the following files(s): {}".format(errors))
98+
99+
for mismatch in mismatches:
100+
shutil.copyfile(os.path.join(srcpath, mismatch), os.path.join(dstpath, mismatch))
101+
102+
# Copy over files from source which do not exist in the destination
103+
for missingfile in srcfiles.difference(dstfiles):
104+
shutil.copyfile(os.path.join(srcpath, missingfile), os.path.join(dstpath, missingfile))
105+
106+
#If destructive, delete files in destination which do not exist in sourc
107+
if destructive:
108+
for deadfile in dstfiles.difference(srcfiles):
109+
print(deadfile)
110+
os.remove(os.path.join(dstpath, deadfile))
111+
112+
for deaddir in dstdirs.difference(srcdirs):
113+
print(deaddir)
114+
shutil.rmtree(os.path.join(dstpath, deaddir))
115+
116+
#If recursive, do this again for each source directory
117+
if recursive:
118+
for dirname in srcdirs:
119+
dstdir, srcdir = os.path.join(dstpath, dirname), os.path.join(srcpath, dirname)
120+
if not os.path.exists(dstdir):
121+
os.makedirs(dstdir)
122+
update_directory(srcdir, dstdir, recursive, destructive, shallow)

0 commit comments

Comments
 (0)