3
|
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 |
|
5
|
163 |
path = coreplat_name + '/' + product
|
|
164 |
|
|
165 |
# the new way (product)
|
|
166 |
if (os.path.exists(os.path.join(options.project, product, "root.confml"))):
|
|
167 |
path = product
|
|
168 |
# the old way (coreplat/product)
|
|
169 |
elif (os.path.exists(os.path.join(options.project, coreplat_name, product, "root.confml"))):
|
|
170 |
path = coreplat_name + '/' + product
|
|
171 |
# any other way, product root somewhere else (?/?/product/root.confml)
|
|
172 |
else:
|
|
173 |
for root, dirs, files in os.walk(os.path.abspath(options.project)):
|
|
174 |
if os.path.exists(os.path.join(root, product, "root.confml")):
|
|
175 |
fullpath = os.path.abspath(os.path.join(root, product, "root.confml"))
|
|
176 |
m = re.search(r'%s[\\/](.*)[\\/]root.confml' % re.escape(os.path.abspath(options.project)), fullpath)
|
|
177 |
if m:
|
|
178 |
path = m.group(1)
|
|
179 |
path = re.sub(r'\\','/', path)
|
|
180 |
break
|
|
181 |
|
3
|
182 |
temp_cpf_folder = tempfile.mkdtemp()
|
|
183 |
|
|
184 |
export_options = ExportOptions()
|
|
185 |
export_options.project = options.project
|
|
186 |
export_options.remote = os.path.join(temp_cpf_folder, 'temp.cpf')
|
|
187 |
export_options.configs = [options.boconfig]
|
|
188 |
export_options.username = options.username
|
|
189 |
export_options.password = options.password
|
|
190 |
export_options.config_wildcards = None
|
|
191 |
export_options.config_regexes = None
|
|
192 |
export_options.export_dir = None
|
|
193 |
export_options.exclude_content_filter = None
|
5
|
194 |
export_options.include_content_filter = None
|
3
|
195 |
export_options.added = [path + '/customer/custvariant/manual/root.confml',
|
|
196 |
path + '/customer/custvariant/configurator/root.confml']
|
|
197 |
export_options.exclude_empty_folders = False
|
|
198 |
export_options.action = None
|
|
199 |
|
|
200 |
options.remote = export_options.remote
|
|
201 |
|
|
202 |
print "Exporting variant CPF to a temporary directory"
|
|
203 |
run_export(export_options)
|
|
204 |
|
|
205 |
target_project = api.Project(api.Storage.open(options.project,"a", username=options.username, password=options.password))
|
|
206 |
source_project = api.Project(api.Storage.open(options.remote,"r", username=options.username, password=options.password))
|
|
207 |
|
|
208 |
print "Target project: %s" % options.project
|
|
209 |
print "Source project: %s" % options.remote
|
|
210 |
replace_dict = {"VAR_ID": options.variant_id, "VAR_NAME": options.variant_name}
|
|
211 |
|
|
212 |
try:
|
|
213 |
# Open the source configuration
|
|
214 |
source_config = get_active_root_if_necessary(source_project, options.sourceconfiguration, 'source')
|
|
215 |
source_config = source_project.get_configuration(source_config)
|
|
216 |
|
|
217 |
# Determine the new name of the layer part (replaces 'custvariant[^/]*')
|
|
218 |
if options.variant_name:
|
|
219 |
new_name = "custvariant_%s_%s" % (options.variant_id, options.variant_name)
|
|
220 |
else:
|
|
221 |
new_name = "custvariant_%s" % options.variant_id
|
|
222 |
|
|
223 |
# Determine the target configuration
|
|
224 |
if options.configuration:
|
|
225 |
target_config = options.configuration
|
|
226 |
else:
|
|
227 |
# Target configuration not given explicitly, automatically
|
|
228 |
# determine the name based on the product name and the new
|
|
229 |
# layer name
|
|
230 |
try:
|
|
231 |
product_name = utils.expand_refs_by_default_view(
|
|
232 |
options.product_name,
|
|
233 |
source_config.get_default_view(),
|
|
234 |
catch_not_found = False)
|
|
235 |
except Exception, e:
|
|
236 |
print "Could not determine product name: %s" % e
|
|
237 |
sys.exit(2)
|
|
238 |
print "Product name: %s" % product_name
|
|
239 |
target_config = "%s_%s_root.confml" % (product_name, new_name)
|
|
240 |
|
|
241 |
|
|
242 |
def find_layers(source_config, target_config):
|
|
243 |
ret_list = []
|
|
244 |
|
|
245 |
layers = find_variant_layers_to_merge(source_config,
|
|
246 |
target_config,
|
|
247 |
options.find_pattern)
|
|
248 |
p = re.compile(r'custvariant[^/]*')
|
|
249 |
for layer in layers:
|
|
250 |
tgt_layer = p.sub(new_name, layer)
|
|
251 |
ret_list.append((layer, tgt_layer))
|
|
252 |
|
|
253 |
return ret_list
|
|
254 |
|
|
255 |
# Perform the merge
|
|
256 |
merge_config_root_to_config_root(
|
|
257 |
source_project = source_project,
|
|
258 |
target_project = target_project,
|
|
259 |
source_config = options.sourceconfiguration,
|
|
260 |
target_config = target_config,
|
|
261 |
layer_finder_func = find_layers,
|
5
|
262 |
merge_policy = MergePolicy.OVERWRITE_LAYER,
|
|
263 |
find_pattern = options.find_pattern)
|
3
|
264 |
|
|
265 |
if options.set_active_root:
|
|
266 |
target_project.get_storage().set_active_configuration(target_config)
|
|
267 |
except MergeFailedException, e:
|
|
268 |
print "Could not initialize variant: %s" % e
|
|
269 |
sys.exit(2)
|
|
270 |
else:
|
|
271 |
# Merge successful, so save the target project
|
|
272 |
# to persist the changes
|
|
273 |
target_project.save()
|
|
274 |
|
|
275 |
target_project.close()
|
|
276 |
source_project.close()
|
|
277 |
|
|
278 |
if temp_cpf_folder:
|
|
279 |
logger.debug("Removing temporary CPF directory '%s'" % temp_cpf_folder)
|
|
280 |
shutil.rmtree(temp_cpf_folder)
|
|
281 |
|
|
282 |
|
|
283 |
if __name__ == "__main__":
|
|
284 |
main()
|