| #!/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())) |