Upstream version 8.36.161.0
[platform/framework/web/crosswalk.git] / src / third_party / chromite / scripts / loman.py
1 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """This module allows adding and deleting of projects to the local manifest."""
6
7 import logging
8 import platform
9 import optparse
10 import os
11 import sys
12 import xml.etree.ElementTree as ElementTree
13 from chromite.lib import cros_build_lib
14 from chromite.lib import git
15
16
17 class Manifest(object):
18   """Class which provides an abstraction for manipulating the local manifest."""
19
20   @classmethod
21   def FromPath(cls, path, empty_if_missing=False):
22     if os.path.isfile(path):
23       with open(path) as f:
24         return cls(f.read())
25     elif empty_if_missing:
26       cros_build_lib.Die('Manifest file, %r, not found' % path)
27     return cls()
28
29   def __init__(self, text=None):
30     self._text = text or '<manifest>\n</manifest>'
31     self.nodes = ElementTree.fromstring(self._text)
32
33   def AddNonWorkonProject(self, name, path, remote=None, revision=None):
34     """Add a new nonworkon project element to the manifest tree."""
35     element = ElementTree.Element('project', name=name, path=path,
36                                   remote=remote)
37     element.attrib['workon'] = 'False'
38     if revision is not None:
39       element.attrib['revision'] = revision
40     self.nodes.append(element)
41     return element
42
43   def GetProject(self, name, path=None):
44     """Accessor method for getting a project node from the manifest tree.
45
46     Returns:
47       project element node from ElementTree, otherwise, None
48     """
49     if path is None:
50       # Use a unique value that can't ever match.
51       path = object()
52     for project in self.nodes.findall('project'):
53       if project.attrib['name'] == name or project.attrib['path'] == path:
54         return project
55     return None
56
57   def ToString(self):
58     # Reset the tail for each node, then just do a hacky replace.
59     project = None
60     for project in self.nodes.findall('project'):
61       project.tail = '\n  '
62     if project is not None:
63       # Tweak the last project to not have the trailing space.
64       project.tail = '\n'
65     # Fix manifest tag text and tail.
66     self.nodes.text = '\n  '
67     self.nodes.tail = '\n'
68     return ElementTree.tostring(self.nodes)
69
70   def GetProjects(self):
71     return list(self.nodes.findall('project'))
72
73
74 def _AddProjectsToManifestGroups(options, *args):
75   """Enable the given manifest groups for the configured repository."""
76
77   groups_to_enable = ['name:%s' % x for x in args]
78
79   git_config = options.git_config
80
81   cmd = ['config', '-f', git_config, '--get', 'manifest.groups']
82   enabled_groups = git.RunGit('.', cmd, error_code_ok=True).output.split(',')
83
84   # Note that ordering actually matters, thus why the following code
85   # is written this way.
86   # Per repo behaviour, enforce an appropriate platform group if
87   # we're converting from a default manifest group to a limited one.
88   # Finally, note we reprocess the existing groups; this is to allow
89   # us to cleanup any user screwups, or our own screwups.
90   requested_groups = (
91       ['minilayout', 'platform-%s' % (platform.system().lower(),)] +
92       enabled_groups + list(groups_to_enable))
93
94   processed_groups = set()
95   finalized_groups = []
96
97   for group in requested_groups:
98     if group not in processed_groups:
99       finalized_groups.append(group)
100       processed_groups.add(group)
101
102   cmd = ['config', '-f', git_config, 'manifest.groups',
103          ','.join(finalized_groups)]
104   git.RunGit('.', cmd)
105
106
107 def _UpgradeMinilayout(options):
108   """Convert a repo checkout away from minilayout.xml to default.xml."""
109
110   full_tree = Manifest.FromPath(options.default_manifest_path)
111   local_manifest_exists = os.path.exists(options.local_manifest_path)
112
113   new_groups = []
114   if local_manifest_exists:
115     local_tree = Manifest.FromPath(options.local_manifest_path)
116     # Identify which projects need to be transferred across.
117     projects = local_tree.GetProjects()
118     new_groups = [x.attrib['name'] for x in projects]
119     allowed = set(x.attrib['name'] for x in full_tree.GetProjects())
120     transferred = [x for x in projects if x.attrib['name'] in allowed]
121     for project in transferred:
122       # Mangle local_manifest object, removing those projects;
123       # note we'll still be adding those projects to the default groups,
124       # including those that didn't intersect the main manifest.
125       local_tree.nodes.remove(project)
126
127   _AddProjectsToManifestGroups(options, *new_groups)
128
129   if local_manifest_exists:
130     # Rewrite the local_manifest now; if there is no settings left in
131     # the local_manifest, wipe it.
132     if local_tree.nodes.getchildren():
133       with open(options.local_manifest_path, 'w') as f:
134         f.write(local_tree.ToString())
135     else:
136       os.unlink(options.local_manifest_path)
137
138   # Finally, move the symlink.
139   os.unlink(options.manifest_sym_path)
140   os.symlink('manifests/default.xml', options.manifest_sym_path)
141   logging.info("Converted the checkout to manifest groups based minilayout.")
142
143
144 def main(argv):
145   parser = optparse.OptionParser(usage='usage: %prog add [options] <name> '
146                                        '<--workon | <path> --remote <remote> >')
147   parser.add_option('-w', '--workon', action='store_true', dest='workon',
148                     default=False, help='Is this a workon package?')
149   parser.add_option('-r', '--remote', dest='remote',
150                     default=None)
151   parser.add_option('-v', '--revision', dest='revision',
152                     default=None,
153                     help="Use to override the manifest defined default "
154                     "revision used for a given project.")
155   parser.add_option('--upgrade-minilayout', default=False, action='store_true',
156                     help="Upgrade a minilayout checkout into a full.xml "
157                     "checkout utilizing manifest groups.")
158   (options, args) = parser.parse_args(argv)
159
160   repo_dir = git.FindRepoDir(os.getcwd())
161   if not repo_dir:
162     parser.error("This script must be invoked from within a repository "
163                  "checkout.")
164
165   options.git_config = os.path.join(repo_dir, 'manifests.git', 'config')
166   options.repo_dir = repo_dir
167   options.local_manifest_path = os.path.join(repo_dir, 'local_manifest.xml')
168   # This constant is used only when we're doing an upgrade away from
169   # minilayout.xml to default.xml.
170   options.default_manifest_path = os.path.join(repo_dir, 'manifests',
171                                                'default.xml')
172   options.manifest_sym_path = os.path.join(repo_dir, 'manifest.xml')
173
174   active_manifest = os.path.basename(os.readlink(options.manifest_sym_path))
175   upgrade_required = active_manifest == 'minilayout.xml'
176
177   if options.upgrade_minilayout:
178     if args:
179       parser.error("--upgrade-minilayout takes no arguments.")
180     if not upgrade_required:
181       print "This repository checkout isn't using minilayout.xml; nothing to do"
182     else:
183       _UpgradeMinilayout(options)
184     return 0
185   elif upgrade_required:
186     logging.warn(
187         "Your repository checkout is using the old minilayout.xml workflow; "
188         "auto-upgrading it.")
189     cros_build_lib.RunCommand(
190         [sys.argv[0], '--upgrade-minilayout'], cwd=os.getcwd(),
191          print_cmd=False)
192
193   if not args:
194     parser.error("No command specified.")
195   elif args[0] != 'add':
196     parser.error("Only supported subcommand is add right now.")
197   elif options.workon:
198     if len(args) != 2:
199       parser.error(
200           "Argument count is wrong for --workon; must be add <project>")
201     name, path = args[1], None
202   else:
203     if options.remote is None:
204       parser.error('Adding non-workon projects requires a remote.')
205     elif len(args) != 3:
206       parser.error(
207           "Argument count is wrong for non-workon mode; "
208           "must be add <project> <path> --remote <remote-arg>")
209     name, path = args[1:]
210
211   revision = options.revision
212   if revision is not None:
213     if (not git.IsRefsTags(revision) and
214         not git.IsSHA1(revision)):
215       revision = git.StripRefsHeads(revision, False)
216
217   main_manifest = Manifest.FromPath(options.manifest_sym_path,
218                                     empty_if_missing=False)
219   local_manifest = Manifest.FromPath(options.local_manifest_path)
220
221   main_element = main_manifest.GetProject(name, path=path)
222
223   if options.workon:
224     if main_element is None:
225       parser.error('No project named %r in the default manifest.' % name)
226     _AddProjectsToManifestGroups(options, main_element.attrib['name'])
227
228   elif main_element is not None:
229     if options.remote is not None:
230       # Likely this project wasn't meant to be remote, so workon main element
231       print "Project already exists in manifest. Using that as workon project."
232       _AddProjectsToManifestGroups(options, main_element.attrib['name'])
233     else:
234       # Conflict will occur; complain.
235       parser.error("Requested project name=%r path=%r will conflict with "
236                    "your current manifest %s" % (name, path, active_manifest))
237
238   elif local_manifest.GetProject(name, path=path) is not None:
239     parser.error("Requested project name=%r path=%r conflicts with "
240                  "your local_manifest.xml" % (name, path))
241
242   else:
243     element = local_manifest.AddNonWorkonProject(name=name, path=path,
244                                                  remote=options.remote,
245                                                  revision=revision)
246     _AddProjectsToManifestGroups(options, element.attrib['name'])
247
248     with open(options.local_manifest_path, 'w') as f:
249       f.write(local_manifest.ToString())
250   return 0