isu_pkgs_maker: Tailor tizen-manifest.xml according to DA requirements 50/310250/9
authorAdam Michalski <a.michalski2@partner.samsung.com>
Wed, 24 Apr 2024 11:13:15 +0000 (13:13 +0200)
committerAdam Michalski <a.michalski2@partner.samsung.com>
Mon, 29 Apr 2024 13:18:58 +0000 (15:18 +0200)
DA requires the change of tizen_manifest.xml as following:

- add the `profile_name` section
- set `api-version` value to the Tizen version
- set `exec` value to the executable command file name (e.g. amd for /usr/bin/amd binary)
- set 'appid' value to 'org.tizen.isu.[pkg_name]' (e.g. 'org.tizen.isu.amd')

Change-Id: Ib6a2ca85c066114c36cfd2e8ef9a66d98016594c

src/pkg_maker/isu_pkgs_maker.py
src/pkg_maker/isu_pkgs_maker_py2.py

index e43f880..174f139 100755 (executable)
@@ -19,6 +19,7 @@ from pathlib import Path
 from tempfile import TemporaryDirectory
 from typing import Any, Callable, Dict, List, Tuple, Union
 import sh
+from collections import OrderedDict
 
 """
  MIT License
@@ -83,6 +84,7 @@ class Config:
         self.files = []
         self.system_services: List[Path] = []
         self.user_services: List[Path] = []
+        self.exec_name = ""
 
 class CustomXMLSEC(object):
     TEMPLATE_HEAD="""
@@ -126,7 +128,9 @@ class CustomXMLSEC(object):
 """
 
     MANIFEST_TEMPLATE="""<?xml version="1.0" encoding="utf-8" standalone="no"?>
-<manifest xmlns="http://tizen.org/ns/packages" api-version="1.0.0" package="org.tizen.isu.{}" res-type="tizen.isu.resource.{}" res-version="{}" version="1.0.0">
+<manifest xmlns="http://tizen.org/ns/packages" api-version="{}" package="org.tizen.isu.{}" res-type="tizen.isu.resource.{}" res-version="{}" version="{}">
+    <profile name="{}"/>
+    <service-application appid="org.tizen.isu.{}" auto-restart="true" exec="{}" multiple="false" on-boot="true" taskmanage="false" type="capp"/>
     <label>{}</label>
     <isu>
         <name>{}</name>
@@ -168,13 +172,15 @@ class CustomXMLSEC(object):
         file_content = self.FILE_TEMPLATE.format(file_path, sha256)
         file.write(file_content)
 
-    def _make_tizen_manifest(self):
+    def _make_tizen_manifest(self, profile_name, supplementary_config_data):
         config_file = CustomConfigParser()
         config_file.read_config("isu.cfg")
         name = config_file.sections['isu']['name']
         version = config_file.sections['isu']['version']
+        api_version = supplementary_config_data['tz_build_version']
+        exec_name = supplementary_config_data['exec_name']
 
-        content = self.MANIFEST_TEMPLATE.format(name, name, version, name, name, version)
+        content = self.MANIFEST_TEMPLATE.format(api_version, name, name, version, api_version, profile_name, name, exec_name, name, name, version)
 
         with open("tizen-manifest.xml", "w") as file:
             file.write(content)
@@ -207,8 +213,8 @@ class CustomXMLSEC(object):
 
         self.sign_signature(sig_file_name, key, password)
 
-    def run(self, out_dir, name):
-        self._make_tizen_manifest()
+    def run(self, out_dir, profile_name, name, supplementary_config_data):
+        self._make_tizen_manifest(profile_name, supplementary_config_data)
         self._gen_signature(True, self.key_author, self.password_author)
         self._gen_signature(False, self.key_distributor, self.password_distributor)
         logger.debug("Maker RPK archive")
@@ -235,7 +241,7 @@ class CustomConfigParser(object):
                     section_name = line[1:-1]
                     if section_name in self.sections:
                         raise Exception("Section {} already exist".format(section_name))
-                    self.sections[section_name] = {}
+                    self.sections[section_name] = OrderedDict()
                     current_section = section_name
                 else:
                     splitted = line.split("=")
@@ -388,10 +394,11 @@ class FileMapper:
 
 
 class ISUPkgsMakerCtx:
