1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
#!/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.
Also, remember all directories that had to be followed to find out what paths
mean. This is of particular importance to the '..' operator, which may
involve temporary excursions into directories that otherwise contain no files
of relevance to the build. But the directories must still be replicated on the
server for the semantics of '..' to work. These are called must_exist_dirs.
"""
def __init__(self,
simple_build_stat,
canonical_path,
realpath_map,
systemdir_prefix_cache):
"""Constructor.
Arguments:
simple_build_stat: object of type SimpleBuildStat
canonical_path: function of type CanonicalPath
realpath_map: a CanonicalMapToIndex; see cache_basics.py
systemdir_prefix_cache: a SystemdirPrefixCache; see cache_basics.py.
"""
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
self.must_exist_dirs = []
self.realpath_map = realpath_map
self.systemdir_prefix_cache = systemdir_prefix_cache
def Links(self):
"""Return the list of symbolic links created."""
return self.links
def MustExistDirs(self):
return self.must_exist_dirs
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 the parent, root_prefix_real, is there
if not lookup(root_prefix_real):
# We have not been in this real location before.
if not os.path.isdir(root_prefix_real):
# Now check that the parent of the link is not under a default system
# dir. If it is, then we assume that the parent and indeed the
# link itself exist on the server as well, and thus, don't need to
# be mirrored.
realpath_map = self.realpath_map
realpath_idx = realpath_map.Index(prefix_real)
if not self.systemdir_prefix_cache.StartsWithSystemdir(realpath_idx,
realpath_map):
# Not under default system dir. Mark this directory as one that
# must always be created on the server.
self.must_exist_dirs.append(root_prefix_real)
# Create parent path in mirror directory.
os.makedirs(root_prefix_real)
else:
break
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
|