|
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() |