Add update-info-builder.py, used to generate update-info file.
[platform/core/system/upgrade-tools.git] / mk_delta / common / bin / update-info-builder.py
1 #!/usr/bin/python3
2
3 '''
4
5 Copyright © 2022 Samsung Electronics Co., Ltd.. All rights reserved.
6
7 Permission is hereby granted, without written agreement and without license or royalty fees, to use, copy, modify, and distribute
8 this software and its documentation for any purpose, provided that the above copyright notice and the following two paragraphs appear
9 in all copies of this software.
10
11 IN NO EVENT SHALL SAMSUNG ELECTRONICS CO., LTD. BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
12 DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF SAMSUNG ELECTRONICS CO., LTD. HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
13
14 SAMSUNG ELECTRONICS CO., LTD. SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
15 FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND SAMSUNG ELECTRONICS CO., LTD. HAS NO OBLIGATION TO PROVIDE
16 MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
17
18 '''
19
20 '''
21 This script is used to generate an info file, which is to be attached to a delta of any type.
22 The file contains specific details about an image that a particular delta will upgrade.
23 Sample file (with currently supported image details)
24 <<<
25     model_name=rpi4
26     manufacturer=Tizen
27     device_type=IoT_Headless
28     tz_build_release_name=Tizen7/Unified
29     tz_build_arch=aarch64
30     tz_build_date=20220922_060719
31 >>>
32 '''
33
34
35 import xml.etree.ElementTree as ET
36 import sys
37 import os
38 import argparse
39
40
41 TIZEN_PREFIX = "tizen.org"
42 SYSTEM_PREFIX = os.path.join(TIZEN_PREFIX, "system")
43 BUILD_PREFIX = "TZ_BUILD_"
44
45
46 # following three could be simpler, but this way its easier in case we add more variables to check
47 def generate_hal_model_config_set():
48     hal_list = [
49         'model_name'
50     ]
51     return set(os.path.join(SYSTEM_PREFIX, elem) for elem in hal_list)
52
53
54 def generate_rootfs_model_config_set():
55     rootfs_list = [
56         'manufacturer',
57         'device_type'
58     ]
59     return set(os.path.join(SYSTEM_PREFIX, elem) for elem in rootfs_list)
60
61
62 def generate_tizen_build_set():
63     tizen_list = [
64         'RELEASE_NAME',
65         'ARCH',
66         'DATE'
67     ]
68     return set(BUILD_PREFIX + elem for elem in tizen_list)
69
70
71 def get_dict_from_text_file(path, set_info):
72     tmp_dict = {}
73     with open(path, 'r') as reader:
74         lines = (line.rstrip() for line in reader.readlines())
75         lines = (line for line in lines if line)
76         for line in lines:
77             var_name, var_value = line.split('=')
78             var_name.rstrip()
79             var_value.lstrip()
80             if var_name in set_info:
81                 set_info.remove(var_name)
82                 # for consistent formatting
83                 tmp_dict[var_name.lower()] = var_value
84                 if len(set_info) == 0:
85                     return tmp_dict
86
87     return None
88
89
90 def get_dict_from_xml_file(path, set_info):
91     tmp_dict = {}
92
93     xml_tree = ET.parse(path)
94     xml_root = xml_tree.getroot()
95
96     for elem in xml_root.findall('./platform/key'):
97         name = elem.get('name')
98         if name in set_info:
99             set_info.remove(name)
100             tmp_dict[name] = elem.text
101             if len(set_info) == 0:
102                 return tmp_dict
103
104     return None
105
106
107 def generate_main_dict(args):
108     main_dict = {}
109
110     hal_set = generate_hal_model_config_set()
111     rootfs_set = generate_rootfs_model_config_set()
112     tizen_set = generate_tizen_build_set()
113
114     hal_dict = get_dict_from_xml_file(args.hal_model_config_path, hal_set)
115     if not hal_dict:
116         print(f'{args.hal_model_config_path}: error parsing file', file=sys.stderr)
117         return None
118
119     rootfs_dict = get_dict_from_xml_file(args.rootfs_model_config_path, rootfs_set)
120     if not rootfs_dict:
121         print(f'{args.rootfs_model_config_path}: error parsing file', file=sys.stderr)
122         return None
123
124     tizen_dict = get_dict_from_text_file(args.tizen_build_config_path, tizen_set)
125     if not tizen_dict:
126         print(f'{args.tizen_build_config_path}: error parsing file', file=sys.stderr)
127         return None
128
129     main_dict.update(hal_dict)
130     main_dict.update(rootfs_dict)
131     main_dict.update(tizen_dict)
132
133     return main_dict
134
135
136 def create_parser():
137     parser = argparse.ArgumentParser(description='Generate a formated update-info.ini file used to verify whether \
138         delta archive used during update is compatible with our device. This file will be created using three files.')
139
140     parser.add_argument('hal_model_config_path', type=str, help='path to a file originally at path_to_hal/etc/config/model-config.xml')
141     parser.add_argument('rootfs_model_config_path', type=str, help='path to a file originally at path_to_rootfs/etc/config/model-config.xml')
142     parser.add_argument('tizen_build_config_path', type=str, help='path to a file originally at path_to_rootfs/etc/tizen-build.conf')
143     parser.add_argument('output_file', type=str, help='path to an output file')
144
145     return parser
146
147
148 def main():
149     parser = create_parser()
150     args = parser.parse_args()
151
152     for index, (arg_name, arg_value) in enumerate(vars(args).items(), start=1):
153         # we do not want output file to exist
154         if index == len(vars(args)):
155             if os.path.exists(arg_value):
156                 parser.error(f'{arg_name} = {arg_value} is a file that already exists!')
157             break
158         if not (os.path.exists(arg_value) and os.path.isfile(arg_value)):
159             parser.error(f'{arg_name} = {arg_value} is not a valid path!')
160
161     print('--- Generate update info file ---')
162     update_data = generate_main_dict(args)
163     if not update_data:
164         # TODO make this exception more verbose
165         raise Exception
166
167     with open(args.output_file, 'w') as writer:
168         for key, value in update_data.items():
169             _, key = os.path.split(key)
170             key = key.split()[-1]
171             value = value.replace('\"', '')
172             writer.write(f'{key}={value}\n')
173
174     print('--- Generating update info file successful ---')
175     return
176
177
178 if __name__ == '__main__':
179     main()