1# Copyright (C) 2022 The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15import collections 16import os 17import sys 18 19# Workaround for python include path 20# TODO: restructure things so that we do not need this. 21# pylint: disable=wrong-import-position,import-error 22_ninja_dir = os.path.realpath( 23 os.path.join(os.path.dirname(__file__), "..", "ninja")) 24if _ninja_dir not in sys.path: 25 sys.path.append(_ninja_dir) 26import ninja_writer 27from ninja_syntax import BuildAction, Rule 28# pylint: enable=wrong-import-position,import-error 29 30 31class Ninja(ninja_writer.Writer): 32 """Some higher level constructs on top of raw ninja writing. 33 34 TODO: Not sure where these should be. 35 """ 36 37 def __init__(self, context, file, **kwargs): 38 super().__init__( 39 file, 40 builddir=context.out.root(base=context.out.Base.OUTER), 41 **kwargs) 42 self._context = context 43 self._did_copy_file = False 44 self._write_rule = None 45 self._phonies = collections.defaultdict(set) 46 self._acp = self._context.tools.acp() 47 48 def add_copy_file(self, copy_to, copy_from): 49 self.add_rule( 50 Rule("copy_file", [ 51 ("command", 52 f"mkdir -p ${{out_dir}} && {self._acp} -f ${{in}} ${{out}}") 53 ])) 54 self.add_build_action( 55 BuildAction(output=copy_to, 56 rule="copy_file", 57 inputs=[copy_from], 58 implicits=[self._acp], 59 variables=[("out_dir", os.path.dirname(copy_to))])) 60 61 def add_global_phony(self, name, deps): 62 """Add a global phony target. 63 64 This should be used when there are multiple places that will want to add 65 to the same phony, with possibly different dependencies. The resulting 66 dependency list will be the combined dependency list. 67 68 If you can, to save memory, use add_phony instead of this function. 69 """ 70 assert isinstance(deps, (set, list, tuple)), f"bad type: {type(deps)}" 71 self._phonies[name] |= set(deps) 72 73 def write(self, *args, **kwargs): 74 """Write the file, including our global phonies.""" 75 for phony, deps in self._phonies.items(): 76 self.add_phony(phony, deps) 77 super().write(*args, **kwargs) 78 79 def add_write_file(self, filepath: str, content: str): 80 """Writes the content as a string to filepath 81 82 The content is written as-is, special characters are not escaped 83 """ 84 self.add_rule( 85 Rule("write_file", [("description", "Writes content to out"), 86 ("command", "printf '${content}' > ${out}")])) 87 88 self.add_build_action( 89 BuildAction(output=filepath, 90 rule="write_file", 91 variables=[("content", content)])) 92