diff options
Diffstat (limited to 'include_server/mirror_path.py')
-rwxr-xr-x | include_server/mirror_path.py | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/include_server/mirror_path.py b/include_server/mirror_path.py new file mode 100755 index 0000000..3be5062 --- /dev/null +++ b/include_server/mirror_path.py @@ -0,0 +1,113 @@ +#!/usr/bin/python2.4 + +# Copyright 2007 Google Inc. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# """Memoizing, piecemeal mirroring of directory and link structure.""" + +__author__ = "Nils Klarlund" + + +import os +import os.path + +import cache_basics + +class MirrorPath(object): + """Make a caching structure for copying all parts of the paths that + method DoPath is called with. This includes replication of symbolic + links. But the targets of symbolic links are absolutized: they are + replaced by the realpath of the original target, whether this target + was relative or absolute.""" + + def __init__(self, + simple_build_stat, + canonical_path): + """Constructor. + + Arguments: + simple_build_stat: object of type SimpleBuildStat + canonical_path: function of type CanonicalPath + """ + assert isinstance(simple_build_stat, cache_basics.SimpleBuildStat) + assert isinstance(canonical_path, cache_basics.CanonicalPath) + # All links encountered so far. + self.links = [] + # We cache tuples (filepath, current_dir_idx) for which we've already fixed + # up the symbolic links. + self.link_stat = set([]) + # Usual abbreviations. + self.simple_build_stat = simple_build_stat + self.canonical_path = canonical_path + + def Links(self): + """Return the list of symbolic links created.""" + return self.links + + def DoPath(self, filepath, current_dir_idx, root): + """Mirror the parts of filepath not yet created under root. + + Arguments: + filepath: a string, which is relative or absolute filename + current_dir_idx: a directory index + root: a string denoting an absolute path for an existing directory + """ + assert isinstance(filepath, str) + assert isinstance(current_dir_idx, int) + assert isinstance(root, str) + assert root[0] == '/' and root[-1] != '/' + assert os.path.isdir(root), root + + link_stat = self.link_stat + lookup = self.simple_build_stat.Lookup + # Working from the end (in the hope that a cache lookup will reveal + # the futility of further work), make sure that intermediate + # destinations exist, and replicate symbolic links where necessary. + while filepath and filepath != '/': + if (filepath, current_dir_idx) in link_stat: + # Filepath is already mirrored + return + link_stat.add((filepath, current_dir_idx)) + # Process suffix of filepath by + # - making sure that the mirrored real path of the prefix exists, + # - and that the suffix if a symbolic link + # is replicated as a symbolic link. + assert filepath[-1] != '/', filepath + + # Now identify the potential symbolic link at the end of filepath + (prefix_filepath, suffix) = os.path.split(filepath) + # Calculate the real position of the destination of the prefix + prefix_real = self.canonical_path.Canonicalize(prefix_filepath) + + if prefix_real == '/': prefix_real = '' + + # And, its counterpart under root + root_prefix_real = root + prefix_real + + # Make sure that root_prefix_real is there + if not lookup(root_prefix_real): + if not os.path.isdir(root_prefix_real): + os.makedirs(root_prefix_real) + self.simple_build_stat.cache[root_prefix_real] = True + + assert os.path.isdir(root_prefix_real) + # Create the mirrored symbolic link if applicable + if os.path.islink(filepath): + link_name = root_prefix_real + '/' + suffix + if not os.path.exists(link_name): + os.symlink(self.canonical_path.Canonicalize(filepath), link_name) + self.links.append(link_name) + filepath = prefix_filepath |