Browse Source

Calculate hash the same way as in Cura.

Hashes generated in the previous way did not match. By copying (a slightly modified) version of how this is actually checked in Uranium, there is more confidence that the hashes calculated this way will actually match what is expected to see in Cura/Uranium.

part of CURA-9068
pull/1/head
Remco Burema 4 years ago
parent
commit
f559421ba2
  1. 82
      generators/CuraPackage.py

82
generators/CuraPackage.py

@ -3,8 +3,8 @@ import shutil
import json
import sysconfig
import hashlib
from pathlib import Path
from typing import List, Set, Tuple
from jinja2 import Template
@ -17,6 +17,69 @@ FILTERED_FILES = ["conaninfo.txt", "conanmanifest.txt"]
FILTERED_DIRS = ["include"]
def _hashPath(path: str, verbose: bool = False) -> str:
"""
Calls the hash function according to the type of the path (directory or file).
:param path: The path that needs to be hashed.
:return: A cryptographic hash of the specified path.
"""
if os.path.isdir(path):
return _hashDirectory(path, verbose)
elif os.path.isfile(path):
return _hashFile(path, verbose)
raise FileNotFoundError(f"The specified path '{path}' was neither a file nor a directory.")
def _hashFile(file_path: str, verbose: bool = False) -> str:
"""
Returns a SHA-256 hash of the specified file.
:param file_path: The path to a file to get the hash of.
:return: A cryptographic hash of the specified file.
"""
block_size = 2 ** 16
hasher = hashlib.sha256()
with open(file_path, "rb") as f:
contents = f.read(block_size)
while len(contents) > 0:
hasher.update(contents)
contents = f.read(block_size)
if verbose:
print(f"Hashing {file_path}")
return hasher.hexdigest()
def _hashDirectory(directory_path: str, exclude_paths: Set[str], verbose: bool = False) -> str:
"""
Returns a SHA-256 hash of the specified directory. The hash is calculated by hashing all individual files and then
appending all the filenames/hashes together. The hash of that string is the hash of the folder.
:param directory_path: The path to a directory to get the hash of.
:return: A cryptographic hash of the specified directory.
"""
hash_list: List[Tuple[str, str]] = [] # Contains a list of (relative_file_path, file_hash) tuples
for root, _, filenames in os.walk(directory_path):
if root in exclude_paths:
continue
for filename in filenames:
rel_dir_path = os.path.relpath(root, directory_path)
rel_path = os.path.join(rel_dir_path, filename)
if rel_dir_path in exclude_paths or rel_path in exclude_paths:
continue
abs_path = os.path.join(root, filename)
hash_list.append((rel_path, _hashFile(abs_path, verbose)))
# We need to be sure that the list is sorted by the relative_file_path to account for cases where the files are read
# in different orders.
ordered_list = sorted(hash_list, key=lambda x: x[0])
hasher = hashlib.sha256()
for i in ordered_list:
hasher.update("".join(i).encode('utf-8'))
return hasher.hexdigest()
class CuraPackage(Generator):
_content_types_xml = r"""<?xml version="1.0" encoding="UTF-8"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
@ -217,6 +280,7 @@ if __name__ == "__main__":
deps = {}
central_storage = []
exclude_paths_from_hashing = set()
for dep_name in self.conanfile.deps_cpp_info.deps:
deps[dep_name] = {"binpaths": set(),
@ -238,10 +302,12 @@ if __name__ == "__main__":
files += [d for d in dirs if os.path.islink(os.path.join(root, d))]
rel_path = Path(os.path.relpath(root, rootpath))
if rel_path in FILTERED_DIRS or any([rel_path.is_relative_to(d) for d in FILTERED_DIRS]):
exclude_paths_from_hashing.add(os.path.relpath(root, rootpath))
continue
for f in files:
if f in FILTERED_FILES:
exclude_paths_from_hashing.add(os.path.join(os.path.relpath(root, rootpath), f))
continue
src = Path(os.path.normpath(os.path.join(root, f)))
@ -264,25 +330,17 @@ if __name__ == "__main__":
if os.path.isfile(dst) or os.path.islink(dst):
os.unlink(dst)
os.symlink(linkto, dst)
if calc_hash:
hash_files[rel_path] = sha256sum(linkto)
else:
if dst.exists():
src_hash = sha256sum(src)
if sha256sum(dst) == src_hash:
if calc_hash:
hash_files[rel_path] = src_hash
continue
if calc_hash:
hash_files[rel_path] = src_hash if dst.exists() else sha256sum(src)
shutil.copy(src, dst)
if calc_hash:
sorted_hash_paths = sorted(hash_files.keys())
hasher = hashlib.sha256()
for hash_path in sorted_hash_paths:
hasher.update("".join((str(hash_path), hash_files[hash_path])).encode('utf-8'))
cs_dep_hash = _hashDirectory(rootpath, exclude_paths=exclude_paths_from_hashing)
dep_version = Version(self.conanfile.deps_cpp_info[dep_name].version)
central_storage.append([os.path.join("deps", dep_name), dep_name, f"{dep_version.major}.{dep_version.minor}.{dep_version.patch}", hasher.hexdigest()])
central_storage.append([os.path.join("deps", dep_name), dep_name, f"{dep_version.major}.{dep_version.minor}.{dep_version.patch}", cs_dep_hash])
deps[dep_name]["binpaths"] = {bin_dir.relative_to(dep_name) for bin_dir in deps[dep_name]["binpaths"] if Path(self._curapackage_deps_path, bin_dir).exists()}

Loading…
Cancel
Save