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 fnmatch
|
|
19 |
import logging
|
|
20 |
from optparse import OptionParser, OptionGroup
|
|
21 |
import cone_common
|
|
22 |
from cone.public import api, plugin, utils, exceptions
|
|
23 |
|
|
24 |
|
|
25 |
VERSION = '1.0'
|
|
26 |
DEFAULT_EXT = '.cpf'
|
3
|
27 |
CONE_SCRIPT_PATTERN = 'conesub_*.py'
|
|
28 |
ROOT_PATH = os.path.abspath(os.path.dirname(__file__))
|
0
|
29 |
|
|
30 |
logger = logging.getLogger('cone')
|
|
31 |
DATA_NAME = 'confml/data.confml'
|
|
32 |
|
3
|
33 |
class ExportFailedException(Exception):
|
|
34 |
pass
|
|
35 |
|
0
|
36 |
def main():
|
3
|
37 |
""" Export configurations. """
|
0
|
38 |
parser = OptionParser(version="%%prog %s" % VERSION)
|
|
39 |
|
|
40 |
parser.add_options(cone_common.COMMON_OPTIONS)
|
|
41 |
|
|
42 |
parser.add_option("-c", "--configuration",
|
|
43 |
dest="configs",
|
|
44 |
action="append",
|
|
45 |
help="Defines the name of the configuration for the action, can be "\
|
|
46 |
"specified multiple times to include multiple configurations.",
|
|
47 |
metavar="CONFIG",
|
|
48 |
default=[])
|
|
49 |
|
|
50 |
parser.add_option("--config-wildcard",
|
|
51 |
action="append",
|
|
52 |
dest="config_wildcards",
|
|
53 |
help="Wildcard pattern for including configurations, e.g. "\
|
|
54 |
"product_langpack_*_root.confml",
|
|
55 |
metavar="WILDCARD",
|
|
56 |
default=[])
|
|
57 |
|
|
58 |
parser.add_option("--config-regex",
|
|
59 |
action="append",
|
|
60 |
dest="config_regexes",
|
|
61 |
help="Regular expression for including configurations, e.g. "\
|
|
62 |
"product_langpack_\\d{2}_root.confml",
|
|
63 |
metavar="REGEX",
|
|
64 |
default=[])
|
|
65 |
|
|
66 |
parser.add_option("-p", "--project",
|
|
67 |
dest="project",
|
|
68 |
help="defines the location of current project. Default is the "\
|
|
69 |
"current working directory.",\
|
|
70 |
default=".",
|
|
71 |
metavar="STORAGE")
|
|
72 |
|
|
73 |
group = OptionGroup(parser, 'Export options',
|
|
74 |
'The export action is intended for exporting configurations '\
|
|
75 |
'from one project (storage) to another. A project can be a '\
|
|
76 |
'folder, a CPF or ZIP file, or a Carbon web storage URL. '\
|
|
77 |
# An ugly way to make newlines, someone should look into
|
|
78 |
# sub-classing optparse.HelpFormatter...
|
|
79 |
' '\
|
|
80 |
'Two different ways of exporting are supported: '\
|
|
81 |
' '\
|
|
82 |
'1. Exporting multiple configurations into one new project using --remote '\
|
|
83 |
' '\
|
|
84 |
'2. Exporting configurations into a number of new projects using --export-dir')
|
|
85 |
|
|
86 |
group.add_option("-r", "--remote",
|
|
87 |
dest="remote",
|
|
88 |
help="Defines the location of remote storage. All configurations included using "\
|
|
89 |
"--configuration, --config-wildcard and --config-regex are exported into "\
|
|
90 |
"the storage. If the remote storage location is not given, the default "\
|
|
91 |
"location is determined based on the first included source configuration name. "\
|
|
92 |
"E.g. 'example.confml' would be exported into 'example.cpf'",
|
|
93 |
metavar="STORAGE")
|
|
94 |
|
|
95 |
group.add_option("--export-dir",
|
|
96 |
help="Defines the directory where each included configuration is exported "\
|
|
97 |
"as a new project.",
|
|
98 |
default=None)
|
|
99 |
|
|
100 |
group.add_option("--export-format",
|
|
101 |
help="Defines the format into which projects are exported when using "\
|
|
102 |
"--export-dir. Possible values are 'cpf' (the default) and 'dir'.",
|
|
103 |
default=None)
|
|
104 |
|
|
105 |
group.add_option("-a","--add",
|
|
106 |
dest="added",
|
|
107 |
action="append",
|
|
108 |
type="string",
|
|
109 |
help="Adds a configuration layer to the given configuration as last element. "\
|
|
110 |
"The add operation can be used several times in a single command and it "\
|
|
111 |
"can create even an empty layer. "\
|
|
112 |
"Example --add foo/root.confml --add bar/root-confml.",
|
|
113 |
metavar="CONFIG",
|
|
114 |
default=None)
|
|
115 |
|
3
|
116 |
group.add_option("--run-action",
|
|
117 |
dest="action",
|
|
118 |
action="append",
|
|
119 |
type="string",
|
|
120 |
help="Adds a execution of extra step that can manipulate the configuration before it is exported to external storage. "\
|
|
121 |
"The --run-action operation can be used several times in a single command and it "\
|
|
122 |
"will execute the actions in given order."\
|
|
123 |
"Example --run-action=fix, which would execute fix action during export.",
|
|
124 |
metavar="PLUGIN",
|
|
125 |
default=None)
|
|
126 |
|
0
|
127 |
group.add_option("--exclude-folders",
|
|
128 |
dest="exclude_empty_folders",
|
|
129 |
action="store_true",
|
|
130 |
help="Excludes empty folders from export",
|
|
131 |
default=False)
|
3
|
132 |
|
|
133 |
group.add_option("--exclude-content-filter",
|
|
134 |
dest="exclude_content_filter",
|
|
135 |
help="Filters out files and folders from content folder which matches with "\
|
|
136 |
"the given regular expression. Matching is case-insensitive. "\
|
|
137 |
"Example --exclude-content-filter=\".*\.sisx|.*\.sis|.*\.wgz|.*wgt\" "\
|
|
138 |
"Filters out all files with extension sis, sisx, wgz or wgt.",
|
|
139 |
default=None)
|
|
140 |
|
0
|
141 |
|
|
142 |
parser.add_option_group(group)
|
|
143 |
(options, args) = parser.parse_args()
|
|
144 |
|
|
145 |
cone_common.handle_common_options(options)
|
|
146 |
|
|
147 |
# Check options
|
|
148 |
if options.export_format and options.export_dir is None:
|
|
149 |
parser.error("--export-format can only be used in conjunction with --export-dir")
|
|
150 |
if options.export_dir and options.remote:
|
|
151 |
parser.error("--export-dir and --remote cannot be used at the same time")
|
|
152 |
if options.export_format and options.export_format.lower() not in ('dir', 'cpf'):
|
|
153 |
parser.error("Invalid export format '%s'" % options.export_format)
|
|
154 |
if options.export_dir and not (options.configs or options.config_wildcards or options.config_regexes):
|
|
155 |
parser.error("Use of --export-dir requires at least one configuration to be specified")
|
|
156 |
if options.export_dir and os.path.isfile(options.export_dir):
|
|
157 |
parser.error("Given export directory '%s' is a file")
|
|
158 |
|
3
|
159 |
try:
|
|
160 |
run_export(options)
|
|
161 |
except ExportFailedException, e:
|
|
162 |
parser.error(str(e))
|
|
163 |
|
|
164 |
|
|
165 |
|
|
166 |
def run_export(options):
|
0
|
167 |
# Open the project and find out the active configuration
|
3
|
168 |
storage = api.Storage.open(options.project, "r", username=options.username, password=options.password)
|
|
169 |
project = api.Project(storage)
|
0
|
170 |
try:
|
|
171 |
active_root = project.get_storage().get_active_configuration()
|
|
172 |
except AttributeError:
|
|
173 |
active_root = None
|
|
174 |
|
|
175 |
# Collect the list of configurations specified from the command line
|
|
176 |
config_list = []
|
|
177 |
if options.configs or options.config_wildcards or options.config_regexes:
|
|
178 |
try:
|
|
179 |
config_list = cone_common.get_config_list_from_project(
|
|
180 |
project = project,
|
|
181 |
configs = options.configs,
|
|
182 |
config_wildcards = options.config_wildcards,
|
|
183 |
config_regexes = options.config_regexes)
|
|
184 |
except cone_common.ConfigurationNotFoundError, e:
|
3
|
185 |
raise ExportFailedException(str(e))
|
0
|
186 |
|
|
187 |
# Use the active configuration if no configurations are specifically given
|
|
188 |
if len(config_list) == 0:
|
|
189 |
if active_root is None:
|
3
|
190 |
raise ExportFailedException("No configurations given and the project does not have an active root")
|
0
|
191 |
else:
|
|
192 |
logger.info('No configurations given! Using active root configuration %s' % active_root)
|
|
193 |
config_list = [active_root]
|
|
194 |
|
|
195 |
# Perform the export
|
|
196 |
if options.export_dir:
|
|
197 |
_export_to_dir(project = project,
|
|
198 |
export_dir = options.export_dir,
|
|
199 |
export_format = options.export_format or 'cpf',
|
|
200 |
configs = config_list,
|
|
201 |
added_layers = options.added,
|
3
|
202 |
empty_folders = not options.exclude_empty_folders,
|
|
203 |
exclude_filters = {"content": options.exclude_content_filter},
|
|
204 |
options = options)
|
0
|
205 |
else:
|
|
206 |
_export_to_storage(project = project,
|
|
207 |
remote_project_location = options.remote,
|
|
208 |
configs = config_list,
|
|
209 |
added_layers = options.added,
|
3
|
210 |
empty_folders = not options.exclude_empty_folders,
|
|
211 |
exclude_filters = {"content": options.exclude_content_filter},
|
|
212 |
options = options)
|
0
|
213 |
|
|
214 |
|
3
|
215 |
def _export_to_storage(project, remote_project_location, configs, added_layers, empty_folders, exclude_filters, options):
|
0
|
216 |
assert len(configs) > 0
|
|
217 |
|
|
218 |
# If the remote storage is not given, determine it automatically based
|
|
219 |
# on the first specified configuration name
|
|
220 |
if not remote_project_location:
|
|
221 |
remotename, ext = os.path.splitext(os.path.basename(configs[0]))
|
|
222 |
remotename += DEFAULT_EXT
|
|
223 |
logger.info('No remote storage given! Using source configuration name %s' % remotename)
|
|
224 |
remote_project_location = remotename
|
|
225 |
|
3
|
226 |
remote_project = api.Project(api.Storage.open(remote_project_location, "w", username=options.username, password=options.password))
|
0
|
227 |
for config_path in configs:
|
|
228 |
config = project.get_configuration(config_path)
|
3
|
229 |
# immediate solution to run fixes during export
|
|
230 |
if options.action:
|
|
231 |
from cone.action import loader
|
|
232 |
for action_name in options.action:
|
|
233 |
try:
|
|
234 |
action_class = loader.get_class(action_name)
|
|
235 |
action = action_class(configuration=config,
|
|
236 |
project=project)
|
|
237 |
action.run()
|
|
238 |
except Exception,e:
|
|
239 |
logger.warning('Running plugin %s threw an exception: %s' % (action_name, e))
|
0
|
240 |
project.export_configuration(config,
|
|
241 |
remote_project.storage,
|
3
|
242 |
empty_folders = empty_folders,
|
|
243 |
exclude_filters = exclude_filters)
|
0
|
244 |
print "Export %s to %s done!" % (config_path, remote_project_location)
|
|
245 |
|
|
246 |
# Setting first as active configuration if there are more than one configuration defined.
|
|
247 |
configs = remote_project.list_configurations()
|
|
248 |
if len(configs):
|
|
249 |
try:
|
|
250 |
remote_project.get_storage().set_active_configuration(configs[0])
|
|
251 |
except AttributeError:
|
|
252 |
pass
|
|
253 |
|
|
254 |
remote_project.save()
|
|
255 |
remote_project.close()
|
|
256 |
|
3
|
257 |
_add_layers(project, remote_project_location, added_layers, empty_folders, exclude_filters, options)
|
0
|
258 |
|
3
|
259 |
def _add_layers(source_project, remote_project_location, added_configs, empty_folders, exclude_filters, options):
|
0
|
260 |
"""
|
|
261 |
Add new configuration layers from source_project into
|
|
262 |
"""
|
|
263 |
if not added_configs:
|
|
264 |
return
|
|
265 |
|
|
266 |
target_project = api.Project(api.Storage.open(remote_project_location, "a"))
|
|
267 |
for target_config_name in target_project.list_configurations():
|
|
268 |
target_config = target_project.get_configuration(target_config_name)
|
|
269 |
|
|
270 |
for added_config_name in added_configs:
|
|
271 |
# Add layers only once
|
|
272 |
if not target_project.storage.is_resource(added_config_name):
|
|
273 |
logger.info('Adding configuration %s' % added_config_name)
|
|
274 |
|
|
275 |
if source_project.storage.is_resource(added_config_name):
|
|
276 |
# The configuration exists in the source project, export it from there
|
|
277 |
existing_config = source_project.get_configuration(added_config_name)
|
|
278 |
source_project.export_configuration(existing_config,
|
|
279 |
target_project.storage,
|
3
|
280 |
empty_folders = empty_folders,
|
|
281 |
exclude_filters = exclude_filters)
|
0
|
282 |
else:
|
|
283 |
# The given configuration does not exist in the source project,
|
|
284 |
# create a new empty layer
|
|
285 |
logger.info("Creating new layer %s." % added_config_name)
|
|
286 |
new_config = target_project.create_configuration(added_config_name)
|
|
287 |
new_config.create_configuration(DATA_NAME)
|
|
288 |
|
|
289 |
# Include the added configuration in the configuration root
|
|
290 |
target_config.include_configuration(utils.resourceref.norm(added_config_name))
|
|
291 |
|
|
292 |
target_project.save()
|
|
293 |
target_project.close()
|
|
294 |
|
3
|
295 |
def _export_to_dir(project, export_dir, export_format, configs, added_layers, empty_folders, exclude_filters, options):
|
0
|
296 |
if not os.path.exists(export_dir):
|
|
297 |
os.makedirs(export_dir)
|
|
298 |
|
|
299 |
for config in configs:
|
|
300 |
remote_name, _ = os.path.splitext(os.path.basename(config))
|
|
301 |
if export_format.lower() == 'cpf':
|
|
302 |
remote_name += '.cpf'
|
|
303 |
elif export_format.lower() == 'dir':
|
|
304 |
remote_name += '/'
|
|
305 |
|
|
306 |
remote_name = os.path.join(export_dir, remote_name)
|
3
|
307 |
_export_to_storage(project, remote_name, [config], added_layers, empty_folders, exclude_filters, options)
|
0
|
308 |
|
|
309 |
if __name__ == "__main__":
|
|
310 |
main()
|