summaryrefslogtreecommitdiffstats
path: root/meta/lib/oe
diff options
context:
space:
mode:
authorEnrico Scholz <enrico.scholz@sigma-chemnitz.de>2013-02-10 13:41:46 +0100
committerRichard Purdie <richard.purdie@linuxfoundation.org>2013-02-11 14:46:11 +0000
commit21b6ff9bc36f5706456b3ed3ca7b6455f77a5eaa (patch)
treea81edd12c3629331741ce3caf10d931531989ccb /meta/lib/oe
parentf2e16c655286aab465054db6727fa3365bef3124 (diff)
downloadpoky-21b6ff9bc36f5706456b3ed3ca7b6455f77a5eaa.tar.gz
lib: implemented oe.path.realpath()
Various parts of the buildsystem have to work with symlinks. Resolving them is not trivial because they are always relative to a sysroot directory. Patch adds a function which returns the destination of a symlink by assuming a given path as the / toplevel directory. A testsuite was added too. (From OE-Core rev: 76e0bd7f8e3a3bd052a6e329f88e2d8099e899c4) Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/lib/oe')
-rw-r--r--meta/lib/oe/path.py87
-rw-r--r--meta/lib/oe/tests/test_path.py89
2 files changed, 176 insertions, 0 deletions
diff --git a/meta/lib/oe/path.py b/meta/lib/oe/path.py
index ea58bedc8b..0b34cebdab 100644
--- a/meta/lib/oe/path.py
+++ b/meta/lib/oe/path.py
@@ -2,6 +2,7 @@ import errno
2import glob 2import glob
3import shutil 3import shutil
4import subprocess 4import subprocess
5import os.path
5 6
6def join(*paths): 7def join(*paths):
7 """Like os.path.join but doesn't treat absolute RHS specially""" 8 """Like os.path.join but doesn't treat absolute RHS specially"""
@@ -161,3 +162,89 @@ def find(dir, **walkoptions):
161 for root, dirs, files in os.walk(dir, **walkoptions): 162 for root, dirs, files in os.walk(dir, **walkoptions):
162 for file in files: 163 for file in files:
163 yield os.path.join(root, file) 164 yield os.path.join(root, file)
165
166
167## realpath() related functions
168def __is_path_below(file, root):
169 return (file + os.path.sep).startswith(root)
170
171def __realpath_rel(start, rel_path, root, loop_cnt):
172 """Calculates real path of symlink 'start' + 'rel_path' below
173 'root'; no part of 'start' below 'root' must contain symlinks. """
174 have_dir = True
175
176 for d in rel_path.split(os.path.sep):
177 if not have_dir:
178 raise OSError(errno.ENOENT, "no such directory %s" % start)
179
180 if d == os.path.pardir: # '..'
181 if len(start) >= len(root):
182 # do not follow '..' before root
183 start = os.path.dirname(start)
184 else:
185 # emit warning?
186 pass
187 else:
188 (start, have_dir) = __realpath(os.path.join(start, d),
189 root, loop_cnt)
190
191 assert(__is_path_below(start, root))
192
193 return start
194
195def __realpath(file, root, loop_cnt):
196 while os.path.islink(file) and len(file) >= len(root):
197 if loop_cnt == 0:
198 raise OSError(errno.ELOOP, file)
199
200 loop_cnt -= 1
201 target = os.path.normpath(os.readlink(file))
202
203 if not os.path.isabs(target):
204 tdir = os.path.dirname(file)
205 assert(__is_path_below(tdir, root))
206 else:
207 tdir = root
208
209 file = __realpath_rel(tdir, target, root, loop_cnt)
210
211 try:
212 is_dir = os.path.isdir(file)
213 except:
214 is_dir = false
215
216 return (file, is_dir)
217
218def realpath(file, root, use_physdir = True, loop_cnt = 100):
219 """ Returns the canonical path of 'file' with assuming a toplevel
220 'root' directory. When 'use_physdir' is set, all preceding path
221 components of 'file' will be resolved first; this flag should be
222 set unless it is guaranteed that there is no symlink in the path."""
223
224 root = os.path.normpath(root)
225 file = os.path.normpath(file)
226
227 if not root.endswith(os.path.sep):
228 # letting root end with '/' makes some things easier
229 root = root + os.path.sep
230
231 if not __is_path_below(file, root):
232 raise OSError(errno.EINVAL, "file '%s' is not below root" % file)
233
234 try:
235 if use_physdir:
236 file = __realpath_rel(root, file[(len(root) - 1):], root, loop_cnt)
237 else:
238 file = __realpath(file, root, loop_cnt)[0]
239 except OSError, e:
240 if e.errno == errno.ELOOP:
241 # make ELOOP more readable; without catching it, there will
242 # be printed a backtrace with 100s of OSError exceptions
243 # else
244 raise OSError(errno.ELOOP,
245 "too much recursions while resolving '%s'; loop in '%s'" %
246 (file, e.strerror))
247
248 raise
249
250 return file
diff --git a/meta/lib/oe/tests/test_path.py b/meta/lib/oe/tests/test_path.py
new file mode 100644
index 0000000000..e6aa601618
--- /dev/null
+++ b/meta/lib/oe/tests/test_path.py
@@ -0,0 +1,89 @@
1import unittest
2import oe, oe.path
3import tempfile
4import os
5import errno
6import shutil
7
8class TestRealPath(unittest.TestCase):
9 DIRS = [ "a", "b", "etc", "sbin", "usr", "usr/bin", "usr/binX", "usr/sbin", "usr/include", "usr/include/gdbm" ]
10 FILES = [ "etc/passwd", "b/file" ]
11 LINKS = [
12 ( "bin", "/usr/bin", "/usr/bin" ),
13 ( "binX", "usr/binX", "/usr/binX" ),
14 ( "c", "broken", "/broken" ),
15 ( "etc/passwd-1", "passwd", "/etc/passwd" ),
16 ( "etc/passwd-2", "passwd-1", "/etc/passwd" ),
17 ( "etc/passwd-3", "/etc/passwd-1", "/etc/passwd" ),
18 ( "etc/shadow-1", "/etc/shadow", "/etc/shadow" ),
19 ( "etc/shadow-2", "/etc/shadow-1", "/etc/shadow" ),
20 ( "prog-A", "bin/prog-A", "/usr/bin/prog-A" ),
21 ( "prog-B", "/bin/prog-B", "/usr/bin/prog-B" ),
22 ( "usr/bin/prog-C", "../../sbin/prog-C", "/sbin/prog-C" ),
23 ( "usr/bin/prog-D", "/sbin/prog-D", "/sbin/prog-D" ),
24 ( "usr/binX/prog-E", "../sbin/prog-E", None ),
25 ( "usr/bin/prog-F", "../../../sbin/prog-F", "/sbin/prog-F" ),
26 ( "loop", "a/loop", None ),
27 ( "a/loop", "../loop", None ),
28 ( "b/test", "file/foo", None ),
29 ]
30
31 LINKS_PHYS = [
32 ( "./", "/", "" ),
33 ( "binX/prog-E", "/usr/sbin/prog-E", "/sbin/prog-E" ),
34 ]
35
36 EXCEPTIONS = [
37 ( "loop", errno.ELOOP ),
38 ( "b/test", errno.ENOENT ),
39 ]
40
41 def __del__(self):
42 try:
43 #os.system("tree -F %s" % self.tmpdir)
44 shutil.rmtree(self.tmpdir)
45 except:
46 pass
47
48 def setUp(self):
49 self.tmpdir = tempfile.mkdtemp(prefix = "oe-test_path")
50 self.root = os.path.join(self.tmpdir, "R")
51
52 os.mkdir(os.path.join(self.tmpdir, "_real"))
53 os.symlink("_real", self.root)
54
55 for d in self.DIRS:
56 os.mkdir(os.path.join(self.root, d))
57 for f in self.FILES:
58 file(os.path.join(self.root, f), "w")
59 for l in self.LINKS:
60 os.symlink(l[1], os.path.join(self.root, l[0]))
61
62 def __realpath(self, file, use_physdir):
63 return oe.path.realpath(os.path.join(self.root, file), self.root, use_physdir)
64
65 def test_norm(self):
66 for l in self.LINKS:
67 if l[2] == None:
68 continue
69
70 target_p = self.__realpath(l[0], True)
71 target_l = self.__realpath(l[0], False)
72
73 if l[2] != False:
74 self.assertEqual(target_p, target_l)
75 self.assertEqual(l[2], target_p[len(self.root):])
76
77 def test_phys(self):
78 for l in self.LINKS_PHYS:
79 target_p = self.__realpath(l[0], True)
80 target_l = self.__realpath(l[0], False)
81
82 self.assertEqual(l[1], target_p[len(self.root):])
83 self.assertEqual(l[2], target_l[len(self.root):])
84
85 def test_loop(self):
86 for e in self.EXCEPTIONS:
87 self.assertRaisesRegexp(OSError, r'\[Errno %u\]' % e[1],
88 self.__realpath, e[0], False)
89