1 |
Author: grobian |
2 |
Date: 2008-12-01 21:10:53 +0000 (Mon, 01 Dec 2008) |
3 |
New Revision: 12132 |
4 |
|
5 |
Modified: |
6 |
main/branches/prefix/bin/ebuild.sh |
7 |
main/branches/prefix/bin/isolated-functions.sh |
8 |
main/branches/prefix/bin/repoman |
9 |
main/branches/prefix/man/portage.5 |
10 |
main/branches/prefix/pym/_emerge/__init__.py |
11 |
main/branches/prefix/pym/portage/__init__.py |
12 |
main/branches/prefix/pym/portage/dbapi/vartree.py |
13 |
Log: |
14 |
Merged from trunk -r12086:12117 |
15 |
|
16 |
| 12090 | Note that file names in /etc/portage/package.* directories | |
17 |
| zmedico | are sorted ascending alphabetical order before being summed | |
18 |
| | together. | |
19 |
|
20 |
| 12092 | Bug #248782 - Handle permission error in | |
21 |
| zmedico | EbuildFetchonly.execute() if PORTAGE_TMPDIR is not writable. | |
22 |
|
23 |
| 12094 | Bug #248464 - With git, there's never any keyword expansion, | |
24 |
| zmedico | so there's no need to regenerate manifests and all files | |
25 |
| | will be committed in one big commit at the end. | |
26 |
|
27 |
| 12096 | Remove redundant reference to 'mynew' since 'myupdates' | |
28 |
| zmedico | already contains those files. | |
29 |
|
30 |
| 12098 | Eliminate redundant Manifest separation code by combining | |
31 |
| zmedico | mychanged + mynew sooner. | |
32 |
|
33 |
| 12101 | Add some more clarification to the 'ebuild phase exited | |
34 |
| zmedico | unexpectedly' message. | |
35 |
|
36 |
| 12103 | Make Scheduler.merge() bail out early if PORTAGE_TMPDIR is | |
37 |
| zmedico | not properly set. | |
38 |
|
39 |
| 12105 | Bug #205044 - When creating $EBUILD_EXIT_STATUS_FILE, don't | |
40 |
| zmedico | direct output to /dev/null since it should never fail and if | |
41 |
| | it does then the error message might be useful. | |
42 |
|
43 |
| 12107 | Don't direct to /dev/null when creating | |
44 |
| zmedico | $EBUILD_EXIT_STATUS_FILE inside die. | |
45 |
|
46 |
| 12109 | Bug #235642 - Create hardlinks when merging identical files. | |
47 |
| zmedico | This works by using a tuple of (md5, st_size) as a key to a | |
48 |
| | list of hardlink candidates. Multiple candidates are used in | |
49 |
| | case some happen to be merged to separate devices. | |
50 |
|
51 |
| 12111 | In movefile(), ignore the hardlink_candidates parameter when | |
52 |
| zmedico | it's an empty list. | |
53 |
|
54 |
| 12113 | For bug #235642, include the stat mode, uid, and gid bits in | |
55 |
| zmedico | the hardlink key. | |
56 |
|
57 |
| 12115 | Don't call prepare_build_dirs() inside doebuild() when | |
58 |
| zmedico | called for parallel fetching. | |
59 |
|
60 |
| 12117 | Use stat st_dev attributes instead of the older approach. | |
61 |
| zmedico | | |
62 |
|
63 |
|
64 |
Modified: main/branches/prefix/bin/ebuild.sh |
65 |
=================================================================== |
66 |
--- main/branches/prefix/bin/ebuild.sh 2008-12-01 20:59:48 UTC (rev 12131) |
67 |
+++ main/branches/prefix/bin/ebuild.sh 2008-12-01 21:10:53 UTC (rev 12132) |
68 |
@@ -275,9 +275,13 @@ |
69 |
# Ensure that $PWD is sane whenever possible, to protect against |
70 |
# exploitation of insecure search path for python -c in ebuilds. |
71 |
# See bug #239560. |
72 |
-if ! hasq "$EBUILD_PHASE" clean depend help ; then |
73 |
+if ! hasq "$EBUILD_PHASE" clean cleanrm depend help ; then |
74 |
cd "$PORTAGE_BUILDDIR" || \ |
75 |
die "PORTAGE_BUILDDIR does not exist: '$PORTAGE_BUILDDIR'" |
76 |
+else |
77 |
+ # Don't try to create this when it's parent |
78 |
+ # directory doesn't necessarily exist. |
79 |
+ unset EBUILD_EXIT_STATUS_FILE |
80 |
fi |
81 |
|
82 |
#if no perms are specified, dirs/files will have decent defaults |
83 |
@@ -2087,8 +2091,10 @@ |
84 |
exit 1 |
85 |
;; |
86 |
esac |
87 |
- [ -n "${EBUILD_EXIT_STATUS_FILE}" ] && \ |
88 |
- touch "${EBUILD_EXIT_STATUS_FILE}" &>/dev/null |
89 |
+ if [ -n "$EBUILD_EXIT_STATUS_FILE" ] ; then |
90 |
+ > "$EBUILD_EXIT_STATUS_FILE" || \ |
91 |
+ die "failed to create '$EBUILD_EXIT_STATUS_FILE'" |
92 |
+ fi |
93 |
} |
94 |
|
95 |
[[ -n $EBUILD_SH_ARGS ]] && ebuild_main |
96 |
|
97 |
Modified: main/branches/prefix/bin/isolated-functions.sh |
98 |
=================================================================== |
99 |
--- main/branches/prefix/bin/isolated-functions.sh 2008-12-01 20:59:48 UTC (rev 12131) |
100 |
+++ main/branches/prefix/bin/isolated-functions.sh 2008-12-01 21:10:53 UTC (rev 12132) |
101 |
@@ -128,22 +128,8 @@ |
102 |
done |
103 |
fi |
104 |
|
105 |
- [[ -n ${PORTAGE_LOG_FILE} ]] \ |
106 |
- && eerror "build log: '${PORTAGE_LOG_FILE}'" |
107 |
- if [ -f "${T}/environment" ] ; then |
108 |
- eerror "ebuild environment: '${T}/environment'" |
109 |
- elif [ -d "${T}" ] ; then |
110 |
- { |
111 |
- set |
112 |
- export |
113 |
- } > "${T}/die.env" |
114 |
- eerror "ebuild environment: '${T}/die.env'" |
115 |
- fi |
116 |
- eerror "S: '${S}'" |
117 |
+ [ -n "$EBUILD_EXIT_STATUS_FILE" ] && > "$EBUILD_EXIT_STATUS_FILE" |
118 |
|
119 |
- [ -n "${EBUILD_EXIT_STATUS_FILE}" ] && \ |
120 |
- touch "${EBUILD_EXIT_STATUS_FILE}" &>/dev/null |
121 |
- |
122 |
# subshell die support |
123 |
kill -s SIGTERM ${EBUILD_MASTER_PID} |
124 |
exit 1 |
125 |
|
126 |
Modified: main/branches/prefix/bin/repoman |
127 |
=================================================================== |
128 |
--- main/branches/prefix/bin/repoman 2008-12-01 20:59:48 UTC (rev 12131) |
129 |
+++ main/branches/prefix/bin/repoman 2008-12-01 21:10:53 UTC (rev 12132) |
130 |
@@ -1758,23 +1758,14 @@ |
131 |
# Manifests need to be regenerated after all other commits, so don't commit |
132 |
# them now even if they have changed. |
133 |
mymanifests = set() |
134 |
- changed_set = set() |
135 |
- new_set = set() |
136 |
- for f in mychanged: |
137 |
+ myupdates = set() |
138 |
+ for f in mychanged + mynew: |
139 |
if "Manifest" == os.path.basename(f): |
140 |
mymanifests.add(f) |
141 |
else: |
142 |
- changed_set.add(f) |
143 |
- for f in mynew: |
144 |
- if "Manifest" == os.path.basename(f): |
145 |
- mymanifests.add(f) |
146 |
- else: |
147 |
- new_set.add(f) |
148 |
- mychanged = list(changed_set) |
149 |
- mynew = list(new_set) |
150 |
+ myupdates.add(f) |
151 |
+ myupdates = list(myupdates) |
152 |
mymanifests = list(mymanifests) |
153 |
- del changed_set, new_set |
154 |
- myupdates = mychanged + mynew |
155 |
myheaders = [] |
156 |
mydirty = [] |
157 |
headerstring = "'\$(Header|Id)" |
158 |
@@ -1795,8 +1786,16 @@ |
159 |
if myout[0] == 0: |
160 |
myheaders.append(myfile) |
161 |
|
162 |
- print "*",green(str(len(myupdates))),"files being committed...",green(str(len(myheaders))),"have headers that will change." |
163 |
- print "*","Files with headers will cause the manifests to be made and recommited." |
164 |
+ print "* %s files being committed..." % green(str(len(myupdates))), |
165 |
+ if vcs == 'git': |
166 |
+ # With git, there's never any keyword expansion, so there's |
167 |
+ # no need to regenerate manifests and all files will be |
168 |
+ # committed in one big commit at the end. |
169 |
+ print |
170 |
+ else: |
171 |
+ print "%s have headers that will change." % green(str(len(myheaders))) |
172 |
+ print "* Files with headers will cause the " + \ |
173 |
+ "manifests to be made and recommited." |
174 |
logging.info("myupdates:", str(myupdates)) |
175 |
logging.info("myheaders:", str(myheaders)) |
176 |
|
177 |
@@ -1844,7 +1843,7 @@ |
178 |
commitmessage += ", RepoMan options: --force" |
179 |
commitmessage += ")" |
180 |
|
181 |
- if myupdates or myremoved: |
182 |
+ if vcs != 'git' and (myupdates or myremoved): |
183 |
myfiles = myupdates + myremoved |
184 |
if not myheaders and "sign" not in repoman_settings.features: |
185 |
myfiles += mymanifests |
186 |
@@ -1937,8 +1936,8 @@ |
187 |
write_atomic(x, "".join(mylines)) |
188 |
|
189 |
manifest_commit_required = True |
190 |
- if myupdates or myremoved or mynew: |
191 |
- myfiles=myupdates+myremoved+mynew |
192 |
+ if vcs != 'git' and (myupdates or myremoved): |
193 |
+ myfiles = myupdates + myremoved |
194 |
for x in range(len(myfiles)-1, -1, -1): |
195 |
if myfiles[x].count("/") < 4-repolevel: |
196 |
del myfiles[x] |
197 |
@@ -2059,8 +2058,14 @@ |
198 |
portage.writemsg("!!! Disabled FEATURES='sign'\n") |
199 |
signed = False |
200 |
|
201 |
- if manifest_commit_required or signed: |
202 |
+ if vcs == 'git' or manifest_commit_required or signed: |
203 |
|
204 |
+ myfiles = mymanifests[:] |
205 |
+ if vcs == 'git': |
206 |
+ myfiles += myupdates |
207 |
+ myfiles += myremoved |
208 |
+ myfiles.sort() |
209 |
+ |
210 |
fd, commitmessagefile = tempfile.mkstemp(".repoman.msg") |
211 |
mymsg = os.fdopen(fd, "w") |
212 |
mymsg.write(commitmessage) |
213 |
@@ -2075,7 +2080,7 @@ |
214 |
commit_cmd.append("commit") |
215 |
commit_cmd.extend(vcs_local_opts) |
216 |
commit_cmd.extend(["-F", commitmessagefile]) |
217 |
- commit_cmd.extend(f.lstrip("./") for f in mymanifests) |
218 |
+ commit_cmd.extend(f.lstrip("./") for f in myfiles) |
219 |
|
220 |
try: |
221 |
if options.pretend: |
222 |
|
223 |
Modified: main/branches/prefix/man/portage.5 |
224 |
=================================================================== |
225 |
--- main/branches/prefix/man/portage.5 2008-12-01 20:59:48 UTC (rev 12131) |
226 |
+++ main/branches/prefix/man/portage.5 2008-12-01 21:10:53 UTC (rev 12132) |
227 |
@@ -371,13 +371,14 @@ |
228 |
.BR /etc/portage/ |
229 |
Any file in this directory that begins with "package." can be more than just a |
230 |
flat file. If it is a directory, then all the files in that directory will be |
231 |
-summed together as if it were a single file. |
232 |
+sorted in ascending alphabetical order by file name and summed together as if |
233 |
+it were a single file. |
234 |
|
235 |
.I Example: |
236 |
.nf |
237 |
-/etc/portage/package.keywords/kde |
238 |
/etc/portage/package.keywords/common |
239 |
/etc/portage/package.keywords/e17 |
240 |
+/etc/portage/package.keywords/kde |
241 |
.fi |
242 |
.RS |
243 |
.TP |
244 |
|
245 |
Modified: main/branches/prefix/pym/_emerge/__init__.py |
246 |
=================================================================== |
247 |
--- main/branches/prefix/pym/_emerge/__init__.py 2008-12-01 20:59:48 UTC (rev 12131) |
248 |
+++ main/branches/prefix/pym/_emerge/__init__.py 2008-12-01 21:10:53 UTC (rev 12132) |
249 |
@@ -1632,7 +1632,12 @@ |
250 |
settings = self.settings |
251 |
global_tmpdir = settings["PORTAGE_TMPDIR"] |
252 |
from tempfile import mkdtemp |
253 |
- private_tmpdir = mkdtemp("", "._portage_fetch_.", global_tmpdir) |
254 |
+ try: |
255 |
+ private_tmpdir = mkdtemp("", "._portage_fetch_.", global_tmpdir) |
256 |
+ except OSError, e: |
257 |
+ if e.errno != portage.exception.PermissionDenied.errno: |
258 |
+ raise |
259 |
+ raise portage.exception.PermissionDenied(global_tmpdir) |
260 |
settings["PORTAGE_TMPDIR"] = private_tmpdir |
261 |
settings.backup_changes("PORTAGE_TMPDIR") |
262 |
try: |
263 |
@@ -9734,6 +9739,22 @@ |
264 |
|
265 |
for root in self.trees: |
266 |
root_config = self.trees[root]["root_config"] |
267 |
+ |
268 |
+ # Even for --pretend --fetch mode, PORTAGE_TMPDIR is required |
269 |
+ # since it might spawn pkg_nofetch which requires PORTAGE_BUILDDIR |
270 |
+ # for ensuring sane $PWD (bug #239560) and storing elog messages. |
271 |
+ tmpdir = root_config.settings.get("PORTAGE_TMPDIR", "") |
272 |
+ if not tmpdir or not os.path.isdir(tmpdir): |
273 |
+ msg = "The directory specified in your " + \ |
274 |
+ "PORTAGE_TMPDIR variable, '%s', " % tmpdir + \ |
275 |
+ "does not exist. Please create this " + \ |
276 |
+ "directory or correct your PORTAGE_TMPDIR setting." |
277 |
+ msg = textwrap.wrap(msg, 70) |
278 |
+ out = portage.output.EOutput() |
279 |
+ for l in msg: |
280 |
+ out.eerror(l) |
281 |
+ return 1 |
282 |
+ |
283 |
if self._background: |
284 |
root_config.settings.unlock() |
285 |
root_config.settings["PORTAGE_BACKGROUND"] = "1" |
286 |
|
287 |
Modified: main/branches/prefix/pym/portage/__init__.py |
288 |
=================================================================== |
289 |
--- main/branches/prefix/pym/portage/__init__.py 2008-12-01 20:59:48 UTC (rev 12131) |
290 |
+++ main/branches/prefix/pym/portage/__init__.py 2008-12-01 21:10:53 UTC (rev 12132) |
291 |
@@ -5190,9 +5190,20 @@ |
292 |
"is known to be triggered " + \ |
293 |
"by things such as failed variable " + \ |
294 |
"assignments (bug #190128) or bad substitution " + \ |
295 |
- "errors (bug #200313). This behavior may also be " + \ |
296 |
- "triggered by a corrupt bash binary or a hardware " + \ |
297 |
- "problem such as memory or cpu malfunction." |
298 |
+ "errors (bug #200313). Normally, before exiting, bash should " + \ |
299 |
+ "have displayed an error message above. If bash did not " + \ |
300 |
+ "produce an error message above, it's possible " + \ |
301 |
+ "that the ebuild has called `exit` when it " + \ |
302 |
+ "should have called `die` instead. This behavior may also " + \ |
303 |
+ "be triggered by a corrupt bash binary or a hardware " + \ |
304 |
+ "problem such as memory or cpu malfunction. If the problem is not " + \ |
305 |
+ "reproducible or it appears to occur randomly, then it is likely " + \ |
306 |
+ "to be triggered by a hardware problem. " + \ |
307 |
+ "If you suspect a hardware problem then you should " + \ |
308 |
+ "try some basic hardware diagnostics such as memtest. " + \ |
309 |
+ "Please do not report this as a bug unless it is consistently " + \ |
310 |
+ "reproducible and you are sure that your bash binary and hardware " + \ |
311 |
+ "are functioning properly." |
312 |
return msg |
313 |
|
314 |
def _doebuild_exit_status_check_and_log(settings, mydo, retval): |
315 |
@@ -5331,6 +5342,9 @@ |
316 |
fetchall = 1 |
317 |
mydo = "fetch" |
318 |
|
319 |
+ parallel_fetchonly = mydo in ("fetch", "fetchall") and \ |
320 |
+ "PORTAGE_PARALLEL_FETCHONLY" in mysettings |
321 |
+ |
322 |
if mydo not in clean_phases and not os.path.exists(myebuild): |
323 |
writemsg("!!! doebuild: %s not found for %s\n" % (myebuild, mydo), |
324 |
noiselevel=-1) |
325 |
@@ -5564,7 +5578,7 @@ |
326 |
|
327 |
# Build directory creation isn't required for any of these. |
328 |
have_build_dirs = False |
329 |
- if not mydo in ("digest", "help", "manifest"): |
330 |
+ if not parallel_fetchonly and mydo not in ("digest", "help", "manifest"): |
331 |
mystatus = prepare_build_dirs(myroot, mysettings, cleanup) |
332 |
if mystatus: |
333 |
return mystatus |
334 |
@@ -6005,7 +6019,8 @@ |
335 |
raise portage.exception.PortageException( |
336 |
"mv '%s' '%s'" % (src, dest)) |
337 |
|
338 |
-def movefile(src,dest,newmtime=None,sstat=None,mysettings=None): |
339 |
+def movefile(src, dest, newmtime=None, sstat=None, mysettings=None, |
340 |
+ hardlink_candidates=None): |
341 |
"""moves a file from src to dest, preserving all permissions and attributes; mtime will |
342 |
be preserved even when moving across filesystems. Returns true on success and false on |
343 |
failure. Move is atomic.""" |
344 |
@@ -6077,8 +6092,44 @@ |
345 |
print "!!!",e |
346 |
return None |
347 |
|
348 |
+ hardlinked = False |
349 |
+ # Since identical files might be merged to multiple filesystems, |
350 |
+ # so os.link() calls might fail for some paths, so try them all. |
351 |
+ # For atomic replacement, first create the link as a temp file |
352 |
+ # and them use os.rename() to replace the destination. |
353 |
+ if hardlink_candidates: |
354 |
+ head, tail = os.path.split(dest) |
355 |
+ hardlink_tmp = os.path.join(head, ".%s._portage_merge_.%s" % \ |
356 |
+ (tail, os.getpid())) |
357 |
+ try: |
358 |
+ os.unlink(hardlink_tmp) |
359 |
+ except OSError, e: |
360 |
+ if e.errno != errno.ENOENT: |
361 |
+ writemsg("!!! Failed to remove hardlink temp file: %s\n" % \ |
362 |
+ (hardlink_tmp,), noiselevel=-1) |
363 |
+ writemsg("!!! %s\n" % (e,), noiselevel=-1) |
364 |
+ return None |
365 |
+ del e |
366 |
+ for hardlink_src in hardlink_candidates: |
367 |
+ try: |
368 |
+ os.link(hardlink_src, hardlink_tmp) |
369 |
+ except OSError: |
370 |
+ continue |
371 |
+ else: |
372 |
+ try: |
373 |
+ os.rename(hardlink_tmp, dest) |
374 |
+ except OSError, e: |
375 |
+ writemsg("!!! Failed to rename %s to %s\n" % \ |
376 |
+ (hardlink_tmp, dest), noiselevel=-1) |
377 |
+ writemsg("!!! %s\n" % (e,), noiselevel=-1) |
378 |
+ return None |
379 |
+ hardlinked = True |
380 |
+ break |
381 |
+ |
382 |
renamefailed=1 |
383 |
- if sstat[stat.ST_DEV]==dstat[stat.ST_DEV] or selinux_enabled: |
384 |
+ if hardlinked: |
385 |
+ renamefailed = False |
386 |
+ if not hardlinked and (selinux_enabled or sstat.st_dev == dstat.st_dev): |
387 |
try: |
388 |
if selinux_enabled: |
389 |
ret=selinux.secure_rename(src,dest) |
390 |
@@ -6139,11 +6190,14 @@ |
391 |
return None |
392 |
|
393 |
try: |
394 |
- if newmtime is not None: |
395 |
- os.utime(dest, (newmtime, newmtime)) |
396 |
+ if hardlinked: |
397 |
+ newmtime = long(os.stat(dest).st_mtime) |
398 |
else: |
399 |
- os.utime(dest, (sstat.st_atime, sstat.st_mtime)) |
400 |
- newmtime = long(sstat.st_mtime) |
401 |
+ if newmtime is not None: |
402 |
+ os.utime(dest, (newmtime, newmtime)) |
403 |
+ else: |
404 |
+ os.utime(dest, (sstat.st_atime, sstat.st_mtime)) |
405 |
+ newmtime = long(sstat.st_mtime) |
406 |
except OSError: |
407 |
# The utime can fail here with EPERM even though the move succeeded. |
408 |
# Instead of failing, use stat to return the mtime if possible. |
409 |
|
410 |
Modified: main/branches/prefix/pym/portage/dbapi/vartree.py |
411 |
=================================================================== |
412 |
--- main/branches/prefix/pym/portage/dbapi/vartree.py 2008-12-01 20:59:48 UTC (rev 12131) |
413 |
+++ main/branches/prefix/pym/portage/dbapi/vartree.py 2008-12-01 21:10:53 UTC (rev 12132) |
414 |
@@ -2282,6 +2282,7 @@ |
415 |
self._contents_inodes = None |
416 |
self._contents_basenames = None |
417 |
self._linkmap_broken = False |
418 |
+ self._md5_merge_map = {} |
419 |
|
420 |
def lockdb(self): |
421 |
if self._lock_vdb: |
422 |
@@ -3895,6 +3896,7 @@ |
423 |
if self.mergeme(srcroot, destroot, outfile, None, |
424 |
secondhand, cfgfiledict, mymtime): |
425 |
return 1 |
426 |
+ self._md5_merge_map.clear() |
427 |
|
428 |
#restore umask |
429 |
os.umask(prevmask) |
430 |
@@ -4296,9 +4298,18 @@ |
431 |
# whether config protection or not, we merge the new file the |
432 |
# same way. Unless moveme=0 (blocking directory) |
433 |
if moveme: |
434 |
- mymtime = movefile(mysrc, mydest, newmtime=thismtime, sstat=mystat, mysettings=self.settings) |
435 |
+ hardlink_key = (mymd5, mystat.st_size, |
436 |
+ mystat.st_mode, mystat.st_uid, mystat.st_gid) |
437 |
+ hardlink_candidates = self._md5_merge_map.get(hardlink_key) |
438 |
+ if hardlink_candidates is None: |
439 |
+ hardlink_candidates = [] |
440 |
+ self._md5_merge_map[hardlink_key] = hardlink_candidates |
441 |
+ mymtime = movefile(mysrc, mydest, newmtime=thismtime, |
442 |
+ sstat=mystat, mysettings=self.settings, |
443 |
+ hardlink_candidates=hardlink_candidates) |
444 |
if mymtime is None: |
445 |
return 1 |
446 |
+ hardlink_candidates.append(mydest) |
447 |
zing = ">>>" |
448 |
|
449 |
if mymtime != None: |