blob: ed3fbb79f118e657496dec146abc777dadacf001 [file] [log] [blame]
Kiyoung Kim62abd122020-10-06 17:16:44 +09001#!/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
Kiyoung Kim24dfc1f2020-11-16 10:48:44 +090021import os
Jooyung Han3397b6a2023-03-08 05:44:17 +090022import sys
Kiyoung Kim62abd122020-10-06 17:16:44 +090023
Spandan Das1d4bfd82021-08-25 22:58:46 +000024import linker_config_pb2 #pylint: disable=import-error
Kiyoung Kim24dfc1f2020-11-16 10:48:44 +090025from google.protobuf.descriptor import FieldDescriptor
Kiyoung Kim62abd122020-10-06 17:16:44 +090026from google.protobuf.json_format import ParseDict
27from google.protobuf.text_format import MessageToString
28
29
Jooyung Hanf6fd4c22023-03-09 14:50:35 +090030def 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
Kiyoung Kim62abd122020-10-06 17:16:44 +090043def Proto(args):
Jooyung Han3397b6a2023-03-08 05:44:17 +090044 """
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 """
Jooyung Han014ccd42023-01-09 16:23:14 +090051 pb = linker_config_pb2.LinkerConfig()
Jooyung Han3397b6a2023-03-08 05:44:17 +090052 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
Jooyung Hanb531bee2023-03-04 08:28:40 +090062 if args.source:
63 for input in args.source.split(':'):
Jooyung Hanf6fd4c22023-03-09 14:50:35 +090064 pb.MergeFrom(LoadJsonMessage(input))
Kiyoung Kim3df5f502023-09-27 11:26:58 +090065
66 ValidateAndWriteAsPbFile(pb, args.output)
Kiyoung Kim62abd122020-10-06 17:16:44 +090067
68
69def Print(args):
Spandan Das1d4bfd82021-08-25 22:58:46 +000070 with open(args.source, 'rb') as f:
71 pb = linker_config_pb2.LinkerConfig()
72 pb.ParseFromString(f.read())
73 print(MessageToString(pb))
Kiyoung Kim62abd122020-10-06 17:16:44 +090074
75
Kiyoung Kim24dfc1f2020-11-16 10:48:44 +090076def SystemProvide(args):
Spandan Das1d4bfd82021-08-25 22:58:46 +000077 pb = linker_config_pb2.LinkerConfig()
78 with open(args.source, 'rb') as f:
79 pb.ParseFromString(f.read())
80 libraries = args.value.split()
Kiyoung Kim24dfc1f2020-11-16 10:48:44 +090081
Spandan Das1d4bfd82021-08-25 22:58:46 +000082 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)
Kiyoung Kim24dfc1f2020-11-16 10:48:44 +090088
Spandan Das1d4bfd82021-08-25 22:58:46 +000089 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)
Kiyoung Kim3df5f502023-09-27 11:26:58 +090093
94 ValidateAndWriteAsPbFile(pb, args.output)
Kiyoung Kim24dfc1f2020-11-16 10:48:44 +090095
96
Kiyoung Kim4ee686d2020-12-03 15:20:07 +090097def Append(args):
Spandan Das1d4bfd82021-08-25 22:58:46 +000098 pb = linker_config_pb2.LinkerConfig()
99 with open(args.source, 'rb') as f:
100 pb.ParseFromString(f.read())
Kiyoung Kim4ee686d2020-12-03 15:20:07 +0900101
Spandan Das1d4bfd82021-08-25 22:58:46 +0000102 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)
Kiyoung Kim4ee686d2020-12-03 15:20:07 +0900108
Kiyoung Kim3df5f502023-09-27 11:26:58 +0900109 ValidateAndWriteAsPbFile(pb, args.output)
110
Spandan Das1d4bfd82021-08-25 22:58:46 +0000111
Kiyoung Kim4ee686d2020-12-03 15:20:07 +0900112
Jooyung Hane134d092021-04-15 05:13:34 +0900113def Merge(args):
Spandan Das1d4bfd82021-08-25 22:58:46 +0000114 pb = linker_config_pb2.LinkerConfig()
115 for other in args.input:
116 with open(other, 'rb') as f:
117 pb.MergeFromString(f.read())
Jooyung Hane134d092021-04-15 05:13:34 +0900118
Kiyoung Kim3df5f502023-09-27 11:26:58 +0900119 ValidateAndWriteAsPbFile(pb, args.output)
Spandan Das1d4bfd82021-08-25 22:58:46 +0000120
Kiyoung Kim4ee686d2020-12-03 15:20:07 +0900121
Jooyung Han4bc10262023-09-08 11:51:45 +0900122def Validate(args):
123 if os.path.isdir(args.input):
124 config_file = os.path.join(args.input, 'etc/linker.config.pb')
125 if os.path.exists(config_file):
126 args.input = config_file
127 Validate(args)
128 # OK if there's no linker config file.
129 return
130
131 if not os.path.isfile(args.input):
132 sys.exit(f"{args.input} is not a file")
133
134 pb = linker_config_pb2.LinkerConfig()
135 with open(args.input, 'rb') as f:
136 pb.ParseFromString(f.read())
137
138 if args.type == 'apex':
139 # Shouldn't use provideLibs/requireLibs in APEX linker.config.pb
140 if getattr(pb, 'provideLibs'):
141 sys.exit(f'{args.input}: provideLibs is set. Use provideSharedLibs in apex_manifest')
142 if getattr(pb, 'requireLibs'):
143 sys.exit(f'{args.input}: requireLibs is set. Use requireSharedLibs in apex_manifest')
144 elif args.type == 'system':
145 if getattr(pb, 'visible'):
146 sys.exit(f'{args.input}: do not use visible, which is for APEX')
147 if getattr(pb, 'permittedPaths'):
148 sys.exit(f'{args.input}: do not use permittedPaths, which is for APEX')
149 else:
150 sys.exit(f'Unknown type: {args.type}')
151
Jooyung Haneb421ee2023-11-08 13:38:36 +0900152 # Reject contributions field at build time while keeping the runtime behavior for GRF.
153 if getattr(pb, 'contributions'):
154 sys.exit(f"{args.input}: 'contributions' is set. "
155 "It's deprecated. Instead, make the APEX 'visible' and use android_dlopen_ext().")
156
Jooyung Han4bc10262023-09-08 11:51:45 +0900157
Kiyoung Kim3df5f502023-09-27 11:26:58 +0900158def ValidateAndWriteAsPbFile(pb, output_path):
159 ValidateConfiguration(pb)
160 with open(output_path, 'wb') as f:
161 f.write(pb.SerializeToString())
162
163
164def ValidateConfiguration(pb):
165 """
166 Validate if the configuration is valid to be used as linker configuration
167 """
168
169 # Validate if provideLibs and requireLibs have common module
170 provideLibs = set(getattr(pb, 'provideLibs'))
171 requireLibs = set(getattr(pb, 'requireLibs'))
172
173 intersectLibs = provideLibs.intersection(requireLibs)
174
175 if intersectLibs:
176 for lib in intersectLibs:
177 print(f'{lib} exists both in requireLibs and provideLibs', file=sys.stderr)
178 sys.exit(1)
179
180
Kiyoung Kim62abd122020-10-06 17:16:44 +0900181def GetArgParser():
Spandan Das1d4bfd82021-08-25 22:58:46 +0000182 parser = argparse.ArgumentParser()
183 subparsers = parser.add_subparsers()
Kiyoung Kim62abd122020-10-06 17:16:44 +0900184
Spandan Das1d4bfd82021-08-25 22:58:46 +0000185 parser_proto = subparsers.add_parser(
186 'proto',
187 help='Convert the input JSON configuration file into protobuf.')
188 parser_proto.add_argument(
189 '-s',
190 '--source',
Jooyung Hanb531bee2023-03-04 08:28:40 +0900191 nargs='?',
Spandan Das1d4bfd82021-08-25 22:58:46 +0000192 type=str,
Jooyung Han014ccd42023-01-09 16:23:14 +0900193 help='Colon-separated list of linker configuration files in JSON.')
Spandan Das1d4bfd82021-08-25 22:58:46 +0000194 parser_proto.add_argument(
195 '-o',
196 '--output',
197 required=True,
198 type=str,
199 help='Target path to create protobuf file.')
Jooyung Han3397b6a2023-03-08 05:44:17 +0900200 option_for_existing_output = parser_proto.add_mutually_exclusive_group()
201 option_for_existing_output.add_argument(
202 '-f',
203 '--force',
204 action='store_true',
205 help='Overwrite if the output file exists.')
206 option_for_existing_output.add_argument(
207 '-a',
208 '--append',
209 action='store_true',
210 help='Append the input to the output file if the output file exists.')
Spandan Das1d4bfd82021-08-25 22:58:46 +0000211 parser_proto.set_defaults(func=Proto)
Kiyoung Kim62abd122020-10-06 17:16:44 +0900212
Spandan Das1d4bfd82021-08-25 22:58:46 +0000213 print_proto = subparsers.add_parser(
214 'print', help='Print configuration in human-readable text format.')
215 print_proto.add_argument(
216 '-s',
217 '--source',
218 required=True,
219 type=str,
220 help='Source linker configuration file in protobuf.')
221 print_proto.set_defaults(func=Print)
Kiyoung Kim62abd122020-10-06 17:16:44 +0900222
Spandan Das1d4bfd82021-08-25 22:58:46 +0000223 system_provide_libs = subparsers.add_parser(
224 'systemprovide',
225 help='Append system provide libraries into the configuration.')
226 system_provide_libs.add_argument(
227 '-s',
228 '--source',
229 required=True,
230 type=str,
231 help='Source linker configuration file in protobuf.')
232 system_provide_libs.add_argument(
233 '-o',
234 '--output',
235 required=True,
236 type=str,
237 help='Target linker configuration file to write in protobuf.')
238 system_provide_libs.add_argument(
239 '--value',
240 required=True,
241 type=str,
242 help='Values of the libraries to append. If there are more than one '
243 'it should be separated by empty space'
244 )
245 system_provide_libs.add_argument(
246 '--system', required=True, type=str, help='Path of the system image.')
247 system_provide_libs.set_defaults(func=SystemProvide)
Kiyoung Kim24dfc1f2020-11-16 10:48:44 +0900248
Spandan Das1d4bfd82021-08-25 22:58:46 +0000249 append = subparsers.add_parser(
250 'append', help='Append value(s) to given key.')
251 append.add_argument(
252 '-s',
253 '--source',
254 required=True,
255 type=str,
256 help='Source linker configuration file in protobuf.')
257 append.add_argument(
258 '-o',
259 '--output',
260 required=True,
261 type=str,
262 help='Target linker configuration file to write in protobuf.')
263 append.add_argument('--key', required=True, type=str, help='.')
264 append.add_argument(
265 '--value',
266 required=True,
267 type=str,
268 help='Values of the libraries to append. If there are more than one'
269 'it should be separated by empty space'
270 )
271 append.set_defaults(func=Append)
Kiyoung Kim4ee686d2020-12-03 15:20:07 +0900272
Spandan Das1d4bfd82021-08-25 22:58:46 +0000273 append = subparsers.add_parser('merge', help='Merge configurations')
274 append.add_argument(
275 '-o',
276 '--out',
277 required=True,
278 type=str,
279 help='Output linker configuration file to write in protobuf.')
280 append.add_argument(
281 '-i',
282 '--input',
283 nargs='+',
284 type=str,
285 help='Linker configuration files to merge.')
286 append.set_defaults(func=Merge)
Jooyung Hane134d092021-04-15 05:13:34 +0900287
Jooyung Han4bc10262023-09-08 11:51:45 +0900288 validate = subparsers.add_parser('validate', help='Validate configuration')
289 validate.add_argument(
290 '--type',
291 required=True,
292 choices=['apex', 'system'],
293 help='Type of linker configuration')
294 validate.add_argument(
295 'input',
296 help='Input can be a directory which has etc/linker.config.pb or a path'
297 ' to the linker config file')
298 validate.set_defaults(func=Validate)
299
Spandan Das1d4bfd82021-08-25 22:58:46 +0000300 return parser
Kiyoung Kim62abd122020-10-06 17:16:44 +0900301
302
303def main():
Jooyung Han3397b6a2023-03-08 05:44:17 +0900304 parser = GetArgParser()
305 args = parser.parse_args()
306 if 'func' in args:
307 args.func(args)
308 else:
309 parser.print_help()
Kiyoung Kim62abd122020-10-06 17:16:44 +0900310
311
312if __name__ == '__main__':
Spandan Das1d4bfd82021-08-25 22:58:46 +0000313 main()