conv_linker_config.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  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. import argparse
  18. import collections
  19. import json
  20. import os
  21. import sys
  22. import linker_config_pb2 #pylint: disable=import-error
  23. from google.protobuf.descriptor import FieldDescriptor
  24. from google.protobuf.json_format import ParseDict
  25. from google.protobuf.text_format import MessageToString
  26. def LoadJsonMessage(path):
  27. """
  28. Loads a message from a .json file with `//` comments strippedfor convenience.
  29. """
  30. json_content = ''
  31. with open(path) as f:
  32. for line in f:
  33. if not line.lstrip().startswith('//'):
  34. json_content += line
  35. obj = json.loads(json_content, object_pairs_hook=collections.OrderedDict)
  36. return ParseDict(obj, linker_config_pb2.LinkerConfig())
  37. def Proto(args):
  38. """
  39. Merges input json files (--source) into a protobuf message (--output).
  40. Fails if the output file exists. Set --force or --append to deal with the existing
  41. output file.
  42. --force to overwrite the output file with the input (.json files).
  43. --append to append the input to the output file.
  44. """
  45. pb = linker_config_pb2.LinkerConfig()
  46. if os.path.isfile(args.output):
  47. if args.force:
  48. pass
  49. elif args.append:
  50. with open(args.output, 'rb') as f:
  51. pb.ParseFromString(f.read())
  52. else:
  53. sys.stderr.write(f'Error: {args.output} exists. Use --force or --append.\n')
  54. sys.exit(1)
  55. if args.source:
  56. for input in args.source.split(':'):
  57. pb.MergeFrom(LoadJsonMessage(input))
  58. with open(args.output, 'wb') as f:
  59. f.write(pb.SerializeToString())
  60. def Print(args):
  61. with open(args.source, 'rb') as f:
  62. pb = linker_config_pb2.LinkerConfig()
  63. pb.ParseFromString(f.read())
  64. print(MessageToString(pb))
  65. def SystemProvide(args):
  66. pb = linker_config_pb2.LinkerConfig()
  67. with open(args.source, 'rb') as f:
  68. pb.ParseFromString(f.read())
  69. libraries = args.value.split()
  70. def IsInLibPath(lib_name):
  71. lib_path = os.path.join(args.system, 'lib', lib_name)
  72. lib64_path = os.path.join(args.system, 'lib64', lib_name)
  73. return os.path.exists(lib_path) or os.path.islink(
  74. lib_path) or os.path.exists(lib64_path) or os.path.islink(
  75. lib64_path)
  76. installed_libraries = [lib for lib in libraries if IsInLibPath(lib)]
  77. for item in installed_libraries:
  78. if item not in getattr(pb, 'provideLibs'):
  79. getattr(pb, 'provideLibs').append(item)
  80. with open(args.output, 'wb') as f:
  81. f.write(pb.SerializeToString())
  82. def Append(args):
  83. pb = linker_config_pb2.LinkerConfig()
  84. with open(args.source, 'rb') as f:
  85. pb.ParseFromString(f.read())
  86. if getattr(type(pb),
  87. args.key).DESCRIPTOR.label == FieldDescriptor.LABEL_REPEATED:
  88. for value in args.value.split():
  89. getattr(pb, args.key).append(value)
  90. else:
  91. setattr(pb, args.key, args.value)
  92. with open(args.output, 'wb') as f:
  93. f.write(pb.SerializeToString())
  94. def Merge(args):
  95. pb = linker_config_pb2.LinkerConfig()
  96. for other in args.input:
  97. with open(other, 'rb') as f:
  98. pb.MergeFromString(f.read())
  99. with open(args.out, 'wb') as f:
  100. f.write(pb.SerializeToString())
  101. def GetArgParser():
  102. parser = argparse.ArgumentParser()
  103. subparsers = parser.add_subparsers()
  104. parser_proto = subparsers.add_parser(
  105. 'proto',
  106. help='Convert the input JSON configuration file into protobuf.')
  107. parser_proto.add_argument(
  108. '-s',
  109. '--source',
  110. nargs='?',
  111. type=str,
  112. help='Colon-separated list of linker configuration files in JSON.')
  113. parser_proto.add_argument(
  114. '-o',
  115. '--output',
  116. required=True,
  117. type=str,
  118. help='Target path to create protobuf file.')
  119. option_for_existing_output = parser_proto.add_mutually_exclusive_group()
  120. option_for_existing_output.add_argument(
  121. '-f',
  122. '--force',
  123. action='store_true',
  124. help='Overwrite if the output file exists.')
  125. option_for_existing_output.add_argument(
  126. '-a',
  127. '--append',
  128. action='store_true',
  129. help='Append the input to the output file if the output file exists.')
  130. parser_proto.set_defaults(func=Proto)
  131. print_proto = subparsers.add_parser(
  132. 'print', help='Print configuration in human-readable text format.')
  133. print_proto.add_argument(
  134. '-s',
  135. '--source',
  136. required=True,
  137. type=str,
  138. help='Source linker configuration file in protobuf.')
  139. print_proto.set_defaults(func=Print)
  140. system_provide_libs = subparsers.add_parser(
  141. 'systemprovide',
  142. help='Append system provide libraries into the configuration.')
  143. system_provide_libs.add_argument(
  144. '-s',
  145. '--source',
  146. required=True,
  147. type=str,
  148. help='Source linker configuration file in protobuf.')
  149. system_provide_libs.add_argument(
  150. '-o',
  151. '--output',
  152. required=True,
  153. type=str,
  154. help='Target linker configuration file to write in protobuf.')
  155. system_provide_libs.add_argument(
  156. '--value',
  157. required=True,
  158. type=str,
  159. help='Values of the libraries to append. If there are more than one '
  160. 'it should be separated by empty space'
  161. )
  162. system_provide_libs.add_argument(
  163. '--system', required=True, type=str, help='Path of the system image.')
  164. system_provide_libs.set_defaults(func=SystemProvide)
  165. append = subparsers.add_parser(
  166. 'append', help='Append value(s) to given key.')
  167. append.add_argument(
  168. '-s',
  169. '--source',
  170. required=True,
  171. type=str,
  172. help='Source linker configuration file in protobuf.')
  173. append.add_argument(
  174. '-o',
  175. '--output',
  176. required=True,
  177. type=str,
  178. help='Target linker configuration file to write in protobuf.')
  179. append.add_argument('--key', required=True, type=str, help='.')
  180. append.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. append.set_defaults(func=Append)
  188. append = subparsers.add_parser('merge', help='Merge configurations')
  189. append.add_argument(
  190. '-o',
  191. '--out',
  192. required=True,
  193. type=str,
  194. help='Output linker configuration file to write in protobuf.')
  195. append.add_argument(
  196. '-i',
  197. '--input',
  198. nargs='+',
  199. type=str,
  200. help='Linker configuration files to merge.')
  201. append.set_defaults(func=Merge)
  202. return parser
  203. def main():
  204. parser = GetArgParser()
  205. args = parser.parse_args()
  206. if 'func' in args:
  207. args.func(args)
  208. else:
  209. parser.print_help()
  210. if __name__ == '__main__':
  211. main()