0
|
1 |
#
|
|
2 |
# Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
|
3 |
# All rights reserved.
|
|
4 |
# This component and the accompanying materials are made available
|
|
5 |
# under the terms of "Eclipse Public License v1.0"
|
|
6 |
# which accompanies this distribution, and is available
|
|
7 |
# at the URL "http://www.eclipse.org/legal/epl-v10.html".
|
|
8 |
#
|
|
9 |
# Initial Contributors:
|
|
10 |
# Nokia Corporation - initial contribution.
|
|
11 |
#
|
|
12 |
# Contributors:
|
|
13 |
#
|
|
14 |
# Description:
|
|
15 |
#
|
|
16 |
|
|
17 |
import os
|
|
18 |
import sys
|
|
19 |
import logging
|
|
20 |
from optparse import OptionParser, OptionGroup
|
|
21 |
import codecs
|
|
22 |
|
|
23 |
import cone_common
|
|
24 |
import time
|
|
25 |
|
|
26 |
from cone.public import api, plugin, utils, exceptions
|
|
27 |
from time import gmtime, strftime
|
|
28 |
import report_util
|
|
29 |
ROOT_PATH = os.path.dirname(os.path.abspath(__file__))
|
|
30 |
|
|
31 |
VERSION = '1.0'
|
|
32 |
|
|
33 |
log = logging.getLogger('cone')
|
|
34 |
|
|
35 |
REPORT_SHORTCUTS = {
|
|
36 |
'api': report_util.ReportShortcut(
|
|
37 |
os.path.join(ROOT_PATH, 'compare_api_report_template.html'),
|
|
38 |
'api_comparison.html',
|
|
39 |
'Report changes in feature definitions'),
|
|
40 |
|
|
41 |
'data': report_util.ReportShortcut(
|
|
42 |
os.path.join(ROOT_PATH, 'compare_data_report_template.html'),
|
|
43 |
'data_comparison.html',
|
|
44 |
'Report changes in data values'),
|
|
45 |
|
|
46 |
'crml_dc': report_util.ReportShortcut(
|
|
47 |
os.path.join(ROOT_PATH, 'crml_dc_report_template.html'),
|
|
48 |
'crml_dc_report.html',
|
|
49 |
'Report CRML data compatibility issues'),
|
|
50 |
|
|
51 |
'crml_dc_csv': report_util.ReportShortcut(
|
|
52 |
os.path.join(ROOT_PATH, 'crml_dc_report_template.csv'),
|
|
53 |
'crml_dc_report.csv',
|
|
54 |
'Report CRML data compatibility issues (CSV format)'),
|
|
55 |
}
|
|
56 |
DEFAULT_SHORTCUT = 'data'
|
|
57 |
|
|
58 |
def main():
|
|
59 |
shortcut_container = report_util.ReportShortcutContainer(REPORT_SHORTCUTS,
|
|
60 |
DEFAULT_SHORTCUT)
|
|
61 |
|
|
62 |
gset = cone_common.get_settings([os.path.join(ROOT_PATH,'conesub_compare.cfg')])
|
|
63 |
|
|
64 |
parser = OptionParser(version="%%prog %s" % VERSION)
|
|
65 |
|
|
66 |
parser.add_options(cone_common.COMMON_OPTIONS)
|
|
67 |
|
|
68 |
parser.add_option("-p", "--project",\
|
|
69 |
dest="project",\
|
|
70 |
help="defines the location of current project. Default is the current working directory.",\
|
|
71 |
default=".",\
|
|
72 |
metavar="STORAGE")
|
|
73 |
|
|
74 |
group = OptionGroup(parser, 'Compare options',
|
|
75 |
'The generate function will create target files from a specific configuration.'\
|
|
76 |
'The generate will always work with read-only mode of the project, so no changes are saved to project')
|
|
77 |
|
|
78 |
group.add_option("-s", "--sourceconfiguration",\
|
|
79 |
dest="sourceconfiguration",\
|
|
80 |
help="defines the name of the sourceconfiguration for the compare action. "\
|
|
81 |
"The configuration is expected to be located current storage.",\
|
|
82 |
metavar="CONFIG")
|
|
83 |
|
|
84 |
group.add_option("-t", "--targetconfiguration",\
|
|
85 |
dest="targetconfiguration",\
|
|
86 |
help="defines the name of the target configuration for the compare action. "\
|
|
87 |
"The configuration can be located in the current storage or it the configuration"\
|
|
88 |
"definition can contain a path to a storage. The storage definition is given as a path"\
|
|
89 |
"before semicolon. e.g. x:\data\configproject;productx.confml, test.cpf;root.confml",\
|
|
90 |
metavar="CONFIG")
|
|
91 |
|
|
92 |
# group.add_option("--compare-dict",\
|
|
93 |
# dest="compare_dict",\
|
|
94 |
# action="store",
|
|
95 |
# type="string",
|
|
96 |
# help="Compare elements as a dictionary",
|
|
97 |
# metavar="DICT",\
|
|
98 |
# default=None)
|
|
99 |
|
|
100 |
group.add_option("--report",\
|
|
101 |
dest="report_file",\
|
|
102 |
action="store",
|
|
103 |
type="string",
|
|
104 |
help="The file where the comparison report is written."\
|
|
105 |
"By default this value is determined by the used "\
|
|
106 |
"report type. Example: --report report.html.",
|
|
107 |
metavar="FILE",\
|
|
108 |
default=None)
|
|
109 |
|
|
110 |
group.add_option("--template",\
|
|
111 |
dest="template",\
|
|
112 |
action="store",
|
|
113 |
type="string",
|
|
114 |
help="Template used in a report generation. By default "\
|
|
115 |
"this value is determined by the used report type. "\
|
|
116 |
"Example: --template report_template.html.",
|
|
117 |
metavar="FILE",\
|
|
118 |
default=None)
|
|
119 |
|
|
120 |
group.add_option("--report-type",
|
|
121 |
dest="report_type",
|
|
122 |
action="store",
|
|
123 |
type="string",
|
|
124 |
help="The type of the report to generate. This is a convenience "\
|
|
125 |
"switch for setting the used template. "\
|
|
126 |
"Possible values:\n "\
|
|
127 |
+ shortcut_container.get_shortcut_help_text(),
|
|
128 |
metavar="TYPE",\
|
|
129 |
default=None)
|
|
130 |
|
|
131 |
group.add_option("--impl-filter",\
|
|
132 |
dest="impl_filter",\
|
|
133 |
action="store",
|
|
134 |
type="string",
|
|
135 |
help="The pattern used for filtering implementations for the "\
|
|
136 |
"comparison. See the switch --impl in action generate for "\
|
|
137 |
"more info. ",
|
|
138 |
metavar="PATTERN",\
|
|
139 |
default=None)
|
|
140 |
|
|
141 |
start_time = time.time()
|
|
142 |
|
|
143 |
parser.add_option_group(group)
|
|
144 |
(options, args) = parser.parse_args()
|
|
145 |
|
|
146 |
cone_common.handle_common_options(options, settings=gset)
|
|
147 |
|
|
148 |
if not options.sourceconfiguration: parser.error("sourceconfiguration must be given")
|
|
149 |
if not options.targetconfiguration: parser.error("targetconfiguration must be given")
|
|
150 |
if options.report_type and options.template:
|
|
151 |
parser.error("both --report-type and --template supplied; use only one of them")
|
|
152 |
if not shortcut_container.is_valid_shortcut(options.report_type):
|
|
153 |
parser.error("Invalid report type: %s" % options.report_type)
|
|
154 |
|
|
155 |
template_file, report_file = shortcut_container.determine_template_and_report(
|
|
156 |
options.report_type,
|
|
157 |
options.template,
|
|
158 |
options.report_file,
|
|
159 |
'comparison')
|
|
160 |
|
|
161 |
action = CompareAction(options.project,
|
|
162 |
options.sourceconfiguration,
|
|
163 |
options.targetconfiguration,
|
|
164 |
template = template_file,
|
|
165 |
report_file = report_file,
|
|
166 |
impl_filter = options.impl_filter)
|
|
167 |
result = action.run_action()
|
|
168 |
|
|
169 |
resmap = {True: 0, False: 1}
|
|
170 |
sys.exit(resmap[result])
|
|
171 |
|
|
172 |
|
|
173 |
|
|
174 |
class CompareAction(object):
|
|
175 |
|
|
176 |
def __init__(self, current, sourceconfig, targetconfig, **kwargs):
|
|
177 |
self.current = current
|
|
178 |
self.sourceconfig = sourceconfig
|
|
179 |
self.targetconfig = targetconfig
|
|
180 |
self.reportfile = kwargs.get("report_file", 'compare.html')
|
|
181 |
self.reporttemplate = kwargs.get('template', '')
|
|
182 |
self.columns = kwargs.get('columns', None)
|
|
183 |
self.impl_filter = kwargs.get('impl_filter',)
|
|
184 |
|
|
185 |
def run_action(self):
|
|
186 |
"""
|
|
187 |
Run the action.
|
|
188 |
@return: True if successful, False if not.
|
|
189 |
"""
|
|
190 |
|
|
191 |
currentprj = api.Project(api.Storage.open(self.current,"r"))
|
|
192 |
targetprj = None
|
|
193 |
(targetproject,targetconf) = self.parse_target_configuration(self.targetconfig)
|
|
194 |
print "Compare %s <> %s in %s" % (self.sourceconfig, targetconf, targetproject)
|
|
195 |
if targetproject != '':
|
|
196 |
targetprj = api.Project(api.Storage.open(targetproject,"r"))
|
|
197 |
else:
|
|
198 |
# The comparison doesn't seem to work if the same project
|
|
199 |
# object is used
|
|
200 |
#targetprj = currentprj
|
|
201 |
targetprj = api.Project(api.Storage.open(self.current,"r"))
|
|
202 |
|
|
203 |
source = currentprj.get_configuration(self.sourceconfig)
|
|
204 |
target = targetprj.get_configuration(targetconf)
|
|
205 |
|
|
206 |
print "Writing report to %s" % self.reportfile
|
|
207 |
sourcedata = {}
|
|
208 |
targetdata = {}
|
|
209 |
sourcedata['name'] = self.sourceconfig
|
|
210 |
targetdata['name'] = self.targetconfig
|
|
211 |
sourcedata['features'] = self.get_feature_api_data(source)
|
|
212 |
targetdata['features'] = self.get_feature_api_data(target)
|
|
213 |
|
|
214 |
impl_comp_data_proxy = ImplComparisonDataProxy(source,
|
|
215 |
target,
|
|
216 |
self.impl_filter)
|
|
217 |
|
|
218 |
template_data = {'sourcedata': sourcedata,
|
|
219 |
'targetdata': targetdata,
|
|
220 |
'impl_data': impl_comp_data_proxy}
|
|
221 |
|
|
222 |
result = report_util.generate_report(self.reporttemplate,
|
|
223 |
self.reportfile,
|
|
224 |
{'data': template_data})
|
|
225 |
print "Done."
|
|
226 |
return result
|
|
227 |
|
|
228 |
def parse_target_configuration(self,configpath):
|
|
229 |
"""
|
|
230 |
return tuple (storage, configpath) from storagepath;root.confml.
|
|
231 |
returns ('', 'root.confml') from root.confml
|
|
232 |
"""
|
|
233 |
elems = configpath.rsplit(';',2)
|
|
234 |
if len(elems) > 1:
|
|
235 |
return (elems[0],elems[1])
|
|
236 |
else:
|
|
237 |
return ('',elems[0])
|
|
238 |
|
|
239 |
def get_feature_api_data(self,config):
|
|
240 |
# Traverse through all features in the api
|
|
241 |
# and construct the data rows
|
|
242 |
data = {}
|
|
243 |
for elem in config.get_default_view().get_features('**'):
|
|
244 |
data[elem.fqr] = elem._obj
|
|
245 |
return data
|
|
246 |
|
|
247 |
class ImplComparisonDataProxy(object):
|
|
248 |
"""
|
|
249 |
Proxy object for loading implementation comparison data on demand.
|
|
250 |
"""
|
|
251 |
def __init__(self, sourceconfig, targetconfig, impl_filter):
|
|
252 |
self.sourceconfig = sourceconfig
|
|
253 |
self.targetconfig = targetconfig
|
|
254 |
if impl_filter is None: self.impl_filter = '.*'
|
|
255 |
else: self.impl_filter = impl_filter
|
|
256 |
|
|
257 |
self._flat_data = None
|
|
258 |
|
|
259 |
@property
|
|
260 |
def flat(self):
|
|
261 |
try:
|
|
262 |
if self._flat_data is None:
|
|
263 |
self._flat_data = self._get_flat_comparison_data()
|
|
264 |
return self._flat_data
|
|
265 |
except Exception, e:
|
|
266 |
utils.log_exception(log, 'Error retrieving ImplComparisonDataProxy.flat!')
|
|
267 |
raise
|
|
268 |
|
|
269 |
def _get_flat_comparison_data(self):
|
|
270 |
log.debug("Loading implementations for comparison (impl filter = '%s')..." % (self.impl_filter))
|
|
271 |
|
|
272 |
try:
|
|
273 |
source_impls = plugin.get_impl_set(self.sourceconfig, self.impl_filter)
|
|
274 |
target_impls = plugin.get_impl_set(self.targetconfig, self.impl_filter)
|
|
275 |
except Exception, e:
|
|
276 |
utils.log_exception(log, 'Failed to load implementations!')
|
|
277 |
raise
|
|
278 |
|
|
279 |
log.debug("%d impl(s) in source." % len(source_impls))
|
|
280 |
log.debug("%d impl(s) in target." % len(target_impls))
|
|
281 |
|
|
282 |
log.debug("Generating flat comparison results...")
|
|
283 |
result = source_impls.flat_compare(target_impls)
|
|
284 |
log.debug("Generated %d result row(s)" % len(result))
|
|
285 |
return result
|
|
286 |
|
|
287 |
if __name__ == "__main__":
|
|
288 |
main()
|