diff --git a/gclient.py b/gclient.py index 1a440731a9..70ded2a0f2 100755 --- a/gclient.py +++ b/gclient.py @@ -555,7 +555,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.CreateSCM(solution["url"], self._root_dir, None) url = scm.FullUrlForRelativeUrl(url) if d in deps and deps[d] != url: @@ -672,7 +672,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.CreateSCM(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 @@ -698,7 +698,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.CreateSCM(url, self._root_dir, d) scm.RunCommand(command, self._options, args, file_list) self._options.revision = None @@ -715,7 +715,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.CreateSCM(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 99adc1f1a9..51a3f5f8db 100644 --- a/gclient_scm.py +++ b/gclient_scm.py @@ -28,6 +28,20 @@ SVN_COMMAND = "svn" ### SCM abstraction layer +# Factory Method for SCM wrapper creation + +def CreateSCM(url=None, root_dir=None, relpath=None, 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. @@ -36,7 +50,6 @@ class SCMWrapper(object): 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. self.scm_name = scm_name self.url = url self._root_dir = root_dir @@ -55,21 +68,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.""" @@ -255,6 +268,9 @@ class SCMWrapper(object): # "svn up --revision BASE" achieve the same effect. RunSVNAndGetFileList(['update', '--revision', 'BASE'], path, file_list) + 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 aa341f3cb4..813e4e2b99 100644 --- a/tests/gclient_test.py +++ b/tests/gclient_test.py @@ -71,12 +71,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.CreateSCM + gclient_scm.CreateSCM = self.mox.CreateMockAnything() def tearDown(self): gclient.GClient = self._gclient_gclient - gclient_scm.SCMWrapper = self._scm_wrapper + gclient_scm.CreateSCM = self._scm_wrapper BaseTestCase.tearDown(self) @@ -409,7 +409,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.CreateSCM(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, []) @@ -466,7 +466,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.CreateSCM(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, []) @@ -478,7 +478,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.CreateSCM( 'svn://scm.t/trunk', self.root_dir, os.path.join(solution_name, "src", "t")).AndReturn(scm_wrapper_t) @@ -540,7 +540,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.CreateSCM(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, []) @@ -550,13 +550,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.CreateSCM('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.CreateSCM('svn://custom.t/trunk', self.root_dir, "src/t").AndReturn(scm_wrapper_t) @@ -626,7 +626,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.CreateSCM(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) @@ -635,7 +635,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.CreateSCM(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) @@ -644,7 +644,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.CreateSCM('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, []) @@ -672,9 +672,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.CreateSCM(self.url, self.root_dir, name).AndReturn( + gclient_scm.CreateSCM) + gclient_scm.CreateSCM.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), @@ -766,36 +766,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.CreateSCM(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.CreateSCM(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.CreateSCM(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.CreateSCM(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.CreateSCM( '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.CreateSCM(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.CreateSCM('svn://random_server:123/trunk/python_24@5580', self.root_dir, 'src/third_party/python_24').AndReturn( scm_wrapper_python) @@ -871,18 +871,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.CreateSCM(self.url, self.root_dir, name).AndReturn( + gclient_scm.CreateSCM) + gclient_scm.CreateSCM.RunCommand('update', options, self.args, []) - gclient_scm.SCMWrapper(self.url, self.root_dir, + gclient_scm.CreateSCM(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.CreateSCM(webkit_path, self.root_dir, + 'foo/third_party/WebKit').AndReturn(gclient_scm.CreateSCM) + gclient_scm.CreateSCM.RunCommand('update', options, self.args, []) self.mox.ReplayAll() client = self._gclient_gclient(self.root_dir, options) @@ -927,18 +927,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.CreateSCM(self.url, self.root_dir, name).AndReturn( + gclient_scm.CreateSCM) + gclient_scm.CreateSCM.RunCommand('update', options, self.args, []) - gclient_scm.SCMWrapper(self.url, self.root_dir, + gclient_scm.CreateSCM(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.CreateSCM(webkit_path, self.root_dir, + 'foo/third_party/WebKit').AndReturn(gclient_scm.CreateSCM) + gclient_scm.CreateSCM.RunCommand('update', options, self.args, []) self.mox.ReplayAll() client = self._gclient_gclient(self.root_dir, options) @@ -962,9 +962,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.CreateSCM(self.url, self.root_dir, name).AndReturn( + gclient_scm.CreateSCM) + gclient_scm.CreateSCM.RunCommand('update', options, self.args, []) self.mox.ReplayAll() client = self._gclient_gclient(self.root_dir, options) @@ -1039,12 +1039,19 @@ 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) + 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' @@ -1062,8 +1069,8 @@ class SCMWrapperTestCase(GClientBaseTestCase): scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir, relpath=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.