chromium/chrome/tools/build/sha256_file.py

#!/usr/bin/env python
# Copyright 2021 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""sha256_file.py takes one or more files, computes a SHA-256 hash over it,
and writes a .cc file containing variables with the digest bytes.

Usage:
    sha256_file.py path/to/hashes file1.txt file2.pak

Which will create path/to/hashes.cc with a constant for each of the specified
input file's hash. Corresponding header file should be committed in codebase to
avoid unnecessary build serilization.
"""

import hashlib
import os.path
import sys


def main(argv):
    if len(argv) < 3:
        print('Usage: {} output_path_prefix file1.pak...'.format(argv[0]),
              file=sys.stderr)
        return 1

    output_path_prefix = argv[1]

    cc_contents = '#include "{}.h"\n\n'.format(
        # Drop (<toolchain>/)gen/ prefix.
        output_path_prefix.split('gen/', 1)[1])
    for (name, value) in _hash_files(argv[2:]):
        name = 'kSha256_' + os.path.basename(name).replace('.', '_')
        cc_contents += 'const std::array<uint8_t, 32> {} = {{'.format(name)
        cc_contents += ', '.join(map(hex, value))
        cc_contents += '};\n\n'

    with open(output_path_prefix + '.cc', 'w') as f:
        f.write(FILE_TEMPLATE.format(contents=cc_contents))


def _hash_files(files):
    for path in files:
        with open(path, 'rb') as f:
            yield path, _hash_file_contents(f)


def _hash_file_contents(f):
    sha2 = hashlib.sha256()
    while True:
        data = f.read(4096)
        if not data:
            break
        sha2.update(data)
    return sha2.digest()


FILE_TEMPLATE = """// Generated by chrome/tools/build/sha256_file.py
// !! DO NOT EDIT !!

#include <stdint.h>

#include <array>

{contents}
"""

if __name__ == '__main__':
    sys.exit(main(sys.argv))