Gentoo Archives: gentoo-commits

From: Zac Medico <zmedico@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] proj/portage:master commit in: bin/, pym/portage/tests/bin/
Date: Mon, 08 Jan 2018 05:55:15
Message-Id: 1515390468.d459f05ff71f8ccb8deea2d43c2992a8ae0b0d52.zmedico@gentoo
1 commit: d459f05ff71f8ccb8deea2d43c2992a8ae0b0d52
2 Author: Zac Medico <zmedico <AT> gentoo <DOT> org>
3 AuthorDate: Fri Dec 29 20:15:37 2017 +0000
4 Commit: Zac Medico <zmedico <AT> gentoo <DOT> org>
5 CommitDate: Mon Jan 8 05:47:48 2018 +0000
6 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=d459f05f
7
8 bin/doins.py: implement install -p option (bug 642632)
9
10 Bug: https://bugs.gentoo.org/642632
11 Reviewed-by: Alec Warner <antarus <AT> gentoo.org>
12 Reviewed-by: Mike Gilbert <floppym <AT> gentoo.org>
13
14 bin/doins.py | 34 +++++++++++++++++++++++++++++-----
15 pym/portage/tests/bin/test_doins.py | 6 ++++--
16 2 files changed, 33 insertions(+), 7 deletions(-)
17
18 diff --git a/bin/doins.py b/bin/doins.py
19 index 92e450979..9e6566097 100644
20 --- a/bin/doins.py
21 +++ b/bin/doins.py
22 @@ -107,6 +107,7 @@ def _parse_install_options(
23 parser.add_argument('-g', '--group', default=-1, type=_parse_group)
24 parser.add_argument('-o', '--owner', default=-1, type=_parse_user)
25 parser.add_argument('-m', '--mode', default=0o755, type=_parse_mode)
26 + parser.add_argument('-p', '--preserve-timestamps', action='store_true')
27 split_options = shlex.split(options)
28 namespace, remaining = parser.parse_known_args(split_options)
29 # Because parsing '--mode' option is partially supported. If unknown
30 @@ -139,6 +140,24 @@ def _set_attributes(options, path):
31 os.chmod(path, options.mode)
32
33
34 +def _set_timestamps(source_stat, dest):
35 + """Apply timestamps from source_stat to dest.
36 +
37 + Args:
38 + source_stat: stat result for the source file.
39 + dest: path to the dest file.
40 + """
41 + os.utime(dest, (source_stat.st_atime, source_stat.st_mtime))
42 +
43 +
44 +if sys.version_info >= (3, 3):
45 + def _set_timestamps_ns(source_stat, dest):
46 + os.utime(dest, ns=(source_stat.st_atime_ns, source_stat.st_mtime_ns))
47 +
48 + _set_timestamps_ns.__doc__ = _set_timestamps.__doc__
49 + _set_timestamps = _set_timestamps_ns
50 +
51 +
52 class _InsInProcessInstallRunner(object):
53 """Implements `install` command behavior running in a process."""
54
55 @@ -168,7 +187,9 @@ class _InsInProcessInstallRunner(object):
56 True on success, otherwise False.
57 """
58 dest = os.path.join(dest_dir, os.path.basename(source))
59 - if not self._is_install_allowed(source, dest):
60 + # Raise an exception if stat(source) fails, intentionally.
61 + sstat = os.stat(source)
62 + if not self._is_install_allowed(source, sstat, dest):
63 return False
64
65 # To emulate the `install` command, remove the dest file in
66 @@ -187,6 +208,8 @@ class _InsInProcessInstallRunner(object):
67 movefile._copyxattr(
68 source, dest,
69 exclude=self._xattr_exclude)
70 + if self._parsed_options.preserve_timestamps:
71 + _set_timestamps(sstat, dest)
72 except Exception:
73 logging.exception(
74 'Failed to copy file: '
75 @@ -195,22 +218,23 @@ class _InsInProcessInstallRunner(object):
76 return False
77 return True
78
79 - def _is_install_allowed(self, source, dest):
80 + def _is_install_allowed(self, source, source_stat, dest):
81 """Returns if installing source into dest should work.
82
83 This is to keep compatibility with the `install` command.
84
85 Args:
86 source: path to the source file.
87 + source_stat: stat result for the source file, using stat()
88 + rather than lstat(), in order to match the `install`
89 + command
90 dest: path to the dest file.
91
92 Returns:
93 True if it should succeed.
94 """
95 # To match `install` command, use stat() for source, while
96 - # lstat() for dest. Raise an exception if stat(source) fails,
97 - # intentionally.
98 - source_stat = os.stat(source)
99 + # lstat() for dest.
100 try:
101 dest_lstat = os.lstat(dest)
102 except OSError as e:
103
104 diff --git a/pym/portage/tests/bin/test_doins.py b/pym/portage/tests/bin/test_doins.py
105 index 14d7adfa6..9b6c55d85 100644
106 --- a/pym/portage/tests/bin/test_doins.py
107 +++ b/pym/portage/tests/bin/test_doins.py
108 @@ -38,13 +38,15 @@ class DoIns(setup_env.BinTestCase):
109 self.init()
110 try:
111 env = setup_env.env
112 - env['INSOPTIONS'] = '-m0644'
113 + env['INSOPTIONS'] = '-pm0644'
114 with open(os.path.join(env['S'], 'test'), 'w'):
115 pass
116 doins('test')
117 st = os.lstat(env['D'] + '/test')
118 if stat.S_IMODE(st.st_mode) != 0o644:
119 raise tests.TestCase.failureException
120 + if os.stat(os.path.join(env['S'], 'test')).st_mtime != st.st_mtime:
121 + raise tests.TestCase.failureException
122 finally:
123 self.cleanup()
124
125 @@ -145,7 +147,7 @@ class DoIns(setup_env.BinTestCase):
126 env = setup_env.env
127 # Use an option which doins.py does not know.
128 # Then, fallback to `install` command is expected.
129 - env['INSOPTIONS'] = '-p'
130 + env['INSOPTIONS'] = '-b'
131 with open(os.path.join(env['S'], 'test'), 'w'):
132 pass
133 doins('test')