|
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 sys |
|
18 import logging |
|
19 import tempfile |
|
20 import os, re |
|
21 import shutil |
|
22 |
|
23 from optparse import OptionParser, OptionGroup |
|
24 import cone_common |
|
25 |
|
26 from cone.public import api, utils |
|
27 |
|
28 from conesub_merge import merge_config_root_to_config_root,\ |
|
29 get_active_root_if_necessary,\ |
|
30 MergePolicy, MergeFailedException |
|
31 |
|
32 from conesub_export import run_export |
|
33 |
|
34 VERSION = '1.0' |
|
35 |
|
36 logger = logging.getLogger('cone') |
|
37 |
|
38 class MetaNotFoundException(Exception): |
|
39 pass |
|
40 |
|
41 def find_variant_layers_to_merge(source_config, target_config, find_pattern): |
|
42 """ |
|
43 Find all layers in the configuration that contain custvariant* in |
|
44 their path name and return a list containing source->target mappings. |
|
45 @param source_config: The source configuration object. |
|
46 @param target_config: The target configuration object. |
|
47 @param new_name: The new name to replace custvariant* in the |
|
48 target path name with. |
|
49 @return: A list of (source_layer, target_layer) tuples. |
|
50 """ |
|
51 pattern = re.compile(find_pattern) |
|
52 |
|
53 result = [] |
|
54 for src in source_config.list_configurations(): |
|
55 m = pattern.match(src) |
|
56 if m: |
|
57 result.append(src) |
|
58 |
|
59 print "Found layers %r" % result |
|
60 return result |
|
61 |
|
62 def main(argv=sys.argv): |
|
63 """ Initialize a variant from a cpf. """ |
|
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, 'Initvariant options', |
|
75 'The initvariant action is intended for merging a variant CPF back into the ' |
|
76 'configuration project, or creating a new empty variant based on an existing ' |
|
77 'configuration. It merges all customer variant layers (layers with ' |
|
78 'custvariant* in their path name) and renames them based on the variant ID ' |
|
79 'and variant name ("custvariant_<id>_<name>").') |
|
80 |
|
81 group.add_option("-c", "--configuration", |
|
82 dest="configuration", |
|
83 help="Defines the name of the target configuration. By default the " |
|
84 "configuration file name is composed of product name, variant ID " |
|
85 "and variant name like this: <product>_custvariant_<id>_<name>_root.confml", |
|
86 metavar="CONFIG") |
|
87 |
|
88 group.add_option("-r", "--remote", |
|
89 dest="remote", |
|
90 help="Defines the location of remote storage (CPF)", |
|
91 metavar="STORAGE") |
|
92 |
|
93 group.add_option("-s", "--sourceconfiguration", |
|
94 dest="sourceconfiguration", |
|
95 help="Defines the name of the remote configuration inside the remote storage. " |
|
96 "Default is the active root of the remote project.", |
|
97 metavar="CONFIG") |
|
98 |
|
99 group.add_option("--variant-id", help="Variant ID, mandatory.") |
|
100 group.add_option("--variant-name", help="Variant name, optional.") |
|
101 group.add_option("--product-name", |
|
102 help="Product name, taken from the configuration data by default " |
|
103 "(i.e. defaults to '${imakerapi.productname}')", |
|
104 default="${imakerapi.productname}") |
|
105 |
|
106 group.add_option("--set-active-root", |
|
107 action="store_true", |
|
108 help="Set the newly created (or update) configuration root as the " |
|
109 "project's active root after the merge is done.") |
|
110 |
|
111 group.add_option("-b","--based-on-configuration", |
|
112 dest="boconfig", |
|
113 help="Defines the configuration root which is used as a base " |
|
114 "configuration for the new empty variant.") |
|
115 |
|
116 group.add_option("--find-layer-regexp", |
|
117 dest="find_pattern", |
|
118 default='.*/manual/.*|.*/configurator/.*', |
|
119 help="Defines the pattern which is used to find the layers " |
|
120 "from source configuration that will be merged" |
|
121 "Default: '.*/manual/.*|.*/configurator/.*' " ) |
|
122 |
|
123 parser.add_option_group(group) |
|
124 (options, _) = parser.parse_args(argv) |
|
125 |
|
126 cone_common.handle_common_options(options) |
|
127 |
|
128 # Check the passed options |
|
129 if not options.remote and not options.boconfig: |
|
130 parser.error("Remote project or based-on-configuration must be given") |
|
131 if options.remote and options.boconfig: |
|
132 parser.error("Only either remote project or based-on-configuration can be given, but not both") |
|
133 if not options.variant_id: parser.error("Variant ID must be given") |
|
134 |
|
135 temp_cpf_folder = None |
|
136 |
|
137 if options.boconfig: |
|
138 class ExportOptions(object): |
|
139 pass |
|
140 |
|
141 path = '' |
|
142 coreplat_name = '' |
|
143 product = '' |
|
144 |
|
145 project = api.Project(api.Storage.open(options.project,"a", username=options.username, password=options.password)) |
|
146 config = project.get_configuration(options.boconfig) |
|
147 meta = config.meta |
|
148 |
|
149 if meta: |
|
150 for prop in meta.array: |
|
151 if 'name' in prop.attrs and 'value' in prop.attrs: |
|
152 name = prop.attrs['name'] |
|
153 if name == 'coreplat_name': |
|
154 coreplat_name = prop.attrs['value'] |
|
155 if name == 'product_name': |
|
156 product = prop.attrs['value'] |
|
157 |
|
158 if not coreplat_name or not product: |
|
159 print >>sys.stderr, "Could not find coreplat_name or product_name from meta data." |
|
160 print >>sys.stderr, "Are you sure the given based-on-configuration is valid?" |
|
161 sys.exit(2) |
|
162 |
|
163 path = coreplat_name + '/' + product |
|
164 |
|
165 temp_cpf_folder = tempfile.mkdtemp() |
|
166 |
|
167 export_options = ExportOptions() |
|
168 export_options.project = options.project |
|
169 export_options.remote = os.path.join(temp_cpf_folder, 'temp.cpf') |
|
170 export_options.configs = [options.boconfig] |
|
171 export_options.username = options.username |
|
172 export_options.password = options.password |
|
173 export_options.config_wildcards = None |
|
174 export_options.config_regexes = None |
|
175 export_options.export_dir = None |
|
176 export_options.exclude_content_filter = None |
|
177 export_options.added = [path + '/customer/custvariant/manual/root.confml', |
|
178 path + '/customer/custvariant/configurator/root.confml'] |
|
179 export_options.exclude_empty_folders = False |
|
180 export_options.action = None |
|
181 |
|
182 options.remote = export_options.remote |
|
183 |
|
184 print "Exporting variant CPF to a temporary directory" |
|
185 run_export(export_options) |
|
186 |
|
187 target_project = api.Project(api.Storage.open(options.project,"a", username=options.username, password=options.password)) |
|
188 source_project = api.Project(api.Storage.open(options.remote,"r", username=options.username, password=options.password)) |
|
189 |
|
190 print "Target project: %s" % options.project |
|
191 print "Source project: %s" % options.remote |
|
192 replace_dict = {"VAR_ID": options.variant_id, "VAR_NAME": options.variant_name} |
|
193 |
|
194 try: |
|
195 # Open the source configuration |
|
196 source_config = get_active_root_if_necessary(source_project, options.sourceconfiguration, 'source') |
|
197 source_config = source_project.get_configuration(source_config) |
|
198 |
|
199 # Determine the new name of the layer part (replaces 'custvariant[^/]*') |
|
200 if options.variant_name: |
|
201 new_name = "custvariant_%s_%s" % (options.variant_id, options.variant_name) |
|
202 else: |
|
203 new_name = "custvariant_%s" % options.variant_id |
|
204 |
|
205 # Determine the target configuration |
|
206 if options.configuration: |
|
207 target_config = options.configuration |
|
208 else: |
|
209 # Target configuration not given explicitly, automatically |
|
210 # determine the name based on the product name and the new |
|
211 # layer name |
|
212 try: |
|
213 product_name = utils.expand_refs_by_default_view( |
|
214 options.product_name, |
|
215 source_config.get_default_view(), |
|
216 catch_not_found = False) |
|
217 except Exception, e: |
|
218 print "Could not determine product name: %s" % e |
|
219 sys.exit(2) |
|
220 print "Product name: %s" % product_name |
|
221 target_config = "%s_%s_root.confml" % (product_name, new_name) |
|
222 |
|
223 |
|
224 def find_layers(source_config, target_config): |
|
225 ret_list = [] |
|
226 |
|
227 layers = find_variant_layers_to_merge(source_config, |
|
228 target_config, |
|
229 options.find_pattern) |
|
230 p = re.compile(r'custvariant[^/]*') |
|
231 for layer in layers: |
|
232 tgt_layer = p.sub(new_name, layer) |
|
233 ret_list.append((layer, tgt_layer)) |
|
234 |
|
235 return ret_list |
|
236 |
|
237 # Perform the merge |
|
238 merge_config_root_to_config_root( |
|
239 source_project = source_project, |
|
240 target_project = target_project, |
|
241 source_config = options.sourceconfiguration, |
|
242 target_config = target_config, |
|
243 layer_finder_func = find_layers, |
|
244 merge_policy = MergePolicy.OVERWRITE_LAYER) |
|
245 |
|
246 if options.set_active_root: |
|
247 target_project.get_storage().set_active_configuration(target_config) |
|
248 except MergeFailedException, e: |
|
249 print "Could not initialize variant: %s" % e |
|
250 sys.exit(2) |
|
251 else: |
|
252 # Merge successful, so save the target project |
|
253 # to persist the changes |
|
254 target_project.save() |
|
255 |
|
256 target_project.close() |
|
257 source_project.close() |
|
258 |
|
259 if temp_cpf_folder: |
|
260 logger.debug("Removing temporary CPF directory '%s'" % temp_cpf_folder) |
|
261 shutil.rmtree(temp_cpf_folder) |
|
262 |
|
263 |
|
264 if __name__ == "__main__": |
|
265 main() |