diff --git a/bin/loman b/bin/loman new file mode 120000 index 0000000000..d0f8b11e30 --- /dev/null +++ b/bin/loman @@ -0,0 +1 @@ +loman.py \ No newline at end of file diff --git a/bin/loman.py b/bin/loman.py new file mode 100755 index 0000000000..7575e2904f --- /dev/null +++ b/bin/loman.py @@ -0,0 +1,96 @@ +#!/usr/bin/python + +# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""This module allows adding and deleting of projects to the local manifest.""" + +import sys +import optparse +import os +import xml.etree.ElementTree as ElementTree + +from cros_build_lib import Die + + +def _FindRepoDir(): + cwd = os.getcwd() + while cwd != '/': + repo_dir = os.path.join(cwd, '.repo') + if os.path.isdir(repo_dir): + return repo_dir + cwd = os.path.dirname(cwd) + return None + + +class LocalManifest: + """Class which provides an abstraction for manipulating the local manifest.""" + + def __init__(self, text=None): + self._text = text or '\n' + + def Parse(self): + """Parse the manifest.""" + self._root = ElementTree.fromstring(self._text) + + def AddWorkonProject(self, name, path): + """Add a new workon project if it is not already in the manifest. + + Returns: + True on success. + """ + + for project in self._root.findall('project'): + if project.attrib['path'] == path or project.attrib['name'] == name: + return False + self._AddProject(name, path, workon='True') + return True + + def _AddProject(self, name, path, workon='False'): + element = ElementTree.Element('project', name=name, path=path, + workon=workon) + element.tail = '\n' + self._root.append(element) + + def ToString(self): + return ElementTree.tostring(self._root, encoding='UTF-8') + + +def main(argv): + usage = 'usage: %prog add [options] ' + parser = optparse.OptionParser(usage=usage) + parser.add_option('-w', '--workon', action='store_true', dest='workon', + default=False, help='Is this a workon package?') + parser.add_option('-f', '--file', dest='manifest', + help='Non-default manifest file to read.') + (options, args) = parser.parse_args(argv[2:]) + if len(args) < 2: + parser.error('Not enough arguments') + if argv[1] not in ['add']: + parser.error('Unsupported command: %s.' % argv[1]) + if not options.workon: + parser.error('Adding of non-workon projects is currently unsupported.') + (name, path) = (args[0], args[1]) + + repo_dir = _FindRepoDir() + if not repo_dir: + Die("Unable to find repo dir.") + local_manifest = options.manifest or \ + os.path.join(_FindRepoDir(), 'local_manifest.xml') + if os.path.isfile(local_manifest): + ptree = LocalManifest(open(local_manifest).read()) + else: + ptree = LocalManifest() + ptree.Parse() + if not ptree.AddWorkonProject(name, path): + Die('Path "%s" or name "%s" already exits in the manifest.' % + (path, name)) + try: + print >> open(local_manifest, 'w'), ptree.ToString() + except Exception, e: + Die('Error writing to manifest: %s' % e) + + +if __name__ == '__main__': + main(sys.argv) diff --git a/bin/loman_unittest.py b/bin/loman_unittest.py new file mode 100755 index 0000000000..fc74ac7af2 --- /dev/null +++ b/bin/loman_unittest.py @@ -0,0 +1,111 @@ +#!/usr/bin/python + +# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Unittests for loman.""" + +import os +import StringIO +import sys +import tempfile +import unittest + +import loman + +_TEST_MANIFEST1 = """ + +""" + +class LocalManifestTest(unittest.TestCase): + + def setUp(self): + self.utf8 = "\n" + self.tiny_manifest = '\n' + + def testSimpleParse(self): + ptree = loman.LocalManifest() + ptree.Parse() + + def testParse(self): + ptree = loman.LocalManifest(self.tiny_manifest) + ptree.Parse() + self.assertEqual(ptree.ToString(), self.utf8 + self.tiny_manifest) + + def testUTF8Parse(self): + ptree = loman.LocalManifest(self.utf8 + self.tiny_manifest) + ptree.Parse() + self.assertEqual(ptree.ToString(), self.utf8 + self.tiny_manifest) + + def testAddNew(self): + ptree = loman.LocalManifest('\n') + ptree.Parse() + self.assertTrue(ptree.AddWorkonProject('foo', 'path/to/foo')) + self.assertEqual( + ptree.ToString(), + self.utf8 + '\n' + '\n' + '') + + def testAddDup(self): + ptree = loman.LocalManifest('\n') + ptree.Parse() + ptree.AddWorkonProject('foo', 'path/to/foo') + self.assertTrue(not ptree.AddWorkonProject('foo', 'path/to/foo')) + self.assertTrue(not ptree.AddWorkonProject('foo', 'path/foo')) + self.assertTrue(not ptree.AddWorkonProject('foobar', 'path/to/foo')) + +class MainTest(unittest.TestCase): + + def setUp(self): + self.utf8 = "\n" + self.tiny_manifest = '\n' + self.stderr = sys.stderr + sys.stderr = StringIO.StringIO() + + def tearDown(self): + sys.stderr = self.stderr + + def testNotEnoughArgs(self): + err_msg = 'Not enough arguments\n' + self.assertRaises(SystemExit, loman.main, ['loman']) + self.assertTrue(sys.stderr.getvalue().endswith(err_msg)) + + def testNotWorkon(self): + err_msg = 'Adding of non-workon projects is currently unsupported.\n' + self.assertRaises(SystemExit, loman.main, ['loman', 'add', 'foo', 'path']) + self.assertTrue(sys.stderr.getvalue().endswith(err_msg)) + + def testBadCommand(self): + err_msg = 'Unsupported command: bad.\n' + self.assertRaises(SystemExit, loman.main, ['loman', 'bad', 'foo', 'path']) + self.assertTrue(sys.stderr.getvalue().endswith(err_msg)) + + def testSimpleAdd(self): + temp = tempfile.NamedTemporaryFile('w') + print >> temp, '\n' + temp.flush() + os.fsync(temp.fileno()) + loman.main(['loman', 'add', '--workon', '-f', + temp.name, 'foo', 'path/to/foo']) + self.assertEqual( + open(temp.name, 'r').read(), + self.utf8 + '\n' + '\n' + '\n') + + def testAddDup(self): + temp = tempfile.NamedTemporaryFile('w') + print >> temp, '\n' + temp.flush() + os.fsync(temp.fileno()) + loman.main(['loman', 'add', '--workon', '-f', + temp.name, 'foo', 'path/to/foo']) + self.assertRaises(SystemExit, loman.main, + ['loman', 'add', '--workon', '-f', + temp.name, 'foo', 'path/to/foo']) + + +if __name__ == '__main__': + unittest.main()