From e0df232da7c92b0776a3e86a70687e2c9f2bad7b Mon Sep 17 00:00:00 2001 From: Jeff Hamilton Date: Mon, 21 Apr 2014 17:10:59 -0500 Subject: [PATCH] Add linkfile support. It's just like copyfile and runs at the same time as copyfile but instead of copying it creates a symlink instead. This is needed because copyfile copies the target of the link as opposed to the symlink itself. Change-Id: I7bff2aa23f0d80d9d51061045bd9c86a9b741ac5 --- manifest_xml.py | 16 ++++++++++++++++ project.py | 49 +++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/manifest_xml.py b/manifest_xml.py index 3c8fadd6..e2f58e62 100644 --- a/manifest_xml.py +++ b/manifest_xml.py @@ -261,6 +261,12 @@ class XmlManifest(object): ce.setAttribute('dest', c.dest) e.appendChild(ce) + for l in p.linkfiles: + le = doc.createElement('linkfile') + le.setAttribute('src', l.src) + le.setAttribute('dest', l.dest) + e.appendChild(le) + default_groups = ['all', 'name:%s' % p.name, 'path:%s' % p.relpath] egroups = [g for g in p.groups if g not in default_groups] if egroups: @@ -765,6 +771,8 @@ class XmlManifest(object): for n in node.childNodes: if n.nodeName == 'copyfile': self._ParseCopyFile(project, n) + if n.nodeName == 'linkfile': + self._ParseLinkFile(project, n) if n.nodeName == 'annotation': self._ParseAnnotation(project, n) if n.nodeName == 'project': @@ -814,6 +822,14 @@ class XmlManifest(object): # dest is relative to the top of the tree project.AddCopyFile(src, dest, os.path.join(self.topdir, dest)) + def _ParseLinkFile(self, project, node): + src = self._reqatt(node, 'src') + dest = self._reqatt(node, 'dest') + if not self.IsMirror: + # src is project relative; + # dest is relative to the top of the tree + project.AddLinkFile(src, dest, os.path.join(self.topdir, dest)) + def _ParseAnnotation(self, project, node): name = self._reqatt(node, 'name') value = self._reqatt(node, 'value') diff --git a/project.py b/project.py index 48fa82b7..c2bedde6 100644 --- a/project.py +++ b/project.py @@ -231,6 +231,30 @@ class _CopyFile: except IOError: _error('Cannot copy file %s to %s', src, dest) +class _LinkFile: + def __init__(self, src, dest, abssrc, absdest): + self.src = src + self.dest = dest + self.abs_src = abssrc + self.abs_dest = absdest + + def _Link(self): + src = self.abs_src + dest = self.abs_dest + # link file if it does not exist or is out of date + if not os.path.islink(dest) or os.readlink(dest) != src: + try: + # remove existing file first, since it might be read-only + if os.path.exists(dest): + os.remove(dest) + else: + dest_dir = os.path.dirname(dest) + if not os.path.isdir(dest_dir): + os.makedirs(dest_dir) + os.symlink(src, dest) + except IOError: + _error('Cannot link file %s to %s', src, dest) + class RemoteSpec(object): def __init__(self, name, @@ -555,6 +579,7 @@ class Project(object): self.snapshots = {} self.copyfiles = [] + self.linkfiles = [] self.annotations = [] self.config = GitConfig.ForRepository( gitdir = self.gitdir, @@ -1040,7 +1065,7 @@ class Project(object): except OSError as e: print("warn: Cannot remove archive %s: " "%s" % (tarpath, str(e)), file=sys.stderr) - self._CopyFiles() + self._CopyAndLinkFiles() return True if is_new is None: @@ -1103,9 +1128,11 @@ class Project(object): def PostRepoUpgrade(self): self._InitHooks() - def _CopyFiles(self): + def _CopyAndLinkFiles(self): for copyfile in self.copyfiles: copyfile._Copy() + for linkfile in self.linkfiles: + linkfile._Link() def GetCommitRevisionId(self): """Get revisionId of a commit. @@ -1152,7 +1179,7 @@ class Project(object): def _doff(): self._FastForward(revid) - self._CopyFiles() + self._CopyAndLinkFiles() head = self.work_git.GetHead() if head.startswith(R_HEADS): @@ -1188,7 +1215,7 @@ class Project(object): except GitError as e: syncbuf.fail(self, e) return - self._CopyFiles() + self._CopyAndLinkFiles() return if head == revid: @@ -1210,7 +1237,7 @@ class Project(object): except GitError as e: syncbuf.fail(self, e) return - self._CopyFiles() + self._CopyAndLinkFiles() return upstream_gain = self._revlist(not_rev(HEAD), revid) @@ -1283,12 +1310,12 @@ class Project(object): if cnt_mine > 0 and self.rebase: def _dorebase(): self._Rebase(upstream = '%s^1' % last_mine, onto = revid) - self._CopyFiles() + self._CopyAndLinkFiles() syncbuf.later2(self, _dorebase) elif local_changes: try: self._ResetHard(revid) - self._CopyFiles() + self._CopyAndLinkFiles() except GitError as e: syncbuf.fail(self, e) return @@ -1301,6 +1328,12 @@ class Project(object): abssrc = os.path.join(self.worktree, src) self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest)) + def AddLinkFile(self, src, dest, absdest): + # dest should already be an absolute path, but src is project relative + # make src an absolute path + abssrc = os.path.join(self.worktree, src) + self.linkfiles.append(_LinkFile(src, dest, abssrc, absdest)) + def AddAnnotation(self, name, value, keep): self.annotations.append(_Annotation(name, value, keep)) @@ -2195,7 +2228,7 @@ class Project(object): if GitCommand(self, cmd).Wait() != 0: raise GitError("cannot initialize work tree") - self._CopyFiles() + self._CopyAndLinkFiles() def _gitdir_path(self, path): return os.path.realpath(os.path.join(self.gitdir, path))