diff options
-rw-r--r-- | android/defs.go | 14 | ||||
-rw-r--r-- | scripts/Android.bp | 8 | ||||
-rwxr-xr-x | scripts/calculate_directory_hash.py | 62 |
3 files changed, 84 insertions, 0 deletions
diff --git a/android/defs.go b/android/defs.go index 9f3fb1ee6..c4e3b995f 100644 --- a/android/defs.go +++ b/android/defs.go @@ -102,6 +102,18 @@ var ( Description: "concatenate files to $out", }) + // Calculates the hash of a directory and writes to a file. + // Note that the directory to calculate the hash is intentionally not listed as an input, + // to prevent adding directory as a ninja dependency. Thus, an implicit dependency to a file + // is required. + WriteDirectoryHash = pctx.AndroidStaticRule("WriteDirectoryHash", + blueprint.RuleParams{ + Command: "rm -f $out && ${calculateDirectoryHash} $dir $out", + CommandDeps: []string{"${calculateDirectoryHash}"}, + Description: "Calculates the hash of a directory and writes to $out", + }, "dir", + ) + // Used only when USE_GOMA=true is set, to restrict non-goma jobs to the local parallelism value localPool = blueprint.NewBuiltinPool("local_pool") @@ -118,4 +130,6 @@ func init() { pctx.VariableFunc("RBEWrapper", func(ctx PackageVarContext) string { return ctx.Config().RBEWrapper() }) + + pctx.HostBinToolVariable("calculateDirectoryHash", "calculate_directory_hash") } diff --git a/scripts/Android.bp b/scripts/Android.bp index d39c84a0f..49e6b7399 100644 --- a/scripts/Android.bp +++ b/scripts/Android.bp @@ -327,3 +327,11 @@ python_binary_host { "rustc_linker.py", ], } + +python_binary_host { + name: "calculate_directory_hash", + main: "calculate_directory_hash.py", + srcs: [ + "calculate_directory_hash.py", + ], +} diff --git a/scripts/calculate_directory_hash.py b/scripts/calculate_directory_hash.py new file mode 100755 index 000000000..d4802d8ec --- /dev/null +++ b/scripts/calculate_directory_hash.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2024 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +"""A tool for calculating the hash of a directory based on file contents and metadata.""" + +import argparse +import hashlib +import os +import stat + +def calculate_hash(directory: str) -> str: + """ + Calculates the hash of a directory, including file contents and metadata. + + Following informations are taken into consideration: + * Name: The file or directory name. + * File Type: Whether it's a regular file, directory, symbolic link, etc. + * Size: The size of the file in bytes. + * Permissions: The file's access permissions (read, write, execute). + * Content Hash (for files): The SHA-1 hash of the file's content. + """ + + output = [] + for root, _, files in os.walk(directory): + for file in files: + filepath = os.path.join(root, file) + file_stat = os.lstat(filepath) + stat_info = f"{filepath} {stat.filemode(file_stat.st_mode)} {file_stat.st_size}" + + if os.path.islink(filepath): + stat_info += os.readlink(filepath) + elif os.path.isfile(filepath): + with open(filepath, "rb") as f: + file_hash = hashlib.sha1(f.read()).hexdigest() + stat_info += f" {file_hash}" + + output.append(stat_info) + + return hashlib.sha1("\n".join(sorted(output)).encode()).hexdigest() + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Calculate the hash of a directory.") + parser.add_argument("directory", help="Path to the directory") + parser.add_argument("output_file", help="Path to the output file") + args = parser.parse_args() + + hash_value = calculate_hash(args.directory) + with open(args.output_file, "w") as f: + f.write(hash_value) |