1 // Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
2 // Use of this source code is governed by an apache-2.0 license that can be
3 // found in the LICENSE file.
5 #include "hybrid/step/pkgmgr/step_generate_xml.h"
7 #include <boost/filesystem/operations.hpp>
8 #include <boost/filesystem/path.hpp>
9 #include <boost/system/error_code.hpp>
11 #include <libxml/parser.h>
12 #include <libxml/tree.h>
13 #include <libxml/xpath.h>
14 #include <pkgmgr_parser.h>
16 #include <common/utils/file_util.h>
17 #include <common/utils/glist_range.h>
22 #include "hybrid/hybrid_backend_data.h"
24 namespace ci = common_installer;
25 namespace bf = boost::filesystem;
26 namespace bs = boost::system;
30 const std::vector<std::string> kBlackListNodes = {
36 const std::vector<std::string> kNeedMergeNodes = {
39 const std::vector<std::pair<std::string, std::string>> kApplicationNodeNames = {
40 {"ui-application", "uiapp"},
41 {"service-application", "svcapp"},
42 {"widget-application", "widgetapp"},
43 {"watch-application", "watchapp"},
51 bool StepGenerateXml::LoadXmlDocument(const bf::path& wgt_xml_path,
52 const bf::path& tpk_xml_path) {
54 int keep_blanks = xmlKeepBlanksDefault(0);
55 wgt_doc_ = xmlParseFile(wgt_xml_path.string().c_str());
57 LOG(ERROR) << "Failed to parse file: " << wgt_xml_path;
58 xmlKeepBlanksDefault(keep_blanks);
61 tpk_doc_ = xmlParseFile(tpk_xml_path.string().c_str());
63 LOG(ERROR) << "Failed to parse file: " << tpk_xml_path;
64 xmlKeepBlanksDefault(keep_blanks);
67 xmlKeepBlanksDefault(keep_blanks);
71 xmlNodePtr StepGenerateXml::GetXmlNode(const xmlDocPtr doc,
72 const std::string& name, const std::string& attr,
73 const std::string& attr_val) {
74 xmlXPathContextPtr ctxt = xmlXPathNewContext(doc);
76 LOG(ERROR) << "Failed to create XPath context";
80 std::string expr = "//*[local-name()='" + name + "']";
81 if (!attr.empty() && !attr_val.empty())
82 expr.append("[@" + attr + "='" + attr_val + "']");
84 xmlXPathObjectPtr obj =
85 xmlXPathEvalExpression((const xmlChar*)expr.c_str(), ctxt);
86 if (!obj || !obj->nodesetval || !obj->nodesetval->nodeNr) {
87 xmlXPathFreeContext(ctxt);
91 xmlNodePtr node = obj->nodesetval->nodeTab[0];
92 xmlXPathFreeObject(obj);
93 xmlXPathFreeContext(ctxt);
98 void StepGenerateXml::MergeXmlNode(xmlNodePtr node1, xmlNodePtr node2) {
99 xmlNodePtr last = xmlGetLastChild(node1);
101 // merge node2's child into node1
102 for (xmlNodePtr cur = node2->children; cur; cur = next) {
104 if (std::find(kBlackListNodes.begin(), kBlackListNodes.end(),
105 reinterpret_cast<const char*>(cur->name)) != kBlackListNodes.end())
107 // to avoid duplicate
108 if (std::find(kNeedMergeNodes.begin(), kNeedMergeNodes.end(),
109 reinterpret_cast<const char*>(cur->name)) != kNeedMergeNodes.end())
111 xmlAddNextSibling(last, cur);
116 void StepGenerateXml::SetXmlNodeAttribute(xmlNodePtr node,
117 const std::string& attr, const std::string& attr_val) {
118 xmlSetProp(node, reinterpret_cast<const xmlChar*>(attr.c_str()),
119 reinterpret_cast<const xmlChar*>(attr_val.c_str()));
122 ci::Step::Status StepGenerateXml::process() {
123 bf::path wgt_xml_path = context_->xml_path.get();
124 bf::path tpk_xml_path = context_->pkg_path.get() / "tizen-manifest.xml";
126 if (!LoadXmlDocument(wgt_xml_path, tpk_xml_path))
127 return Step::Status::MANIFEST_ERROR;
129 for (auto& entry : kNeedMergeNodes) {
130 xmlNodePtr tpk_node = GetXmlNode(tpk_doc_, entry);
131 if (tpk_node == nullptr)
133 // TODO(s89.jang): If cannot find node from wgt doc, merge tpk node itself
134 // instead of merging its child nodes.
135 xmlNodePtr wgt_node = GetXmlNode(wgt_doc_, entry);
136 MergeXmlNode(wgt_node, tpk_node);
139 // set app's executable path
140 HybridBackendData* data =
141 static_cast<HybridBackendData*>(context_->backend_data.get());
142 manifest_x* tpk_data = data->tpk_manifest_data.get();
143 for (application_x* app :
144 GListRange<application_x*>(tpk_data->application)) {
145 auto r = std::find_if(kApplicationNodeNames.begin(),
146 kApplicationNodeNames.end(),
147 [app](const std::pair<std::string, std::string>& e) {
148 return strcmp(e.second.c_str(), app->component_type) == 0;
150 if (r == kApplicationNodeNames.end()) {
151 LOG(WARNING) << "Cannot find component type!";
154 xmlNodePtr node = GetXmlNode(wgt_doc_, (*r).first, "appid", app->appid);
155 SetXmlNodeAttribute(node, "exec", app->exec);
158 if (xmlSaveFormatFile(wgt_xml_path.string().c_str(), wgt_doc_, 1) == -1) {
159 LOG(ERROR) << "Cannot write xml file";
160 return Step::Status::MANIFEST_ERROR;
163 if (pkgmgr_parser_check_manifest_validation(
164 wgt_xml_path.string().c_str()) != 0) {
165 LOG(ERROR) << "Merged manifest is not valid";
166 return Step::Status::MANIFEST_ERROR;
172 ci::Step::Status StepGenerateXml::precheck() {
173 bf::path wgt_xml_path = context_->xml_path.get();
174 if (!bf::exists(wgt_xml_path)) {
175 LOG(ERROR) << "Converted config file not found: " << wgt_xml_path;
176 return Step::Status::MANIFEST_ERROR;
179 bf::path tpk_xml_path = context_->pkg_path.get() / "tizen-manifest.xml";
180 if (!bf::exists(tpk_xml_path)) {
181 LOG(ERROR) << "Native manifest file not found: " << tpk_xml_path;
182 return Step::Status::MANIFEST_ERROR;
188 } // namespace pkgmgr
189 } // namespace hybrid