| #!/usr/bin/env python3 | 
 | # Copyright 2022 The Dawn & Tint Authors | 
 | # | 
 | # Redistribution and use in source and binary forms, with or without | 
 | # modification, are permitted provided that the following conditions are met: | 
 | # | 
 | # 1. Redistributions of source code must retain the above copyright notice, this | 
 | #    list of conditions and the following disclaimer. | 
 | # | 
 | # 2. Redistributions in binary form must reproduce the above copyright notice, | 
 | #    this list of conditions and the following disclaimer in the documentation | 
 | #    and/or other materials provided with the distribution. | 
 | # | 
 | # 3. Neither the name of the copyright holder nor the names of its | 
 | #    contributors may be used to endorse or promote products derived from | 
 | #    this software without specific prior written permission. | 
 | # | 
 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | 
 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 
 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | 
 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | 
 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | 
 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | 
 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
 |  | 
 | import os, subprocess, sys, shutil | 
 |  | 
 | from generator_lib import Generator, run_generator, FileRender, GeneratorOutput | 
 |  | 
 | def get_git(): | 
 |     # Will find git, git.exe, git.bat... | 
 |     git_exec = shutil.which("git") | 
 |     if not git_exec: | 
 |         raise Exception("No git executable found") | 
 |  | 
 |     return git_exec | 
 |  | 
 |  | 
 | def get_git_hash(dawn_dir): | 
 |     try: | 
 |         result = subprocess.run([get_git(), "rev-parse", "HEAD"], | 
 |                                 stdout=subprocess.PIPE, | 
 |                                 cwd=dawn_dir) | 
 |         if result.returncode == 0: | 
 |             return result.stdout.decode("utf-8").strip() | 
 |     except Exception: | 
 |         return "" | 
 |     # No hash was available (possibly) because the directory was not a git checkout. Dawn should | 
 |     # explicitly handle its absenece and disable features relying on the hash, i.e. caching. | 
 |     return "" | 
 |  | 
 |  | 
 | def get_git_head(dawn_dir): | 
 |     return os.path.join(dawn_dir, ".git", "HEAD") | 
 |  | 
 |  | 
 | def git_exists(dawn_dir): | 
 |     return os.path.exists(get_git_head(dawn_dir)) | 
 |  | 
 |  | 
 | def unpack_git_ref(packed, resolved): | 
 |     with open(packed) as fin: | 
 |         refs = fin.read().strip().split("\n") | 
 |  | 
 |     # Strip comments | 
 |     refs = [ref.split(" ") for ref in refs if ref.strip()[0] != "#"] | 
 |  | 
 |     # Parse results which are in the format [<gitHash>, <refFile>] from previous step. | 
 |     refs = [gitHash for (gitHash, refFile) in refs if refFile == resolved] | 
 |     if len(refs) == 1: | 
 |         with open(resolved, "w") as fout: | 
 |             fout.write(refs[0] + "\n") | 
 |         return True | 
 |     return False | 
 |  | 
 |  | 
 | def get_git_resolved_head(dawn_dir): | 
 |     result = subprocess.run( | 
 |         [get_git(), "rev-parse", "--symbolic-full-name", "HEAD"], | 
 |         stdout=subprocess.PIPE, | 
 |         cwd=dawn_dir) | 
 |     if result.returncode != 0: | 
 |         raise Exception("Failed to execute git rev-parse to resolve git head:", result.stdout) | 
 |  | 
 |     resolved = os.path.join(dawn_dir, ".git", | 
 |                             result.stdout.decode("utf-8").strip()) | 
 |  | 
 |     # Check a packed-refs file exists. If so, we need to potentially unpack and include it as a dep. | 
 |     packed = os.path.join(dawn_dir, ".git", "packed-refs") | 
 |     if os.path.exists(packed) and unpack_git_ref(packed, resolved): | 
 |         return [packed, resolved] | 
 |  | 
 |     if not os.path.exists(resolved): | 
 |         raise Exception("Unable to resolve git HEAD hash file:", resolved) | 
 |     return [resolved] | 
 |  | 
 |  | 
 | def get_version(args): | 
 |     version_file = args.version_file | 
 |     if version_file: | 
 |         with open(version_file) as f: | 
 |             return f.read() | 
 |     return get_git_hash(os.path.abspath(args.dawn_dir)) | 
 |  | 
 |  | 
 | def compute_params(args): | 
 |     return { | 
 |         "get_version": lambda: get_version(args), | 
 |     } | 
 |  | 
 |  | 
 | class DawnVersionGenerator(Generator): | 
 |     def get_description(self): | 
 |         return ( | 
 |             "Generates version dependent Dawn code. Currently regenerated dependent on the version " | 
 |             "header (if available), otherwise tries to use git hash.") | 
 |  | 
 |     def add_commandline_arguments(self, parser): | 
 |         parser.add_argument( | 
 |             "--dawn-dir", | 
 |             required=True, | 
 |             type=str, | 
 |             help="The Dawn root directory path to use", | 
 |         ) | 
 |         parser.add_argument( | 
 |             "--version-file", | 
 |             required=False, | 
 |             type=str, | 
 |             help= | 
 |             ("Path to one-liner version string file used when git may not be present. " | 
 |              "In general the version string is a git hash.")) | 
 |  | 
 |     def get_dependencies(self, args): | 
 |         dawn_dir = os.path.abspath(args.dawn_dir) | 
 |         version_file = args.version_file | 
 |  | 
 |         if version_file: | 
 |             return [version_file] | 
 |         if git_exists(dawn_dir): | 
 |             try: | 
 |                 return [get_git_head(dawn_dir) | 
 |                         ] + get_git_resolved_head(dawn_dir) | 
 |             except Exception: | 
 |                 return [] | 
 |         return [] | 
 |  | 
 |     def get_outputs(self, args): | 
 |         params = compute_params(args) | 
 |  | 
 |         renders = [ | 
 |             FileRender("dawn/common/Version.h", | 
 |                        "src/dawn/common/Version_autogen.h", [params]), | 
 |         ] | 
 |         return GeneratorOutput(renders=renders, imported_templates=[]) | 
 |  | 
 |  | 
 | if __name__ == "__main__": | 
 |     sys.exit(run_generator(DawnVersionGenerator())) |