Commit e105d8d0 authored by maruel@chromium.org's avatar maruel@chromium.org

Pull gclient revision 72 and add the gclient unit test.

Review URL: http://codereview.chromium.org/99226

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@14957 0039d316-1c4b-4281-b951-d872f2087c98
parent 207fdf36
# Copyright 2008-2009, Google Inc.
gclient is a tool for managing a modular checkout of source code
from multiple source code repositories. It wraps underlying source
code management commands to provide support for distributing tree
updates, status commands, and diffs across multiple checked-out
working directories.
The gclient script is controlled by a ".gclient" file at the top
of a directory tree which will contain source code from multiple
locations. A ".gclient" file is a Python script that defines a list
of "solutions" with the following format:
solutions = [
{ "name" : "src",
"url" : "svn://svnserver/component/trunk/src",
"custom_deps" : {
# To use the trunk of a component instead of what's in DEPS:
#"component": "https://svnserver/component/trunk/",
# To exclude a component from your working copy:
#"data/really_large_component": None,
}
},
]
A "solution" is a collection of component pieces of software that will
be checked out in a specific directory layout for building together.
Each entry in the "solutions" list is defined by a Python dictionary
that contains the following items:
name
The name of the directory in which the solution will be
checked out.
url
The URL from which this solution will be checked out.
gclient expects that the checked-out solution will contain a
file named "DEPS" that in turn defines the specific pieces
that must be checked out to create the working directory
layout for building and developing the solution's software.
custom_deps
A dictionary containing optional custom overrides for entries
in the solution's "DEPS" file. This can be used to have
the local working directory *not* check out and update specific
components, or to sync the local working-directory copy of a
given component to a different specific revision, or a branch,
or the head of a tree. It can also be used to append new entries
that do not exist in the "DEPS" file.
Within each checked-out solution, gclient expects to find a file
named "DEPS" which defines the different component pieces of
software that must be checked out for the solution. The "DEPS"
file is a Python script that defines a dictionary named "deps":
deps = {
"src/outside" : "http://outside-server/trunk@1234",
"src/component" : "svn://svnserver/component/trunk/src@77829",
"src/relative" : "/trunk/src@77829",
}
Each item in the "deps" dictionary consists of a key-value pair.
The key is the directory into which the component will be checked
out, relative to the directory containing the ".gclient" file.
The value is the URL from which that directory will be checked out.
If there is no address scheme (that is, no "http:" or "svn:" prefix),
then the value must begin with a slash and is treated relative to the
root of the solution's repository.
The URL typically contains a specific revision or change number (as
appropriate for the underlying SCM system) to "freeze" the external
software at a specific, known state. Alternatively, if there is no
revision or change number, the URL will track the latest changes on the
specific trunk or branch.
......@@ -78,18 +78,6 @@ import urlparse
import xml.dom.minidom
import urllib
def getText(nodelist):
"""
Return the concatenated text for the children of a list of DOM nodes.
"""
rc = []
for node in nodelist:
if node.nodeType == node.TEXT_NODE:
rc.append(node.data)
else:
rc.append(getText(node.childNodes))
return ''.join(rc)
SVN_COMMAND = "svn"
......@@ -271,6 +259,26 @@ solutions = [
## Generic utils
def getText(nodelist):
"""
Return the concatenated text for the children of a list of DOM nodes.
"""
rc = []
for node in nodelist:
if node.nodeType == node.TEXT_NODE:
rc.append(node.data)
else:
rc.append(getText(node.childNodes))
return ''.join(rc)
def ParseXML(output):
try:
return xml.dom.minidom.parseString(output)
except xml.parsers.expat.ExpatError:
return None
class Error(Exception):
"""gclient exception class."""
pass
......@@ -584,20 +592,15 @@ def CaptureSVNHeadRevision(options, url):
class FileStatus:
def __init__(self, path, text_status, props, locked, history, switched,
repo_locked, out_of_date):
self.path = path.strip()
def __init__(self, path, text_status, props, history):
self.path = path
self.text_status = text_status
self.props = props
self.locked = locked
self.history = history
self.switched = switched
self.repo_locked = repo_locked
self.out_of_date = out_of_date
def __str__(self):
return (self.text_status + self.props + self.locked + self.history +
self.switched + self.repo_locked + self.out_of_date +
# Emulate svn status 1.5 output.
return (self.text_status + self.props + ' ' + self.history + ' ' +
self.path)
......@@ -608,18 +611,53 @@ def CaptureSVNStatus(options, path):
path: The directory to run svn status.
Returns:
An array of FileStatus corresponding to the output of 'svn status'
"""
info = CaptureSVN(options, ["status"], path)
result = []
if not info:
return result
for line in info.splitlines():
if line:
new_item = FileStatus(line[7:], line[0:1], line[1:2], line[2:3],
line[3:4], line[4:5], line[5:6], line[6:7])
result.append(new_item)
return result
An array of FileStatus corresponding to the emulated output of 'svn status'
version 1.5."""
dom = ParseXML(CaptureSVN(options, ["status", "--xml"], path))
results = []
if dom:
# /status/target/entry/(wc-status|commit|author|date)
for target in dom.getElementsByTagName('target'):
base_path = target.getAttribute('path')
for entry in target.getElementsByTagName('entry'):
file = entry.getAttribute('path')
wc_status = entry.getElementsByTagName('wc-status')
assert len(wc_status) == 1
# Emulate svn 1.5 status ouput...
statuses = [' ' for i in range(7)]
# Col 0
xml_item_status = wc_status[0].getAttribute('item')
if xml_item_status == 'unversioned':
statuses[0] = '?'
elif xml_item_status == 'modified':
statuses[0] = 'M'
elif xml_item_status == 'added':
statuses[0] = 'A'
elif xml_item_status == 'conflicted':
statuses[0] = 'C'
elif not xml_item_status:
pass
else:
raise Exception('Unknown item status "%s"; please implement me!' %
xml_item_status)
# Col 1
xml_props_status = wc_status[0].getAttribute('props')
if xml_props_status == 'modified':
statuses[1] = 'M'
elif xml_props_status == 'conflicted':
statuses[1] = 'C'
elif (not xml_props_status or xml_props_status == 'none' or
xml_props_status == 'normal'):
pass
else:
raise Exception('Unknown props status "%s"; please implement me!' %
xml_props_status)
# Col 3
if wc_status[0].getAttribute('copied') == 'true':
statuses[3] = '+'
item = FileStatus(file, statuses[0], statuses[1], statuses[3])
results.append(item)
return results
### SCM abstraction layer
......@@ -638,10 +676,10 @@ class SCMWrapper(object):
self.url = url
self._root_dir = root_dir
if self._root_dir:
self._root_dir = self._root_dir.replace('/', os.sep).strip()
self._root_dir = self._root_dir.replace('/', os.sep)
self.relpath = relpath
if self.relpath:
self.relpath = self.relpath.replace('/', os.sep).strip()
self.relpath = self.relpath.replace('/', os.sep)
def FullUrlForRelativeUrl(self, url):
# Find the forth '/' and strip from there. A bit hackish.
......
# Copyright 2008-2009, Google Inc.
To run the gclient's unit tests, you need to checkout pymox and install it:
svn co http://pymox.googlecode.com/svn/trunk pymox
cd pymox
python setup.py install
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment