Valentin David pushed to branch valentindavid/link_files_sort_resolved at BuildStream / buildstream
Commits:
-
96fc3310
by Valentin David at 2019-01-04T16:09:37Z
-
4bd6b9bb
by Valentin David at 2019-01-04T16:09:37Z
12 changed files:
- buildstream/utils.py
- + tests/integration/compose-symlink-order.py
- tests/integration/compose-symlinks.py
- + tests/integration/project/elements/compose-symlink-order/a.bst
- + tests/integration/project/elements/compose-symlink-order/b.bst
- + tests/integration/project/elements/compose-symlink-order/compose.bst
- + tests/integration/project/elements/compose-symlinks/a-foo-symlink.bst
- + tests/integration/project/elements/compose-symlinks/compose-absolute-symlink.bst
- + tests/integration/project/elements/compose-symlinks/foo-dir.bst
- + tests/integration/project/elements/compose-symlinks/integration-move-dir.bst
- + tests/integration/project/files/compose-symlink-order/a/b/.keep
- + tests/integration/project/files/compose-symlink-order/b/a/c/d
Changes:
... | ... | @@ -787,6 +787,48 @@ def _ensure_real_directory(root, destpath): |
787 | 787 |
return destpath_resolved
|
788 | 788 |
|
789 | 789 |
|
790 |
+@functools.lru_cache(maxsize=1)
|
|
791 |
+def _symloop_max():
|
|
792 |
+ if hasattr(os, 'sysconf'):
|
|
793 |
+ try:
|
|
794 |
+ ret = os.sysconf('_SC_SYMLOOP_MAX')
|
|
795 |
+ if ret != -1:
|
|
796 |
+ return ret
|
|
797 |
+ except ValueError:
|
|
798 |
+ pass
|
|
799 |
+ return 8
|
|
800 |
+ |
|
801 |
+ |
|
802 |
+@functools.lru_cache(maxsize=64)
|
|
803 |
+def _sysroot_realpath(path, sysroot):
|
|
804 |
+ assert not os.path.isabs(path)
|
|
805 |
+ assert os.path.isabs(sysroot)
|
|
806 |
+ |
|
807 |
+ loop_count = _symloop_max()
|
|
808 |
+ while True:
|
|
809 |
+ full_path = os.path.join(sysroot, path)
|
|
810 |
+ st = os.lstat(full_path)
|
|
811 |
+ mode = st.st_mode
|
|
812 |
+ if not stat.S_ISLNK(mode):
|
|
813 |
+ break
|
|
814 |
+ loop_count = loop_count - 1
|
|
815 |
+ if loop_count < 0:
|
|
816 |
+ raise UtilError("Symlink loop detected: {}".format(os.path.join(sysroot, path)))
|
|
817 |
+ link_path = os.readlink(full_path)
|
|
818 |
+ if not os.path.isabs(link_path):
|
|
819 |
+ link_path = os.path.join('/', os.path.dirname(path), link_path)
|
|
820 |
+ path = os.path.relpath(os.path.normpath(link_path), '/')
|
|
821 |
+ |
|
822 |
+ parent = os.path.dirname(path)
|
|
823 |
+ if parent != '':
|
|
824 |
+ parent = _sysroot_realpath(parent, sysroot)
|
|
825 |
+ full_parent = os.path.join(sysroot, parent)
|
|
826 |
+ if not os.path.isdir(full_parent):
|
|
827 |
+ raise UtilError("Path is not a directory: {}".format(full_parent))
|
|
828 |
+ |
|
829 |
+ return os.path.join(parent, os.path.basename(path))
|
|
830 |
+ |
|
831 |
+ |
|
790 | 832 |
# _process_list()
|
791 | 833 |
#
|
792 | 834 |
# Internal helper for copying/moving/linking file lists
|
... | ... | @@ -816,7 +858,16 @@ def _process_list(srcdir, destdir, filelist, actionfunc, result, |
816 | 858 |
# symbolic links which lead to directories before processing files inside
|
817 | 859 |
# those directories.
|
818 | 860 |
if not presorted:
|
819 |
- filelist = sorted(filelist)
|
|
861 |
+ resolved = []
|
|
862 |
+ _sysroot_realpath.cache_clear()
|
|
863 |
+ for f in filelist:
|
|
864 |
+ dirname = os.path.dirname(f)
|
|
865 |
+ dirname = _sysroot_realpath(dirname, srcdir)
|
|
866 |
+ if dirname == '.':
|
|
867 |
+ resolved.append(os.path.basename(f))
|
|
868 |
+ else:
|
|
869 |
+ resolved.append(os.path.join(dirname, os.path.basename(f)))
|
|
870 |
+ filelist = sorted(resolved)
|
|
820 | 871 |
|
821 | 872 |
# Now walk the list
|
822 | 873 |
for path in filelist:
|
1 |
+import os
|
|
2 |
+import pytest
|
|
3 |
+ |
|
4 |
+from buildstream import _yaml
|
|
5 |
+ |
|
6 |
+from tests.testutils import cli_integration as cli
|
|
7 |
+ |
|
8 |
+ |
|
9 |
+pytestmark = pytest.mark.integration
|
|
10 |
+ |
|
11 |
+ |
|
12 |
+DATA_DIR = os.path.join(
|
|
13 |
+ os.path.dirname(os.path.realpath(__file__)),
|
|
14 |
+ "project"
|
|
15 |
+)
|
|
16 |
+ |
|
17 |
+ |
|
18 |
+@pytest.mark.datafiles(DATA_DIR)
|
|
19 |
+def test_compose_symlinks_bad_order(cli, tmpdir, datafiles):
|
|
20 |
+ project = str(datafiles)
|
|
21 |
+ checkout = os.path.join(cli.directory, 'checkout')
|
|
22 |
+ element_path = os.path.join(project, 'elements')
|
|
23 |
+ |
|
24 |
+ a_files = os.path.join(project, 'files', 'compose-symlink-order', 'a')
|
|
25 |
+ os.symlink('b', os.path.join(a_files, 'a'),
|
|
26 |
+ target_is_directory=True)
|
|
27 |
+ |
|
28 |
+ result = cli.run(project=project,
|
|
29 |
+ args=['build', 'compose-symlink-order/compose.bst'])
|
|
30 |
+ result.assert_success()
|
|
31 |
+ |
|
32 |
+ result = cli.run(project=project,
|
|
33 |
+ args=['checkout', 'compose-symlink-order/compose.bst',
|
|
34 |
+ checkout])
|
|
35 |
+ result.assert_success()
|
|
36 |
+ |
|
37 |
+ assert os.path.exists(os.path.join(checkout, 'a/c/d'))
|
|
38 |
+ assert os.path.exists(os.path.join(checkout, 'b/c/d'))
|
|
39 |
+ assert os.path.islink(os.path.join(checkout, 'a'))
|
... | ... | @@ -7,6 +7,7 @@ from buildstream import _yaml |
7 | 7 |
|
8 | 8 |
from tests.testutils import cli_integration as cli
|
9 | 9 |
from tests.testutils.integration import walk_dir
|
10 |
+from tests.testutils.site import IS_LINUX, HAVE_BWRAP
|
|
10 | 11 |
|
11 | 12 |
|
12 | 13 |
pytestmark = pytest.mark.integration
|
... | ... | @@ -41,3 +42,19 @@ def test_compose_symlinks(cli, tmpdir, datafiles): |
41 | 42 |
|
42 | 43 |
assert set(walk_dir(checkout)) == set(['/sbin', '/usr', '/usr/sbin',
|
43 | 44 |
'/usr/sbin/init', '/usr/sbin/dummy'])
|
45 |
+ |
|
46 |
+ |
|
47 |
+@pytest.mark.datafiles(DATA_DIR)
|
|
48 |
+@pytest.mark.skipif(not IS_LINUX or not HAVE_BWRAP, reason='Only available on linux with bubblewrap')
|
|
49 |
+def test_compose_absolute_symlinks(cli, tmpdir, datafiles):
|
|
50 |
+ project = str(datafiles)
|
|
51 |
+ checkout = os.path.join(cli.directory, 'checkout')
|
|
52 |
+ element_path = os.path.join(project, 'elements')
|
|
53 |
+ |
|
54 |
+ result = cli.run(project=project, args=['build', 'compose-symlinks/compose-absolute-symlink.bst'])
|
|
55 |
+ result.assert_success()
|
|
56 |
+ |
|
57 |
+ result = cli.run(project=project, args=['checkout', 'compose-symlinks/compose-absolute-symlink.bst', checkout])
|
|
58 |
+ result.assert_success()
|
|
59 |
+ |
|
60 |
+ assert os.readlink(os.path.join(checkout, 'foo')) == 'test/foo'
|
1 |
+kind: import
|
|
2 |
+sources:
|
|
3 |
+- kind: local
|
|
4 |
+ path: files/compose-symlink-order/a
|
1 |
+kind: import
|
|
2 |
+depends:
|
|
3 |
+- filename: compose-symlink-order/a.bst
|
|
4 |
+ |
|
5 |
+sources:
|
|
6 |
+- kind: local
|
|
7 |
+ path: files/compose-symlink-order/b
|
1 |
+kind: compose
|
|
2 |
+depends:
|
|
3 |
+- filename: compose-symlink-order/b.bst
|
|
4 |
+ type: build
|
|
5 |
+ |
|
6 |
+config:
|
|
7 |
+ exclude:
|
|
8 |
+ - devel
|
1 |
+kind: script
|
|
2 |
+ |
|
3 |
+depends:
|
|
4 |
+- filename: base.bst
|
|
5 |
+ type: build
|
|
6 |
+ |
|
7 |
+config:
|
|
8 |
+ commands:
|
|
9 |
+ - |
|
|
10 |
+ mkdir -p "%{install-root}/bar"
|
|
11 |
+ ln -s "/bar" "%{install-root}/foo"
|
1 |
+kind: compose
|
|
2 |
+ |
|
3 |
+depends:
|
|
4 |
+- filename: compose-symlinks/foo-dir.bst
|
|
5 |
+ type: build
|
|
6 |
+- filename: compose-symlinks/a-foo-symlink.bst
|
|
7 |
+ type: build
|
|
8 |
+- filename: compose-symlinks/integration-move-dir.bst
|
|
9 |
+ type: build
|
|
10 |
+ |
|
11 |
+config:
|
|
12 |
+ include-orphans: true
|
|
13 |
+ exclude:
|
|
14 |
+ - dummy
|
1 |
+kind: script
|
|
2 |
+ |
|
3 |
+depends:
|
|
4 |
+- filename: base.bst
|
|
5 |
+ type: build
|
|
6 |
+ |
|
7 |
+config:
|
|
8 |
+ commands:
|
|
9 |
+ - |
|
|
10 |
+ mkdir -p "%{install-root}/foo"
|
|
11 |
+ echo test >"%{install-root}/foo/foo.txt"
|
1 |
+kind: stack
|
|
2 |
+ |
|
3 |
+depends:
|
|
4 |
+- filename: base.bst
|
|
5 |
+ |
|
6 |
+public:
|
|
7 |
+ bst:
|
|
8 |
+ integration-commands:
|
|
9 |
+ - |
|
|
10 |
+ mkdir test
|
|
11 |
+ mv foo test/
|
|
12 |
+ mv bar test/
|
|
13 |
+ ln -s /test/foo /foo
|
|
14 |
+ ln -s /test/bar /bar
|
1 |
+test
|