• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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