2 """ A small program to compute checksums of LLVM checkout.
4 from __future__
import absolute_import
5 from __future__
import division
6 from __future__
import print_function
12 from argparse
import ArgumentParser
13 from project_tree
import *
15 SVN_DATES_REGEX
= re
.compile(r
"\$(Date|LastChangedDate)[^\$]+\$")
19 parser
= ArgumentParser()
21 "-v", "--verbose", action
="store_true", help="enable debug logging")
25 metavar
="reference_file",
26 help="read checksums from reference_file and " +
27 "check they match checksums of llvm_path.")
31 help="ignore projects from reference_file " +
32 "that are not checked out in llvm_path.")
36 help="indicates llvm_path contains llvm, checked out " +
37 "into multiple directories, as opposed to a " +
38 "typical single source tree checkout.")
39 parser
.add_argument("llvm_path")
41 args
= parser
.parse_args()
42 if args
.check
is not None:
43 with
open(args
.check
, "r") as f
:
44 reference_checksums
= ReadLLVMChecksums(f
)
46 reference_checksums
= None
49 logging
.basicConfig(level
=logging
.DEBUG
)
51 llvm_projects
= CreateLLVMProjects(not args
.multi_dir
)
52 checksums
= ComputeLLVMChecksums(args
.llvm_path
, llvm_projects
)
54 if reference_checksums
is None:
55 WriteLLVMChecksums(checksums
, sys
.stdout
)
58 if not ValidateChecksums(reference_checksums
, checksums
, args
.partial
):
59 sys
.stdout
.write("Checksums differ.\nNew checksums:\n")
60 WriteLLVMChecksums(checksums
, sys
.stdout
)
61 sys
.stdout
.write("Reference checksums:\n")
62 WriteLLVMChecksums(reference_checksums
, sys
.stdout
)
65 sys
.stdout
.write("Checksums match.")
68 def ComputeLLVMChecksums(root_path
, projects
):
69 """Compute checksums for LLVM sources checked out using svn.
72 root_path: a directory of llvm checkout.
73 projects: a list of LLVMProject instances, which describe checkout paths,
74 relative to root_path.
77 A dict mapping from project name to project checksum.
79 hash_algo
= hashlib
.sha256
81 def collapse_svn_substitutions(contents
):
82 # Replace svn substitutions for $Date$ and $LastChangedDate$.
83 # Unfortunately, these are locale-specific.
84 return SVN_DATES_REGEX
.sub("$\1$", contents
)
86 def read_and_collapse_svn_subsitutions(file_path
):
87 with
open(file_path
, "rb") as f
:
89 new_contents
= collapse_svn_substitutions(contents
)
90 if contents
!= new_contents
:
91 logging
.debug("Replaced svn keyword substitutions in %s", file_path
)
92 logging
.debug("\n\tBefore\n%s\n\tAfter\n%s", contents
, new_contents
)
95 project_checksums
= dict()
98 project_root
= os
.path
.join(root_path
, proj
.relpath
)
99 if not os
.path
.exists(project_root
):
100 logging
.info("Folder %s doesn't exist, skipping project %s", proj
.relpath
,
106 def add_file_hash(file_path
):
107 if os
.path
.islink(file_path
) and not os
.path
.exists(file_path
):
108 content
= os
.readlink(file_path
)
110 content
= read_and_collapse_svn_subsitutions(file_path
)
112 hasher
.update(content
)
113 file_digest
= hasher
.hexdigest()
114 logging
.debug("Checksum %s for file %s", file_digest
, file_path
)
115 files
.append((file_path
, file_digest
))
117 logging
.info("Computing checksum for %s", proj
.name
)
118 WalkProjectFiles(root_path
, projects
, proj
, add_file_hash
)
120 # Compute final checksum.
121 files
.sort(key
=lambda x
: x
[0])
123 for file_path
, file_digest
in files
:
124 file_path
= os
.path
.relpath(file_path
, project_root
)
125 hasher
.update(file_path
)
126 hasher
.update(file_digest
)
127 project_checksums
[proj
.name
] = hasher
.hexdigest()
128 return project_checksums
131 def WriteLLVMChecksums(checksums
, f
):
132 """Writes checksums to a text file.
135 checksums: a dict mapping from project name to project checksum (result of
136 ComputeLLVMChecksums).
137 f: a file object to write into.
140 for proj
in sorted(checksums
.keys()):
141 f
.write("{} {}\n".format(checksums
[proj
], proj
))
144 def ReadLLVMChecksums(f
):
145 """Reads checksums from a text file, produced by WriteLLVMChecksums.
148 A dict, mapping from project name to project checksum.
155 checksum
, proj
= line
.split()
156 checksums
[proj
] = checksum
160 def ValidateChecksums(reference_checksums
,
162 allow_missing_projects
=False):
163 """Validates that reference_checksums and new_checksums match.
166 reference_checksums: a dict of reference checksums, mapping from a project
167 name to a project checksum.
168 new_checksums: a dict of checksums to be checked, mapping from a project
169 name to a project checksum.
170 allow_missing_projects:
171 When True, reference_checksums may contain more projects than
172 new_checksums. Projects missing from new_checksums are ignored.
173 When False, new_checksums and reference_checksums must contain checksums
174 for the same set of projects. If there is a project in
175 reference_checksums, missing from new_checksums, ValidateChecksums
179 True, if checksums match with regards to allow_missing_projects flag value.
182 if not allow_missing_projects
:
183 if len(new_checksums
) != len(reference_checksums
):
186 for proj
, checksum
in new_checksums
.items():
187 # We never computed a checksum for this project.
188 if proj
not in reference_checksums
:
190 # Checksum did not match.
191 if reference_checksums
[proj
] != checksum
:
197 if __name__
== "__main__":