-    def __init__(self, images_dir: Path, use_images: bool, tmp_dir: Path, out_dir: Path, key: Union[Path, None], file_mapper: FileMapper):
+    def __init__(self, images_dir: Path, use_images: bool, tmp_dir: Path, profile_name: str, out_dir: Path, key: Union[Path, None], file_mapper: FileMapper):
         self.images_dir = images_dir
         self.use_images = use_images
         self.tmp_dir = tmp_dir
+        self.profile_name = profile_name
         self.out_dir = out_dir
         self.key = key
         self.file_mapper = file_mapper
@@ -577,6 +584,7 @@ class ISUSinglePkgMaker:
     ISU_CFG_D_DIR = "isu.cfg.d"
 
     def __init__(self, isudir_path: Path, cfg: Config, mounts: Dict[str, Union[Mounter, NoMounter]], isu_pkgs_ctx: ISUPkgsMakerCtx):
+        self._supplementary_config_data = None
         self._ctx = ISUSinglePkgMakerCtx(isudir_path, cfg, mounts, isu_pkgs_ctx)
 
         if os.path.exists(self._ctx.work_dir):
@@ -661,7 +669,7 @@ class ISUSinglePkgMaker:
 
     def _rpk_pkg(self, rpk_info):
         with CustomXMLSEC(rpk_info, self._ctx.pkg_dir) as rpk_maker:
-            return rpk_maker.run(self._ctx.out_dir, self._ctx.cfg.name)
+            return rpk_maker.run(self._ctx.out_dir, self._ctx.profile_name, self._ctx.cfg.name, self._supplementary_config_data)
 
     def _make_isu_cfg(self):
         tb_path = self._ctx.file_mapper.find_path(Path('/etc/tizen-build.conf'), self._ctx.mounts)
@@ -684,6 +692,15 @@ class ISUSinglePkgMaker:
         for file in self._ctx.cfg.files:
             config_file['files'][file] = None
 
+        supplementary_config_data = dict()
+        supplementary_config_data['tz_build_version'] = tizen_build.get_value('TZ_BUILD_FULLVER')
+        # we assume that the main binary file is the first file specified in the [files] section
+        # of the isu.cfg config file
+        section_files = self._ctx.cfg.files
+        exec_name = section_files[0] if len(section_files) > 0 else ""
+        supplementary_config_data['exec_name'] = os.path.basename(exec_name)
+        self._supplementary_config_data = supplementary_config_data
+
         with open(self._ctx.pkg_dir / self.ISU_CFG_FILE, 'w') as out_file:
             config_file.write(out_file)
 
@@ -720,8 +737,8 @@ class ISUSinglePkgMaker:
 
 
 class ISUPkgsMaker:
-    def __init__(self, images_dir: Path, use_images: bool, tmp_dir: Path, out_dir: Path, key: Union[Path, None], file_mapper: FileMapper):
-        self._ctx = ISUPkgsMakerCtx(images_dir, use_images, tmp_dir, out_dir, key, file_mapper)
+    def __init__(self, images_dir: Path, use_images: bool, tmp_dir: Path, profile_name: str, out_dir: Path, key: Union[Path, None], file_mapper: FileMapper):
+        self._ctx = ISUPkgsMakerCtx(images_dir, use_images, tmp_dir, profile_name, out_dir, key, file_mapper)
 
     def __enter__(self):
         return self
@@ -736,26 +753,26 @@ class ISUPkgsMaker:
         return []
 
     def load_config(self, cfg_file: Path):
-        config_file = configparser.ConfigParser(allow_no_value=True)
-        config_file.read(cfg_file)
+        config_file = CustomConfigParser()
+        config_file.read_config(cfg_file)
 
         cfg = Config()
 
         for section in ["isu", "files"]:
-            if section not in config_file:
+            if section not in config_file.sections:
                 raise InvalidConfigException(f"No \"{section}\" section in the config file.")
 
         for key in ["name", "version"]:
-            if key not in config_file["isu"]:
+            if key not in config_file.sections["isu"]:
                 raise InvalidConfigException(f"No \"{key}\" key in the \"isu\" section.")
 
-        cfg.name = config_file['isu']['name']
+        cfg.name = config_file.sections['isu']['name']
 
-        system_service = config_file['isu'].get('system_service')
+        system_service = config_file.sections['isu'].get('system_service')
         cfg.system_services = list(map(lambda x: Path(x), self._split(system_service)))
