diff --git a/gclient.py b/gclient.py index bc70ccb81..386b1a916 100755 --- a/gclient.py +++ b/gclient.py @@ -554,7 +554,7 @@ class GClient(object): raise Error( "relative DEPS entry \"%s\" must begin with a slash" % d) # Create a scm just to query the full url. - scm = gclient_scm.SCMWrapper(solution["url"], self._root_dir, + scm = gclient_scm.create_scm(solution["url"], self._root_dir, None) url = scm.FullUrlForRelativeUrl(url) if d in deps and deps[d] != url: @@ -671,7 +671,7 @@ class GClient(object): entries[name] = url if run_scm: self._options.revision = revision_overrides.get(name) - scm = gclient_scm.SCMWrapper(url, self._root_dir, name) + scm = gclient_scm.create_scm(url, self._root_dir, name) scm.RunCommand(command, self._options, args, file_list) file_list = [os.path.join(name, file.strip()) for file in file_list] self._options.revision = None @@ -697,7 +697,7 @@ class GClient(object): entries[d] = url if run_scm: self._options.revision = revision_overrides.get(d) - scm = gclient_scm.SCMWrapper(url, self._root_dir, d) + scm = gclient_scm.create_scm(url, self._root_dir, d) scm.RunCommand(command, self._options, args, file_list) self._options.revision = None @@ -714,7 +714,7 @@ class GClient(object): entries[d] = url if run_scm: self._options.revision = revision_overrides.get(d) - scm = gclient_scm.SCMWrapper(url, self._root_dir, d) + scm = gclient_scm.create_scm(url, self._root_dir, d) scm.RunCommand(command, self._options, args, file_list) self._options.revision = None diff --git a/gclient_scm.py b/gclient_scm.py index 148855c89..43eaca772 100644 --- a/gclient_scm.py +++ b/gclient_scm.py @@ -27,23 +27,31 @@ SVN_COMMAND = "svn" ### SCM abstraction layer +# Factory Method for SCM wrapper creation + +def create_scm(url, root_dir, relpath, scm_name='svn'): + # TODO(maruel): Deduce the SCM from the url. + scm_map = { + 'svn' : SVNWrapper, + } + if not scm_name in scm_map: + raise gclient_utils.Error('Unsupported scm %s' % scm_name) + return scm_map[scm_name](url, root_dir, relpath, scm_name) + + +# SCMWrapper base class + class SCMWrapper(object): """Add necessary glue between all the supported SCM. This is the abstraction layer to bind to different SCM. Since currently only subversion is supported, a lot of subersionism remains. This can be sorted out once another SCM is supported.""" - def __init__(self, url=None, root_dir=None, relpath=None, - scm_name='svn'): - # TODO(maruel): Deduce the SCM from the url. + def __init__(self, url, root_dir, relpath, scm_name='svn'): self.scm_name = scm_name self.url = url - self._root_dir = root_dir - if self._root_dir: - self._root_dir = self._root_dir.replace('/', os.sep) - self.relpath = relpath - if self.relpath: - self.relpath = self.relpath.replace('/', os.sep) + self._root_dir = root_dir.replace('/', os.sep) + self.relpath = relpath.replace('/', os.sep) def FullUrlForRelativeUrl(self, url): # Find the forth '/' and strip from there. A bit hackish. @@ -54,21 +62,21 @@ class SCMWrapper(object): if file_list is None: file_list = [] - commands = { - 'cleanup': self.cleanup, - 'export': self.export, - 'update': self.update, - 'revert': self.revert, - 'status': self.status, - 'diff': self.diff, - 'pack': self.pack, - 'runhooks': self.status, - } + commands = ['cleanup', 'export', 'update', 'revert', + 'status', 'diff', 'pack', 'runhooks'] if not command in commands: raise gclient_utils.Error('Unknown command %s' % command) - return commands[command](options, args, file_list) + if not command in dir(self): + raise gclient_utils.Error('Command %s not implemnted in %s wrapper' % ( + command, self.scm_name)) + + return getattr(self, command)(options, args, file_list) + + +class SVNWrapper(SCMWrapper): + """ Wrapper for SVN """ def cleanup(self, options, args, file_list): """Cleanup working copy.""" @@ -258,6 +266,9 @@ class SCMWrapper(object): RunSVN(command + accumulated_paths, os.path.join(self._root_dir, self.relpath)) + def runhooks(self, options, args, file_list): + self.status(options, args, file_list) + def status(self, options, args, file_list): """Display status information.""" path = os.path.join(self._root_dir, self.relpath) diff --git a/tests/gclient_test.py b/tests/gclient_test.py index 8d9295cd2..d61aca372 100644 --- a/tests/gclient_test.py +++ b/tests/gclient_test.py @@ -70,12 +70,12 @@ class GClientBaseTestCase(BaseTestCase): self.mox.StubOutWithMock(gclient_scm, 'RunSVNAndGetFileList') self._gclient_gclient = gclient.GClient gclient.GClient = self.mox.CreateMockAnything() - self._scm_wrapper = gclient_scm.SCMWrapper - gclient_scm.SCMWrapper = self.mox.CreateMockAnything() + self._scm_wrapper = gclient_scm.create_scm + gclient_scm.create_scm = self.mox.CreateMockAnything() def tearDown(self): gclient.GClient = self._gclient_gclient - gclient_scm.SCMWrapper = self._scm_wrapper + gclient_scm.create_scm = self._scm_wrapper BaseTestCase.tearDown(self) @@ -408,7 +408,7 @@ class GClientClassTestCase(GclientTestCase): # An scm will be requested for the solution. scm_wrapper_sol = self.mox.CreateMockAnything() - gclient_scm.SCMWrapper(self.url, self.root_dir, solution_name + gclient_scm.create_scm(self.url, self.root_dir, solution_name ).AndReturn(scm_wrapper_sol) # Then an update will be performed. scm_wrapper_sol.RunCommand('update', options, self.args, []) @@ -465,7 +465,7 @@ class GClientClassTestCase(GclientTestCase): ).AndReturn(False) # An scm will be requested for the solution. - gclient_scm.SCMWrapper(self.url, self.root_dir, solution_name + gclient_scm.create_scm(self.url, self.root_dir, solution_name ).AndReturn(scm_wrapper_sol) # Then an update will be performed. scm_wrapper_sol.RunCommand('update', options, self.args, []) @@ -477,7 +477,7 @@ class GClientClassTestCase(GclientTestCase): # Next we expect an scm to be request for dep src/t but it should # use the url specified in deps and the relative path should now # be relative to the DEPS file. - gclient_scm.SCMWrapper( + gclient_scm.create_scm( 'svn://scm.t/trunk', self.root_dir, os.path.join(solution_name, "src", "t")).AndReturn(scm_wrapper_t) @@ -539,7 +539,7 @@ class GClientClassTestCase(GclientTestCase): ).AndReturn(False) # An scm will be requested for the solution. - gclient_scm.SCMWrapper(self.url, self.root_dir, solution_name + gclient_scm.create_scm(self.url, self.root_dir, solution_name ).AndReturn(scm_wrapper_sol) # Then an update will be performed. scm_wrapper_sol.RunCommand('update', options, self.args, []) @@ -549,13 +549,13 @@ class GClientClassTestCase(GclientTestCase): # Next we expect an scm to be request for dep src/n even though it does not # exist in the DEPS file. - gclient_scm.SCMWrapper('svn://custom.n/trunk', - self.root_dir, - "src/n").AndReturn(scm_wrapper_n) + gclient_scm.create_scm('svn://custom.n/trunk', + self.root_dir, + "src/n").AndReturn(scm_wrapper_n) # Next we expect an scm to be request for dep src/t but it should # use the url specified in custom_deps. - gclient_scm.SCMWrapper('svn://custom.t/trunk', + gclient_scm.create_scm('svn://custom.t/trunk', self.root_dir, "src/t").AndReturn(scm_wrapper_t) @@ -625,7 +625,7 @@ class GClientClassTestCase(GclientTestCase): ).AndReturn(False) # An scm will be requested for the first solution. - gclient_scm.SCMWrapper(url_a, self.root_dir, name_a).AndReturn( + gclient_scm.create_scm(url_a, self.root_dir, name_a).AndReturn( scm_wrapper_a) # Then an attempt will be made to read it's DEPS file. gclient.FileRead(os.path.join(self.root_dir, name_a, options.deps_file) @@ -634,7 +634,7 @@ class GClientClassTestCase(GclientTestCase): scm_wrapper_a.RunCommand('update', options, self.args, []) # An scm will be requested for the second solution. - gclient_scm.SCMWrapper(url_b, self.root_dir, name_b).AndReturn( + gclient_scm.create_scm(url_b, self.root_dir, name_b).AndReturn( scm_wrapper_b) # Then an attempt will be made to read its DEPS file. gclient.FileRead(os.path.join(self.root_dir, name_b, options.deps_file) @@ -643,7 +643,7 @@ class GClientClassTestCase(GclientTestCase): scm_wrapper_b.RunCommand('update', options, self.args, []) # Finally, an scm is requested for the shared dep. - gclient_scm.SCMWrapper('http://svn.t/trunk', self.root_dir, 'src/t' + gclient_scm.create_scm('http://svn.t/trunk', self.root_dir, 'src/t' ).AndReturn(scm_wrapper_dep) # And an update is run on it. scm_wrapper_dep.RunCommand('update', options, self.args, []) @@ -671,9 +671,9 @@ class GClientClassTestCase(GclientTestCase): ).AndReturn(False) gclient.os.path.exists(os.path.join(self.root_dir, options.entries_filename) ).AndReturn(False) - gclient_scm.SCMWrapper(self.url, self.root_dir, name).AndReturn( - gclient_scm.SCMWrapper) - gclient_scm.SCMWrapper.RunCommand('update', options, self.args, []) + gclient_scm.create_scm(self.url, self.root_dir, name).AndReturn( + gclient_scm.create_scm) + gclient_scm.create_scm.RunCommand('update', options, self.args, []) gclient.FileRead(os.path.join(self.root_dir, name, options.deps_file) ).AndReturn("Boo = 'a'") gclient.FileWrite(os.path.join(self.root_dir, options.entries_filename), @@ -765,36 +765,36 @@ deps_os = { gclient.os.path.exists(os.path.join(self.root_dir, options.entries_filename) ).AndReturn(False) - gclient_scm.SCMWrapper(self.url, self.root_dir, 'src').AndReturn( + gclient_scm.create_scm(self.url, self.root_dir, 'src').AndReturn( scm_wrapper_src) scm_wrapper_src.RunCommand('update', mox.Func(OptIsRev123), self.args, []) - gclient_scm.SCMWrapper(self.url, self.root_dir, + gclient_scm.create_scm(self.url, self.root_dir, None).AndReturn(scm_wrapper_src2) scm_wrapper_src2.FullUrlForRelativeUrl('/trunk/deps/third_party/cygwin@3248' ).AndReturn(cygwin_path) - gclient_scm.SCMWrapper(self.url, self.root_dir, + gclient_scm.create_scm(self.url, self.root_dir, None).AndReturn(scm_wrapper_src2) scm_wrapper_src2.FullUrlForRelativeUrl('/trunk/deps/third_party/WebKit' ).AndReturn(webkit_path) - gclient_scm.SCMWrapper(webkit_path, self.root_dir, + gclient_scm.create_scm(webkit_path, self.root_dir, 'foo/third_party/WebKit').AndReturn(scm_wrapper_webkit) scm_wrapper_webkit.RunCommand('update', mox.Func(OptIsRev42), self.args, []) - gclient_scm.SCMWrapper( + gclient_scm.create_scm( 'http://google-breakpad.googlecode.com/svn/trunk/src@285', self.root_dir, 'src/breakpad/bar').AndReturn(scm_wrapper_breakpad) scm_wrapper_breakpad.RunCommand('update', mox.Func(OptIsRevNone), self.args, []) - gclient_scm.SCMWrapper(cygwin_path, self.root_dir, + gclient_scm.create_scm(cygwin_path, self.root_dir, 'src/third_party/cygwin').AndReturn(scm_wrapper_cygwin) scm_wrapper_cygwin.RunCommand('update', mox.Func(OptIsRev333), self.args, []) - gclient_scm.SCMWrapper('svn://random_server:123/trunk/python_24@5580', + gclient_scm.create_scm('svn://random_server:123/trunk/python_24@5580', self.root_dir, 'src/third_party/python_24').AndReturn( scm_wrapper_python) @@ -870,18 +870,18 @@ deps = { ).AndReturn(False) gclient.os.path.exists(os.path.join(self.root_dir, options.entries_filename) ).AndReturn(False) - gclient_scm.SCMWrapper(self.url, self.root_dir, name).AndReturn( - gclient_scm.SCMWrapper) - gclient_scm.SCMWrapper.RunCommand('update', options, self.args, []) + gclient_scm.create_scm(self.url, self.root_dir, name).AndReturn( + gclient_scm.create_scm) + gclient_scm.create_scm.RunCommand('update', options, self.args, []) - gclient_scm.SCMWrapper(self.url, self.root_dir, + gclient_scm.create_scm(self.url, self.root_dir, None).AndReturn(scm_wrapper_src) scm_wrapper_src.FullUrlForRelativeUrl('/trunk/bar/WebKit' ).AndReturn(webkit_path) - gclient_scm.SCMWrapper(webkit_path, self.root_dir, - 'foo/third_party/WebKit').AndReturn(gclient_scm.SCMWrapper) - gclient_scm.SCMWrapper.RunCommand('update', options, self.args, []) + gclient_scm.create_scm(webkit_path, self.root_dir, + 'foo/third_party/WebKit').AndReturn(gclient_scm.create_scm) + gclient_scm.create_scm.RunCommand('update', options, self.args, []) self.mox.ReplayAll() client = self._gclient_gclient(self.root_dir, options) @@ -926,18 +926,18 @@ deps = { ).AndReturn(False) gclient.os.path.exists(os.path.join(self.root_dir, options.entries_filename) ).AndReturn(False) - gclient_scm.SCMWrapper(self.url, self.root_dir, name).AndReturn( - gclient_scm.SCMWrapper) - gclient_scm.SCMWrapper.RunCommand('update', options, self.args, []) + gclient_scm.create_scm(self.url, self.root_dir, name).AndReturn( + gclient_scm.create_scm) + gclient_scm.create_scm.RunCommand('update', options, self.args, []) - gclient_scm.SCMWrapper(self.url, self.root_dir, + gclient_scm.create_scm(self.url, self.root_dir, None).AndReturn(scm_wrapper_src) scm_wrapper_src.FullUrlForRelativeUrl('/trunk/bar_custom/WebKit' ).AndReturn(webkit_path) - gclient_scm.SCMWrapper(webkit_path, self.root_dir, - 'foo/third_party/WebKit').AndReturn(gclient_scm.SCMWrapper) - gclient_scm.SCMWrapper.RunCommand('update', options, self.args, []) + gclient_scm.create_scm(webkit_path, self.root_dir, + 'foo/third_party/WebKit').AndReturn(gclient_scm.create_scm) + gclient_scm.create_scm.RunCommand('update', options, self.args, []) self.mox.ReplayAll() client = self._gclient_gclient(self.root_dir, options) @@ -961,9 +961,9 @@ deps = { options = self.Options() gclient.FileRead(os.path.join(self.root_dir, name, options.deps_file) ).AndReturn(deps_content) - gclient_scm.SCMWrapper(self.url, self.root_dir, name).AndReturn( - gclient_scm.SCMWrapper) - gclient_scm.SCMWrapper.RunCommand('update', options, self.args, []) + gclient_scm.create_scm(self.url, self.root_dir, name).AndReturn( + gclient_scm.create_scm) + gclient_scm.create_scm.RunCommand('update', options, self.args, []) self.mox.ReplayAll() client = self._gclient_gclient(self.root_dir, options) @@ -1038,18 +1038,27 @@ class SCMWrapperTestCase(GClientBaseTestCase): def testDir(self): members = [ 'FullUrlForRelativeUrl', 'RunCommand', 'cleanup', 'diff', 'export', - 'pack', 'relpath', 'revert', 'scm_name', 'status', 'update', 'url', + 'pack', 'relpath', 'revert', 'runhooks', 'scm_name', 'status', + 'update', 'url', ] # If you add a member, be sure to add the relevant test! - self.compareMembers(self._scm_wrapper(), members) + self.compareMembers(self._scm_wrapper(self.url, + self.root_dir, + self.relpath), + members) + + def testUnsupportedSCM(self): + args = [self.url, self.root_dir, self.relpath] + kwargs = {'scm_name' : 'foo'} + exception_msg = 'Unsupported scm %(scm_name)s' % kwargs + self.assertRaisesError(exception_msg, self._scm_wrapper, *args, **kwargs) def testFullUrlForRelativeUrl(self): self.url = 'svn://a/b/c/d' self.mox.ReplayAll() - scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir, - relpath=self.relpath) + scm = self._scm_wrapper(self.url, self.root_dir, self.relpath) self.assertEqual(scm.FullUrlForRelativeUrl('/crap'), 'svn://a/b/crap') def testRunCommandException(self): @@ -1058,11 +1067,10 @@ class SCMWrapperTestCase(GClientBaseTestCase): ).AndReturn(False) self.mox.ReplayAll() - scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir, - relpath=self.relpath) + scm = self._scm_wrapper(self.url, self.root_dir, self.relpath) exception = "Unsupported argument(s): %s" % ','.join(self.args) - self.assertRaisesError(exception, self._scm_wrapper.RunCommand, - scm, 'update', options, self.args) + self.assertRaisesError(exception, scm.RunCommand, + 'update', options, self.args) def testRunCommandUnknown(self): # TODO(maruel): if ever used. @@ -1082,8 +1090,7 @@ class SCMWrapperTestCase(GClientBaseTestCase): self.root_dir, files_list) self.mox.ReplayAll() - scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir, - relpath=self.relpath) + scm = self._scm_wrapper(self.url, self.root_dir, self.relpath) scm.revert(options, self.args, files_list) def testRevertNone(self): @@ -1093,8 +1100,7 @@ class SCMWrapperTestCase(GClientBaseTestCase): gclient_scm.CaptureSVNStatus(base_path).AndReturn([]) self.mox.ReplayAll() - scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir, - relpath=self.relpath) + scm = self._scm_wrapper(self.url, self.root_dir, self.relpath) file_list = [] scm.revert(options, self.args, file_list) @@ -1113,8 +1119,7 @@ class SCMWrapperTestCase(GClientBaseTestCase): gclient_scm.RunSVN(['revert', 'a', 'b'], base_path) self.mox.ReplayAll() - scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir, - relpath=self.relpath) + scm = self._scm_wrapper(self.url, self.root_dir, self.relpath) file_list = [] scm.revert(options, self.args, file_list) self.assertEquals(sorted(file_list), sorted([os.path.join(base_path, 'a'), @@ -1150,8 +1155,7 @@ class SCMWrapperTestCase(GClientBaseTestCase): []).AndReturn(None) self.mox.ReplayAll() - scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir, - relpath=self.relpath) + scm = self._scm_wrapper(self.url, self.root_dir, self.relpath) file_list = [] self.assertEqual(scm.status(options, self.args, file_list), None) @@ -1173,8 +1177,7 @@ class SCMWrapperTestCase(GClientBaseTestCase): gclient_scm.RunSVNAndGetFileList(['checkout', self.url, base_path], self.root_dir, files_list) self.mox.ReplayAll() - scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir, - relpath=self.relpath) + scm = self._scm_wrapper(self.url, self.root_dir, self.relpath) scm.update(options, (), files_list) def testUpdateUpdate(self): @@ -1203,8 +1206,7 @@ class SCMWrapperTestCase(GClientBaseTestCase): self.root_dir, files_list) self.mox.ReplayAll() - scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir, - relpath=self.relpath) + scm = self._scm_wrapper(self.url, self.root_dir, self.relpath) scm.update(options, (), files_list) def testUpdateGit(self): @@ -1214,8 +1216,7 @@ class SCMWrapperTestCase(GClientBaseTestCase): print("________ found .git directory; skipping %s" % self.relpath) self.mox.ReplayAll() - scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir, - relpath=self.relpath) + scm = self._scm_wrapper(self.url, self.root_dir, self.relpath) file_list = [] scm.update(options, self.args, file_list)