1# 2# Copyright (C) 2022 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16import json 17import os 18import sys 19 20import ninja_tools 21import ninja_syntax # Has to be after ninja_tools because of the path hack 22 23 24def final_packaging(context, inner_trees): 25 """Pull together all of the previously defined rules into the final build stems.""" 26 27 with open(context.out.outer_ninja_file(), "w", 28 encoding='iso-8859-1') as ninja_file: 29 ninja = ninja_tools.Ninja(context, ninja_file) 30 31 # Add the api surfaces file 32 ninja.add_subninja( 33 ninja_syntax.Subninja( 34 context.out.api_ninja_file(base=context.out.Base.OUTER))) 35 36 # For each inner tree 37 for tree in inner_trees.keys(): 38 # TODO: Verify that inner_tree.ninja was generated 39 40 # Read and verify file 41 build_targets = read_build_targets_json(context, tree) 42 if not build_targets: 43 continue 44 45 # Generate the ninja and build files for this inner tree 46 generate_cross_domain_build_rules(context, ninja, tree, 47 build_targets) 48 49 # Finish writing the ninja file 50 ninja.write() 51 52 53def read_build_targets_json(context, tree): 54 """Read and validate the build_targets.json file for the given tree.""" 55 try: 56 f = open(tree.out.build_targets_file(), encoding='iso-8859-1') 57 except FileNotFoundError: 58 # It's allowed not to have any artifacts (e.g. if a tree is a light tree with only APIs) 59 return None 60 61 data = None 62 with f: 63 try: 64 data = json.load(f) 65 except json.decoder.JSONDecodeError as ex: 66 sys.stderr.write("Error parsing file: %s\n" % 67 tree.out.build_targets_file()) 68 # TODO: Error reporting 69 raise ex 70 71 # TODO: Better error handling 72 # TODO: Validate json schema 73 return data 74 75 76def generate_cross_domain_build_rules(context, ninja, tree, build_targets): 77 "Generate the ninja and build files for the inner tree." 78 # Include the inner tree's inner_tree.ninja 79 ninja.add_subninja( 80 ninja_syntax.Subninja( 81 tree.out.main_ninja_file(base=tree.out.Base.OUTER), 82 chdir=tree.root, 83 env_vars=tree.env_used)) 84 85 # Generate module rules and files 86 for module in build_targets.get("modules", []): 87 generate_shared_module(context, ninja, tree, module) 88 89 # Generate staging rules 90 staging_dir = context.out.staging_dir(base=context.out.Base.OUTER) 91 for staged in build_targets.get("staging", []): 92 # TODO: Enforce that dest isn't in disallowed subdir of out or absolute 93 dest = staged["dest"] 94 dest = os.path.join(staging_dir, dest) 95 if "src" in staged and "obj" in staged: 96 context.errors.error( 97 "Can't have both \"src\" and \"obj\" tags in \"staging\" entry." 98 ) # TODO: Filename and line if possible 99 if "src" in staged: 100 ninja.add_copy_file(dest, os.path.join(tree.root, staged["src"])) 101 elif "obj" in staged: 102 ninja.add_copy_file( 103 dest, 104 os.path.join(tree.out.root(base=tree.out.Base.OUTER), 105 staged["obj"])) 106 ninja.add_global_phony("staging", [dest]) 107 108 # Generate dist rules 109 dist_dir = context.out.dist_dir(base=context.out.Base.OUTER) 110 for disted in build_targets.get("dist", []): 111 # TODO: Enforce that dest absolute 112 dest = disted["dest"] 113 dest = os.path.join(dist_dir, dest) 114 ninja.add_copy_file(dest, os.path.join(tree.root, disted["src"])) 115 ninja.add_global_phony("dist", [dest]) 116 117 118def generate_shared_module(context, ninja, tree, module): 119 """Generate ninja rules for the given build_targets.json defined module.""" 120 module_name = module["name"] 121 module_type = module["type"] 122 share_dir = context.out.module_share_dir(module_type, 123 module_name, 124 base=context.out.Base.OUTER) 125 src_file = os.path.join(tree.root, module["file"]) 126 127 if module_type == "apex": 128 ninja.add_copy_file(os.path.join(share_dir, module_name + ".apex"), 129 src_file) 130 # TODO: Generate build file 131 132 else: 133 # TODO: Better error handling 134 raise Exception("Invalid module type: %s" % module) 135