-        user_service = config_file['isu'].get('user_service')
+        user_service = config_file.sections['isu'].get('user_service')
         cfg.user_services = list(map(lambda x: Path(x), self._split(user_service)))
-        cfg.files = [f_name for f_name in config_file['files']]
+        cfg.files = [f_name for f_name in config_file.sections['files']]
 
         logger.info(f"Config {cfg_file} ({cfg.name}) loaded.")
 
@@ -858,6 +875,9 @@ def main() -> int:
                         help="File with image<->path mapping")
     parser.add_argument('--key', '-k', type=str, required=False,
                         help="Signing key")
+    parser.add_argument('--profile-name', type=str, required=False,
+                        default=os.getenv('PROFILE_NAME'),
+                        help="Profile name")
     parser.add_argument('--exit_on_any_error', '-e', action='store_true',
                         required=False, help="Terminate after any error")
 
@@ -895,6 +915,7 @@ def main() -> int:
             with ISUPkgsMaker(Path(os.path.realpath(args.src)),
                               args.use_images,
                               Path(tmp_dir),
+                              args.profile_name,
                               Path(os.path.realpath(args.out)),
                               Path(args.key) if args.key else None,
                               map) as isu_pkgs_maker:
index f792943..6324576 100755 (executable)
@@ -16,6 +16,7 @@ from hashlib import sha256
 import tempfile
 import base64
 import urllib
