1#!/usr/bin/env python 2# 3# Copyright (C) 2020 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16""" A tool to convert json file into pb with linker config format.""" 17 18import argparse 19import collections 20import json 21import os 22import sys 23 24import linker_config_pb2 #pylint: disable=import-error 25from google.protobuf.descriptor import FieldDescriptor 26from google.protobuf.json_format import ParseDict 27from google.protobuf.text_format import MessageToString 28 29 30def LoadJsonMessage(path): 31 """ 32 Loads a message from a .json file with `//` comments strippedfor convenience. 33 """ 34 json_content = '' 35 with open(path) as f: 36 for line in f: 37 if not line.lstrip().startswith('//'): 38 json_content += line 39 obj = json.loads(json_content, object_pairs_hook=collections.OrderedDict) 40 return ParseDict(obj, linker_config_pb2.LinkerConfig()) 41 42 43def Proto(args): 44 """ 45 Merges input json files (--source) into a protobuf message (--output). 46 Fails if the output file exists. Set --force or --append to deal with the existing 47 output file. 48 --force to overwrite the output file with the input (.json files). 49 --append to append the input to the output file. 50 """ 51 pb = linker_config_pb2.LinkerConfig() 52 if os.path.isfile(args.output): 53 if args.force: 54 pass 55 elif args.append: 56 with open(args.output, 'rb') as f: 57 pb.ParseFromString(f.read()) 58 else: 59 sys.stderr.write(f'Error: {args.output} exists. Use --force or --append.\n') 60 sys.exit(1) 61 62 if args.source: 63 for input in args.source.split(':'): 64 pb.MergeFrom(LoadJsonMessage(input)) 65 with open(args.output, 'wb') as f: 66 f.write(pb.SerializeToString()) 67 68 69def Print(args): 70 with open(args.source, 'rb') as f: 71 pb = linker_config_pb2.LinkerConfig() 72 pb.ParseFromString(f.read()) 73 print(MessageToString(pb)) 74 75 76def SystemProvide(args): 77 pb = linker_config_pb2.LinkerConfig() 78 with open(args.source, 'rb') as f: 79 pb.ParseFromString(f.read()) 80 libraries = args.value.split() 81 82 def IsInLibPath(lib_name): 83 lib_path = os.path.join(args.system, 'lib', lib_name) 84 lib64_path = os.path.join(args.system, 'lib64', lib_name) 85 return os.path.exists(lib_path) or os.path.islink( 86 lib_path) or os.path.exists(lib64_path) or os.path.islink( 87 lib64_path) 88 89 installed_libraries = [lib for lib in libraries if IsInLibPath(lib)] 90 for item in installed_libraries: 91 if item not in getattr(pb, 'provideLibs'): 92 getattr(pb, 'provideLibs').append(item) 93 with open(args.output, 'wb') as f: 94 f.write(pb.SerializeToString()) 95 96 97def Append(args): 98 pb = linker_config_pb2.LinkerConfig() 99 with open(args.source, 'rb') as f: 100 pb.ParseFromString(f.read()) 101 102 if getattr(type(pb), 103 args.key).DESCRIPTOR.label == FieldDescriptor.LABEL_REPEATED: 104 for value in args.value.split(): 105 getattr(pb, args.key).append(value) 106 else: 107 setattr(pb, args.key, args.value) 108 109 with open(args.output, 'wb') as f: 110 f.write(pb.SerializeToString()) 111 112 113def Merge(args): 114 pb = linker_config_pb2.LinkerConfig() 115 for other in args.input: 116 with open(other, 'rb') as f: 117 pb.MergeFromString(f.read()) 118 119 with open(args.out, 'wb') as f: 120 f.write(pb.SerializeToString()) 121 122 123def GetArgParser(): 124 parser = argparse.ArgumentParser() 125 subparsers = parser.add_subparsers() 126 127 parser_proto = subparsers.add_parser( 128 'proto', 129 help='Convert the input JSON configuration file into protobuf.') 130 parser_proto.add_argument( 131 '-s', 132 '--source', 133 nargs='?', 134 type=str, 135 help='Colon-separated list of linker configuration files in JSON.') 136 parser_proto.add_argument( 137 '-o', 138 '--output', 139 required=True, 140 type=str, 141 help='Target path to create protobuf file.') 142 option_for_existing_output = parser_proto.add_mutually_exclusive_group() 143 option_for_existing_output.add_argument( 144 '-f', 145 '--force', 146 action='store_true', 147 help='Overwrite if the output file exists.') 148 option_for_existing_output.add_argument( 149 '-a', 150 '--append', 151 action='store_true', 152 help='Append the input to the output file if the output file exists.') 153 parser_proto.set_defaults(func=Proto) 154 155 print_proto = subparsers.add_parser( 156 'print', help='Print configuration in human-readable text format.') 157 print_proto.add_argument( 158 '-s', 159 '--source', 160 required=True, 161 type=str, 162 help='Source linker configuration file in protobuf.') 163 print_proto.set_defaults(func=Print) 164 165 system_provide_libs = subparsers.add_parser( 166 'systemprovide', 167 help='Append system provide libraries into the configuration.') 168 system_provide_libs.add_argument( 169 '-s', 170 '--source', 171 required=True, 172 type=str, 173 help='Source linker configuration file in protobuf.') 174 system_provide_libs.add_argument( 175 '-o', 176 '--output', 177 required=True, 178 type=str, 179 help='Target linker configuration file to write in protobuf.') 180 system_provide_libs.add_argument( 181 '--value', 182 required=True, 183 type=str, 184 help='Values of the libraries to append. If there are more than one ' 185 'it should be separated by empty space' 186 ) 187 system_provide_libs.add_argument( 188 '--system', required=True, type=str, help='Path of the system image.') 189 system_provide_libs.set_defaults(func=SystemProvide) 190 191 append = subparsers.add_parser( 192 'append', help='Append value(s) to given key.') 193 append.add_argument( 194 '-s', 195 '--source', 196 required=True, 197 type=str, 198 help='Source linker configuration file in protobuf.') 199 append.add_argument( 200 '-o', 201 '--output', 202 required=True, 203 type=str, 204 help='Target linker configuration file to write in protobuf.') 205 append.add_argument('--key', required=True, type=str, help='.') 206 append.add_argument( 207 '--value', 208 required=True, 209 type=str, 210 help='Values of the libraries to append. If there are more than one' 211 'it should be separated by empty space' 212 ) 213 append.set_defaults(func=Append) 214 215 append = subparsers.add_parser('merge', help='Merge configurations') 216 append.add_argument( 217 '-o', 218 '--out', 219 required=True, 220 type=str, 221 help='Output linker configuration file to write in protobuf.') 222 append.add_argument( 223 '-i', 224 '--input', 225 nargs='+', 226 type=str, 227 help='Linker configuration files to merge.') 228 append.set_defaults(func=Merge) 229 230 return parser 231 232 233def main(): 234 parser = GetArgParser() 235 args = parser.parse_args() 236 if 'func' in args: 237 args.func(args) 238 else: 239 parser.print_help() 240 241 242if __name__ == '__main__': 243 main() 244