+from collections import OrderedDict
 
 """
  MIT License
@@ -80,6 +81,7 @@ class Config:
         self.files = []
         self.system_services = []
         self.user_services = []
+        self.exec_name = ""
 
 class CustomExitStack:
     def __init__(self):
@@ -142,7 +144,9 @@ class CustomXMLSEC(object):
 """
 
     MANIFEST_TEMPLATE="""<?xml version="1.0" encoding="utf-8" standalone="no"?>
-<manifest xmlns="http://tizen.org/ns/packages" api-version="1.0.0" package="org.tizen.isu.{}" res-type="tizen.isu.resource.{}" res-version="{}" version="1.0.0">
+<manifest xmlns="http://tizen.org/ns/packages" api-version="{}" package="org.tizen.isu.{}" res-type="tizen.isu.resource.{}" res-version="{}" version="{}">
+    <profile name="{}"/>
+    <service-application appid="org.tizen.isu.{}" auto-restart="true" exec="{}" multiple="false" on-boot="true" taskmanage="false" type="capp"/>
     <label>{}</label>
     <isu>
         <name>{}</name>
@@ -182,13 +186,15 @@ class CustomXMLSEC(object):
         file_content = self.FILE_TEMPLATE.format(file_path, sha256)
         file.write(file_content)
 
-    def _make_tizen_manifest(self):
+    def _make_tizen_manifest(self, profile_name, supplementary_config_data):
         config_file = CustomConfigParser()
         config_file.read_config("isu.cfg")
         name = config_file.sections['isu']['name']
         version = config_file.sections['isu']['version']
+        api_version = supplementary_config_data['tz_build_version']
+        exec_name = supplementary_config_data['exec_name']
 
-        content = self.MANIFEST_TEMPLATE.format(name, name, version, name, name, version)
+        content = self.MANIFEST_TEMPLATE.format(api_version, name, name, version, api_version, profile_name, name, exec_name, name, name, version)
 
         with open("tizen-manifest.xml", "w") as file:
             file.write(content)
@@ -221,8 +227,8 @@ class CustomXMLSEC(object):
 
         self.sign_signature(sig_file_name, key, password)
 
-    def run(self, out_dir, name):
-        self._make_tizen_manifest()
+    def run(self, out_dir, profile_name, name, supplementary_config_data):
+        self._make_tizen_manifest(profile_name, supplementary_config_data)
         self._gen_signature(True, self.key_author, self.password_author)
         self._gen_signature(False, self.key_distributor, self.password_distributor)
         logger.debug("Maker RPK archive")
@@ -248,7 +254,7 @@ class CustomConfigParser(object):
                     section_name = line[1:-1]
                     if section_name in self.sections:
                         raise Exception("Section {} already exist".format(section_name))
-                    self.sections[section_name] = {}
+                    self.sections[section_name] = OrderedDict()
                     current_section = section_name
                 else:
                     splitted = line.split("=")
@@ -402,10 +408,11 @@ class FileMapper:
 
 
 class ISUPkgsMakerCtx(object):
-    def __init__(self, images_dir, use_images, tmp_dir, out_dir, key, file_mapper):
+    def __init__(self, images_dir, use_images, tmp_dir, profile_name, out_dir, key, file_mapper):
         self.images_dir = images_dir
         self.use_images = use_images
         self.tmp_dir = tmp_dir
+        self.profile_name = profile_name
         self.out_dir = out_dir
         self.key = key
         self.file_mapper = file_mapper
@@ -598,6 +605,7 @@ class ISUSinglePkgMaker:
     ISU_CFG_D_DIR = "isu.cfg.d"
 
     def __init__(self, isudir_path, cfg, mounts, isu_pkgs_ctx):
+        self._supplementary_config_data = None
         self._ctx = ISUSinglePkgMakerCtx(isudir_path, cfg, mounts, isu_pkgs_ctx)
 
         if os.path.exists(str(self._ctx.work_dir)):
@@ -684,7 +692,7 @@ class ISUSinglePkgMaker:
 
     def _rpk_pkg(self, rpk_info):
         with CustomXMLSEC(rpk_info, self._ctx.pkg_dir) as rpk_maker:
-            return rpk_maker.run(self._ctx.out_dir, self._ctx.cfg.name)
+            return rpk_maker.run(self._ctx.out_dir, self._ctx.profile_name, self._ctx.cfg.name, self._supplementary_config_data)
 
 
     def _make_isu_cfg(self):
@@ -709,6 +717,15 @@ class ISUSinglePkgMaker:
         for file in self._ctx.cfg.files:
             config_file.sections['files'][file] = None
 
+        supplementary_config_data = dict()
+        supplementary_config_data['tz_build_version'] = tizen_build.get_value('TZ_BUILD_FULLVER')
+        # we assume that the main binary file is the first file specified in the [files] section
+        # of the isu.cfg config file
+        section_files = self._ctx.cfg.files
+        exec_name = section_files[0] if len(section_files) > 0 else ""
+        supplementary_config_data['exec_name'] = os.path.basename(exec_name)
+        self._supplementary_config_data = supplementary_config_data
+
         out_file = os.path.join(self._ctx.pkg_dir, self.ISU_CFG_FILE)
         config_file.write_config(out_file)
 
@@ -741,8 +758,8 @@ class ISUSinglePkgMaker:
 
 
 class ISUPkgsMaker:
-    def __init__(self, images_dir, use_images, tmp_dir, out_dir, key, file_mapper):
-        self._ctx = ISUPkgsMakerCtx(images_dir, use_images, tmp_dir, out_dir, key, file_mapper)
+    def __init__(self, images_dir, use_images, tmp_dir, profile_name, out_dir, key, file_mapper):
+        self._ctx = ISUPkgsMakerCtx(images_dir, use_images, tmp_dir, profile_name, out_dir, key, file_mapper)
 
     def __enter__(self):
         return self
@@ -776,7 +793,7 @@ class ISUPkgsMaker:
         cfg.system_services = list(map(lambda x: x, self._split(system_service)))
         user_service = config_file.sections['isu'].get('user_service')
         cfg.user_services = list(map(lambda x: x, self._split(user_service)))
-        cfg.files = [f_name for f_name in config_file.sections['files'].keys()]
+        cfg.files = [f_name for f_name in config_file.sections['files']]
 
         logger.info("Config {} ({}) loaded.".format(cfg_file, cfg.name))
 
@@ -879,6 +896,9 @@ def main():
                         help="File with image<->path mapping")
     parser.add_argument('--key', '-k', type=str, required=False,
                         help="Signing key")
+    parser.add_argument('--profile-name', type=str, required=False,
+                        default=os.getenv('PROFILE_NAME'),
+                        help="Profile name")
     parser.add_argument('--exit_on_any_error', '-e', action='store_true',
                         required=False, help="Terminate after any error")
 
@@ -916,6 +936,7 @@ def main():
         with ISUPkgsMaker(os.path.realpath(args.src),
                           args.use_images,
                           tmp_dir,
+                          args.profile_name,
                           os.path.realpath(args.out),
                           args.key,
                           map) as isu_pkgs_maker: