/
actions.py
4044 lines (3583 loc) · 146 KB
/
actions.py
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Copyright 1999-2024 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
import collections
import logging
import operator
import platform
import re
import signal
import subprocess
import sys
import tempfile
import textwrap
import time
import warnings
from itertools import chain
import portage
portage.proxy.lazyimport.lazyimport(
globals(),
"portage.dbapi._similar_name_search:similar_name_search",
"portage.debug",
"portage.news:count_unread_news,display_news_notifications",
"portage.util._get_vm_info:get_vm_info",
"portage.util.locale:check_locale",
"portage.emaint.modules.sync.sync:SyncRepos",
"_emerge.chk_updated_cfg_files:chk_updated_cfg_files",
"_emerge.help:emerge_help",
"_emerge.post_emerge:display_news_notification,post_emerge",
"_emerge.stdout_spinner:stdout_spinner",
)
from portage import os
from portage import shutil
from portage import _encodings, _unicode_decode
from portage.binrepo.config import BinRepoConfigLoader
from portage.const import BINREPOS_CONF_FILE, _DEPCLEAN_LIB_CHECK_DEFAULT
from portage.dbapi.dep_expand import dep_expand
from portage.dbapi._expand_new_virt import expand_new_virt
from portage.dbapi.IndexedPortdb import IndexedPortdb
from portage.dbapi.IndexedVardb import IndexedVardb
from portage.dep import Atom, _repo_separator, _slot_separator
from portage.dep.libc import find_libc_deps
from portage.exception import (
InvalidAtom,
InvalidData,
ParseError,
GPGException,
InvalidBinaryPackageFormat,
)
from portage.output import (
colorize,
create_color_func,
darkgreen,
red,
xtermTitle,
)
good = create_color_func("GOOD")
bad = create_color_func("BAD")
warn = create_color_func("WARN")
from portage.package.ebuild._ipc.QueryCommand import QueryCommand
from portage.package.ebuild.fetch import _hide_url_passwd
from portage._sets import load_default_config, SETPREFIX
from portage._sets.base import InternalPackageSet
from portage.util import (
cmp_sort_key,
normalize_path,
writemsg,
varexpand,
writemsg_level,
writemsg_stdout,
)
from portage.util.digraph import digraph
from portage.util.path import first_existing
from portage.util.SlotObject import SlotObject
from portage.util._async.run_main_scheduler import run_main_scheduler
from portage.util._async.SchedulerInterface import SchedulerInterface
from portage.util._eventloop.global_event_loop import global_event_loop
from portage._global_updates import _global_updates
from portage.sync.old_tree_timestamp import old_tree_timestamp_warn
from portage.localization import _
from portage.metadata import action_metadata
from portage.emaint.main import print_results
from portage.gpg import GPG
from portage.binpkg import get_binpkg_format
from _emerge.clear_caches import clear_caches
from _emerge.create_depgraph_params import create_depgraph_params
from _emerge.Dependency import Dependency
from _emerge.depgraph import backtrack_depgraph, depgraph, resume_depgraph
from _emerge.emergelog import emergelog
from _emerge.is_valid_package_atom import is_valid_package_atom
from _emerge.main import profile_check
from _emerge.MetadataRegen import MetadataRegen
from _emerge.Package import Package
from _emerge.RootConfig import RootConfig
from _emerge.Scheduler import Scheduler
from _emerge.search import search
from _emerge.SetArg import SetArg
from _emerge.show_invalid_depstring_notice import show_invalid_depstring_notice
from _emerge.unmerge import unmerge
from _emerge.UnmergeDepPriority import UnmergeDepPriority
from _emerge.UseFlagDisplay import pkg_use_display
from _emerge.UserQuery import UserQuery
def action_build(
emerge_config,
trees=DeprecationWarning,
mtimedb=DeprecationWarning,
myopts=DeprecationWarning,
myaction=DeprecationWarning,
myfiles=DeprecationWarning,
spinner=None,
):
if not isinstance(emerge_config, _emerge_config):
warnings.warn(
"_emerge.actions.action_build() now expects "
"an _emerge_config instance as the first parameter",
DeprecationWarning,
stacklevel=2,
)
emerge_config = load_emerge_config(
action=myaction, args=myfiles, trees=trees, opts=myopts
)
adjust_configs(emerge_config.opts, emerge_config.trees)
settings, trees, mtimedb = emerge_config
myopts = emerge_config.opts
myaction = emerge_config.action
myfiles = emerge_config.args
if "--usepkgonly" not in myopts:
old_tree_timestamp_warn(settings["PORTDIR"], settings)
# It's best for config updates in /etc/portage to be processed
# before we get here, so warn if they're not (bug #267103).
chk_updated_cfg_files(settings["EROOT"], ["/etc/portage"])
quickpkg_root = (
normalize_path(
os.path.abspath(
emerge_config.opts.get(
"--quickpkg-direct-root",
emerge_config.running_config.settings["ROOT"],
)
)
).rstrip(os.path.sep)
+ os.path.sep
)
quickpkg_direct = (
"--usepkg" in emerge_config.opts
and emerge_config.opts.get("--quickpkg-direct", "n") == "y"
and emerge_config.target_config.settings["ROOT"] != quickpkg_root
)
if "--getbinpkg" in emerge_config.opts or quickpkg_direct:
kwargs = {}
if quickpkg_direct:
if quickpkg_root == emerge_config.running_config.settings["ROOT"]:
quickpkg_vardb = emerge_config.running_config.trees["vartree"].dbapi
else:
quickpkg_settings = portage.config(
config_root=emerge_config.target_config.settings[
"PORTAGE_CONFIGROOT"
],
target_root=quickpkg_root,
env=emerge_config.target_config.settings.backupenv.copy(),
sysroot=emerge_config.target_config.settings["SYSROOT"],
eprefix=emerge_config.target_config.settings["EPREFIX"],
)
quickpkg_vardb = portage.vartree(settings=quickpkg_settings).dbapi
kwargs["add_repos"] = (quickpkg_vardb,)
try:
kwargs["pretend"] = "--pretend" in emerge_config.opts
emerge_config.target_config.trees["bintree"].populate(
getbinpkgs="--getbinpkg" in emerge_config.opts, **kwargs
)
except ParseError as e:
writemsg(f"\n\n!!!{e}.\nSee make.conf(5) for more info.\n", noiselevel=-1)
return 1
# validate the state of the resume data
# so that we can make assumptions later.
for k in ("resume", "resume_backup"):
if k not in mtimedb:
continue
resume_data = mtimedb[k]
if not isinstance(resume_data, dict):
del mtimedb[k]
continue
mergelist = resume_data.get("mergelist")
if not isinstance(mergelist, list):
del mtimedb[k]
continue
for x in mergelist:
if not (isinstance(x, list) and len(x) == 4):
continue
pkg_type, pkg_root, pkg_key, pkg_action = x
if pkg_root not in trees:
# Current $ROOT setting differs,
# so the list must be stale.
mergelist = None
break
if not mergelist:
del mtimedb[k]
continue
resume_opts = resume_data.get("myopts")
if not isinstance(resume_opts, (dict, list)):
del mtimedb[k]
continue
favorites = resume_data.get("favorites")
if not isinstance(favorites, list):
del mtimedb[k]
continue
resume = False
if "--resume" in myopts and ("resume" in mtimedb or "resume_backup" in mtimedb):
resume = True
if "resume" not in mtimedb:
mtimedb["resume"] = mtimedb["resume_backup"]
del mtimedb["resume_backup"]
mtimedb.commit()
# "myopts" is a list for backward compatibility.
resume_opts = mtimedb["resume"].get("myopts", [])
if isinstance(resume_opts, list):
resume_opts = {k: True for k in resume_opts}
for opt in ("--ask", "--color", "--skipfirst", "--tree"):
resume_opts.pop(opt, None)
# Current options always override resume_opts.
resume_opts.update(myopts)
myopts.clear()
myopts.update(resume_opts)
if "--debug" in myopts:
writemsg_level(f"myopts {myopts}\n")
# Adjust config according to options of the command being resumed.
for myroot in trees:
mysettings = trees[myroot]["vartree"].settings
mysettings.unlock()
adjust_config(myopts, mysettings)
mysettings.lock()
del myroot, mysettings
ldpath_mtimes = mtimedb["ldpath"]
favorites = []
buildpkgonly = "--buildpkgonly" in myopts
pretend = "--pretend" in myopts
fetchonly = "--fetchonly" in myopts or "--fetch-all-uri" in myopts
ask = "--ask" in myopts
enter_invalid = "--ask-enter-invalid" in myopts
nodeps = "--nodeps" in myopts
oneshot = "--oneshot" in myopts or "--onlydeps" in myopts
tree = "--tree" in myopts
if nodeps and tree:
tree = False
del myopts["--tree"]
portage.writemsg(
colorize("WARN", " * ") + "--tree is broken with --nodeps. Disabling...\n"
)
debug = "--debug" in myopts
verbose = "--verbose" in myopts
quiet = "--quiet" in myopts
myparams = create_depgraph_params(myopts, myaction)
mergelist_shown = False
if pretend or fetchonly:
mtimedb.make_readonly()
if "--digest" in myopts or "digest" in settings.features:
if "--digest" in myopts:
msg = "The --digest option"
else:
msg = "The FEATURES=digest setting"
msg += (
" can prevent corruption from being"
+ " noticed. The `repoman manifest` command is the preferred"
+ " way to generate manifests and it is capable of doing an"
+ " entire repository or category at once."
)
prefix = bad(" * ")
writemsg(prefix + "\n")
for line in textwrap.wrap(msg, 72):
writemsg(f"{prefix}{line}\n")
writemsg(prefix + "\n")
if resume:
favorites = mtimedb["resume"].get("favorites")
if not isinstance(favorites, list):
favorites = []
resume_data = mtimedb["resume"]
mergelist = resume_data["mergelist"]
if mergelist and "--skipfirst" in myopts:
for i, task in enumerate(mergelist):
if isinstance(task, list) and task and task[-1] == "merge":
del mergelist[i]
break
success = False
mydepgraph = None
try:
success, mydepgraph, dropped_tasks = resume_depgraph(
settings, trees, mtimedb, myopts, myparams, spinner
)
except (portage.exception.PackageNotFound, depgraph.UnsatisfiedResumeDep) as e:
if isinstance(e, depgraph.UnsatisfiedResumeDep):
mydepgraph = e.depgraph
from portage.output import EOutput
out = EOutput()
resume_data = mtimedb["resume"]
mergelist = resume_data.get("mergelist")
if not isinstance(mergelist, list):
mergelist = []
if mergelist and debug or (verbose and not quiet):
out.eerror("Invalid resume list:")
out.eerror("")
indent = " "
for task in mergelist:
if isinstance(task, list):
out.eerror(indent + str(tuple(task)))
out.eerror("")
if isinstance(e, depgraph.UnsatisfiedResumeDep):
out.eerror(
"One or more packages are either masked or "
+ "have missing dependencies:"
)
out.eerror("")
indent = " "
for dep in e.value:
if dep.atom is None:
out.eerror(indent + "Masked package:")
out.eerror(2 * indent + str(dep.parent))
out.eerror("")
else:
out.eerror(indent + str(dep.atom) + " pulled in by:")
out.eerror(2 * indent + str(dep.parent))
out.eerror("")
msg = (
"The resume list contains packages "
+ "that are either masked or have "
+ "unsatisfied dependencies. "
+ "Please restart/continue "
+ "the operation manually, or use --skipfirst "
+ "to skip the first package in the list and "
+ "any other packages that may be "
+ "masked or have missing dependencies."
)
for line in textwrap.wrap(msg, 72):
out.eerror(line)
elif isinstance(e, portage.exception.PackageNotFound):
out.eerror("An expected package is " + f"not available: {str(e)}")
out.eerror("")
msg = (
"The resume list contains one or more "
+ "packages that are no longer "
+ "available. Please restart/continue "
+ "the operation manually."
)
for line in textwrap.wrap(msg, 72):
out.eerror(line)
if success:
if dropped_tasks:
portage.writemsg(
"!!! One or more packages have been "
+ "dropped due to\n"
+ "!!! masking or unsatisfied dependencies:\n\n",
noiselevel=-1,
)
for task, atoms in dropped_tasks.items():
if not atoms:
writemsg(
f" {task} is masked or unavailable\n",
noiselevel=-1,
)
else:
writemsg(
f" {task} requires {', '.join(atoms)}\n",
noiselevel=-1,
)
portage.writemsg("\n", noiselevel=-1)
del dropped_tasks
else:
if mydepgraph is not None:
mydepgraph.display_problems()
if not (ask or pretend):
# delete the current list and also the backup
# since it's probably stale too.
for k in ("resume", "resume_backup"):
mtimedb.pop(k, None)
mtimedb.commit()
return 1
else:
if "--resume" in myopts:
print(darkgreen("emerge: It seems we have nothing to resume..."))
return os.EX_OK
try:
success, mydepgraph, favorites = backtrack_depgraph(
settings, trees, myopts, myparams, myaction, myfiles, spinner
)
except portage.exception.PackageSetNotFound as e:
root_config = trees[settings["EROOT"]]["root_config"]
display_missing_pkg_set(root_config, e.value)
return 1
if success and mydepgraph.need_config_reload():
load_emerge_config(emerge_config=emerge_config)
adjust_configs(emerge_config.opts, emerge_config.trees)
settings, trees, mtimedb = emerge_config
# After config reload, the freshly instantiated binarytree
# instances need to load remote metadata if --getbinpkg
# is enabled. Use getbinpkg_refresh=False to use cached
# metadata, since the cache is already fresh.
if "--getbinpkg" in emerge_config.opts or quickpkg_direct:
for root_trees in emerge_config.trees.values():
kwargs = {}
if quickpkg_direct:
kwargs["add_repos"] = (
emerge_config.running_config.trees["vartree"].dbapi,
)
try:
root_trees["bintree"].populate(
getbinpkgs=True, getbinpkg_refresh=False, **kwargs
)
except ParseError as e:
writemsg(
f"\n\n!!!{e}.\nSee make.conf(5) for more info.\n",
noiselevel=-1,
)
return 1
if "--autounmask-only" in myopts:
mydepgraph.display_problems()
return 0
if not success:
mydepgraph.display_problems()
return 1
mergecount = None
if (
"--pretend" not in myopts
and ("--ask" in myopts or "--tree" in myopts or "--verbose" in myopts)
and not ("--quiet" in myopts and "--ask" not in myopts)
):
if "--resume" in myopts:
mymergelist = mydepgraph.altlist()
if len(mymergelist) == 0:
print(
colorize("INFORM", "emerge: It seems we have nothing to resume...")
)
return os.EX_OK
favorites = mtimedb["resume"]["favorites"]
retval = mydepgraph.display(mydepgraph.altlist(), favorites=favorites)
mydepgraph.display_problems()
mergelist_shown = True
if retval != os.EX_OK:
return retval
prompt = "Would you like to resume merging these packages?"
else:
retval = mydepgraph.display(mydepgraph.altlist(), favorites=favorites)
mydepgraph.display_problems()
mergelist_shown = True
if retval != os.EX_OK:
return retval
mergecount = 0
for x in mydepgraph.altlist():
if isinstance(x, Package) and x.operation == "merge":
mergecount += 1
prompt = None
if mergecount == 0:
sets = trees[settings["EROOT"]]["root_config"].sets
world_candidates = None
if "selective" in myparams and not oneshot and favorites:
# Sets that are not world candidates are filtered
# out here since the favorites list needs to be
# complete for depgraph.loadResumeCommand() to
# operate correctly.
world_candidates = [
x
for x in favorites
if not (
x.startswith(SETPREFIX) and not sets[x[1:]].world_candidate
)
]
if "selective" in myparams and not oneshot and world_candidates:
# Prompt later, inside saveNomergeFavorites.
prompt = None
else:
print()
print("Nothing to merge; quitting.")
print()
return os.EX_OK
elif "--fetchonly" in myopts or "--fetch-all-uri" in myopts:
prompt = "Would you like to fetch the source files for these packages?"
else:
prompt = "Would you like to merge these packages?"
print()
uq = UserQuery(myopts)
if (
prompt is not None
and "--ask" in myopts
and uq.query(prompt, enter_invalid) == "No"
):
print()
print("Quitting.")
print()
return 128 + signal.SIGINT
# Don't ask again (e.g. when auto-cleaning packages after merge)
if mergecount != 0:
myopts.pop("--ask", None)
if ("--pretend" in myopts) and not (
"--fetchonly" in myopts or "--fetch-all-uri" in myopts
):
if "--resume" in myopts:
mymergelist = mydepgraph.altlist()
if len(mymergelist) == 0:
print(
colorize("INFORM", "emerge: It seems we have nothing to resume...")
)
return os.EX_OK
favorites = mtimedb["resume"]["favorites"]
retval = mydepgraph.display(mydepgraph.altlist(), favorites=favorites)
mydepgraph.display_problems()
mergelist_shown = True
if retval != os.EX_OK:
return retval
else:
retval = mydepgraph.display(mydepgraph.altlist(), favorites=favorites)
mydepgraph.display_problems()
mergelist_shown = True
if retval != os.EX_OK:
return retval
return os.EX_OK
gpg = None
try:
if not mergelist_shown:
# If we haven't already shown the merge list above, at
# least show warnings about missed updates and such.
mydepgraph.display_problems()
need_write_vardb = not Scheduler._opts_no_self_update.intersection(myopts)
need_write_bindb = not any(
x in myopts
for x in ("--fetchonly", "--fetch-all-uri", "--pretend", "--usepkgonly")
) and (
any(
"buildpkg" in trees[eroot]["root_config"].settings.features
for eroot in trees
)
or any(
"buildsyspkg" in trees[eroot]["root_config"].settings.features
for eroot in trees
)
)
if need_write_bindb or need_write_vardb:
eroots = set()
ebuild_eroots = set()
for x in mydepgraph.altlist():
if isinstance(x, Package) and x.operation == "merge":
eroots.add(x.root)
if x.type_name == "ebuild":
ebuild_eroots.add(x.root)
for eroot in eroots:
if need_write_vardb and not trees[eroot]["vartree"].dbapi.writable:
writemsg_level(
f"!!! Read-only file system: {trees[eroot]['vartree'].dbapi._dbroot}\n",
level=logging.ERROR,
noiselevel=-1,
)
return 1
if (
need_write_bindb
and eroot in ebuild_eroots
and (
"buildpkg" in trees[eroot]["root_config"].settings.features
or "buildsyspkg"
in trees[eroot]["root_config"].settings.features
)
and not trees[eroot]["bintree"].dbapi.writable
):
writemsg_level(
f"!!! Read-only file system: {trees[eroot]['bintree'].pkgdir}\n",
level=logging.ERROR,
noiselevel=-1,
)
return 1
# unlock GPG if needed
if (
need_write_bindb
and (eroot in ebuild_eroots)
and (
"binpkg-signing"
in trees[eroot]["root_config"].settings.features
)
):
for binpkg_gpg_config in (
"BINPKG_GPG_SIGNING_GPG_HOME",
"BINPKG_GPG_SIGNING_KEY",
):
if not trees[eroot]["root_config"].settings.get(
binpkg_gpg_config
):
writemsg_level(
colorize(
"BAD", f"!!! {binpkg_gpg_config} is not set\n"
),
level=logging.ERROR,
noiselevel=-1,
)
return 1
portage.writemsg_stdout(">>> Unlocking GPG... ")
sys.stdout.flush()
gpg = GPG(trees[eroot]["root_config"].settings)
try:
gpg.unlock()
except GPGException as e:
writemsg_level(
colorize("BAD", f"!!! {e}\n"),
level=logging.ERROR,
noiselevel=-1,
)
return 1
if "--resume" in myopts:
favorites = mtimedb["resume"]["favorites"]
else:
if (
"resume" in mtimedb
and "mergelist" in mtimedb["resume"]
and len(mtimedb["resume"]["mergelist"]) > 1
):
mtimedb["resume_backup"] = mtimedb["resume"]
del mtimedb["resume"]
mtimedb.commit()
mydepgraph.saveNomergeFavorites()
if mergecount == 0:
retval = os.EX_OK
else:
mergetask = Scheduler(
settings,
trees,
mtimedb,
myopts,
spinner,
favorites=favorites,
graph_config=mydepgraph.schedulerGraph(),
)
del mydepgraph
clear_caches(trees)
retval = mergetask.merge()
if retval == os.EX_OK and not (buildpkgonly or fetchonly or pretend):
if "yes" == settings.get("AUTOCLEAN"):
portage.writemsg_stdout(">>> Auto-cleaning packages...\n")
unmerge(
trees[settings["EROOT"]]["root_config"],
myopts,
"clean",
[],
ldpath_mtimes,
autoclean=1,
)
return retval
finally:
if gpg is not None:
gpg.stop()
def action_config(settings, trees, myopts, myfiles):
enter_invalid = "--ask-enter-invalid" in myopts
uq = UserQuery(myopts)
if len(myfiles) != 1:
print(red("!!! config can only take a single package atom at this time\n"))
sys.exit(1)
if not is_valid_package_atom(myfiles[0], allow_repo=True):
portage.writemsg(
f"!!! '{myfiles[0]}' is not a valid package atom.\n", noiselevel=-1
)
portage.writemsg("!!! Please check ebuild(5) for full details.\n")
portage.writemsg(
"!!! (Did you specify a version but forget to prefix with '='?)\n"
)
sys.exit(1)
print()
try:
pkgs = trees[settings["EROOT"]]["vartree"].dbapi.match(myfiles[0])
except portage.exception.AmbiguousPackageName as e:
# Multiple matches thrown from cpv_expand
pkgs = e.args[0]
if len(pkgs) == 0:
print("No packages found.\n")
sys.exit(0)
elif len(pkgs) > 1:
if "--ask" in myopts:
options = []
print("Please select a package to configure:")
idx = 0
for pkg in pkgs:
idx += 1
options.append(str(idx))
print(options[-1] + ") " + pkg)
print("X) Cancel")
options.append("X")
idx = uq.query("Selection?", enter_invalid, responses=options)
if idx == "X":
sys.exit(128 + signal.SIGINT)
pkg = pkgs[int(idx) - 1]
else:
print("The following packages available:")
for pkg in pkgs:
print("* " + pkg)
print("\nPlease use a specific atom or the --ask option.")
sys.exit(1)
else:
pkg = pkgs[0]
print()
if "--ask" in myopts:
if uq.query(f"Ready to configure {pkg}?", enter_invalid) == "No":
sys.exit(128 + signal.SIGINT)
else:
print("Configuring pkg...")
print()
ebuildpath = trees[settings["EROOT"]]["vartree"].dbapi.findname(pkg)
mysettings = portage.config(clone=settings)
vardb = trees[mysettings["EROOT"]]["vartree"].dbapi
debug = mysettings.get("PORTAGE_DEBUG") == "1"
retval = portage.doebuild(
ebuildpath,
"config",
settings=mysettings,
debug=(settings.get("PORTAGE_DEBUG", "") == 1),
cleanup=True,
mydbapi=trees[settings["EROOT"]]["vartree"].dbapi,
tree="vartree",
)
if retval == os.EX_OK:
portage.doebuild(
ebuildpath,
"clean",
settings=mysettings,
debug=debug,
mydbapi=vardb,
tree="vartree",
)
print()
return retval
def action_depclean(
settings, trees, ldpath_mtimes, myopts, action, myfiles, spinner, scheduler=None
):
# Kill packages that aren't explicitly merged or are required as a
# dependency of another package. World file is explicit.
# Global depclean or prune operations are not very safe when there are
# missing dependencies since it's unknown how badly incomplete
# the dependency graph is, and we might accidentally remove packages
# that should have been pulled into the graph. On the other hand, it's
# relatively safe to ignore missing deps when only asked to remove
# specific packages.
# Force autoclean for depcleans (but not purges), as it was changed
# to default off to not run it on every unmerge.
# bug #792195
if action == "depclean":
settings.unlock()
settings["AUTOCLEAN"] = "yes"
settings.backup_changes("AUTOCLEAN")
settings.lock()
msg = []
if (
"preserve-libs" not in settings.features
and not myopts.get("--depclean-lib-check", _DEPCLEAN_LIB_CHECK_DEFAULT) != "n"
):
msg.append("Depclean may break link level dependencies. Thus, it is\n")
msg.append(
"recommended to use a tool such as " + good("`revdep-rebuild`") + " (from\n"
)
msg.append("app-portage/gentoolkit) in order to detect such breakage.\n")
msg.append("\n")
msg.append("Always study the list of packages to be cleaned for any obvious\n")
msg.append("mistakes. Packages that are part of the world set will always\n")
msg.append("be kept. They can be manually added to this set with\n")
msg.append(good("`emerge --noreplace <atom>`") + ". Packages that are listed in\n")
msg.append("package.provided (see portage(5)) will be removed by\n")
msg.append("depclean, even if they are part of the world set.\n")
msg.append("\n")
msg.append("As a safety measure, depclean will not remove any packages\n")
msg.append("unless *all* required dependencies have been resolved. As a\n")
msg.append("consequence of this, it often becomes necessary to run \n")
msg.append(
f"{good('`emerge --update --newuse --deep @world`')}" + " prior to depclean.\n"
)
if action == "depclean" and "--quiet" not in myopts and not myfiles:
portage.writemsg_stdout("\n")
for x in msg:
portage.writemsg_stdout(colorize("WARN", " * ") + x)
root_config = trees[settings["EROOT"]]["root_config"]
vardb = root_config.trees["vartree"].dbapi
args_set = InternalPackageSet(allow_repo=True)
if myfiles:
args_set.update(myfiles)
matched_packages = False
for x in args_set:
if vardb.match(x):
matched_packages = True
else:
writemsg_level(
f"--- Couldn't find '{x.replace('null/', '')}' to {action}.\n",
level=logging.WARN,
noiselevel=-1,
)
if not matched_packages:
writemsg_level(f">>> No packages selected for removal by {action}\n")
return 1
# The calculation is done in a separate function so that depgraph
# references go out of scope and the corresponding memory
# is freed before we call unmerge().
rval, cleanlist, ordered, req_pkg_count = calc_depclean(
settings, trees, ldpath_mtimes, myopts, action, args_set, spinner
)
clear_caches(trees)
if rval != os.EX_OK:
return rval
if cleanlist:
rval = unmerge(
root_config,
myopts,
"unmerge",
cleanlist,
ldpath_mtimes,
ordered=ordered,
scheduler=scheduler,
)
if action == "prune":
return rval
if not cleanlist and "--quiet" in myopts:
return rval
set_atoms = {}
for k in ("profile", "system", "selected"):
try:
set_atoms[k] = root_config.setconfig.getSetAtoms(k)
except portage.exception.PackageSetNotFound:
# A nested set could not be resolved, so ignore nested sets.
set_atoms[k] = root_config.sets[k].getAtoms()
print("Packages installed: " + str(len(vardb.cpv_all())))
print(f"Packages in world: {len(set_atoms['selected'])}")
print(f"Packages in system: {len(set_atoms['system'])}")
if set_atoms["profile"]:
print(f"Packages in profile: {len(set_atoms['profile'])}")
print("Required packages: " + str(req_pkg_count))
if "--pretend" in myopts:
print("Number to remove: " + str(len(cleanlist)))
else:
print("Number removed: " + str(len(cleanlist)))
return rval
def calc_depclean(settings, trees, ldpath_mtimes, myopts, action, args_set, spinner):
result = _calc_depclean(
settings, trees, ldpath_mtimes, myopts, action, args_set, spinner
)
return result.returncode, result.cleanlist, result.ordered, result.req_pkg_count
_depclean_result = collections.namedtuple(
"_depclean_result",
("returncode", "cleanlist", "ordered", "req_pkg_count", "depgraph"),
)
def _calc_depclean(
settings,
trees,
ldpath_mtimes,
myopts,
action,
args_set,
spinner,
frozen_config=None,
):
allow_missing_deps = bool(args_set)
debug = "--debug" in myopts
xterm_titles = "notitles" not in settings.features
root_len = len(settings["ROOT"])
eroot = settings["EROOT"]
root_config = trees[eroot]["root_config"]
psets = root_config.setconfig.psets
deselect = myopts.get("--deselect") != "n"
required_sets = {}
required_sets["world"] = psets["world"]
# When removing packages, a temporary version of the world 'selected'
# set may be used which excludes packages that are intended to be
# eligible for removal.
selected_set = psets["selected"]
required_sets["selected"] = selected_set
protected_set = InternalPackageSet()
protected_set_name = "____depclean_protected_set____"
required_sets[protected_set_name] = protected_set
set_error = False
set_atoms = {}
for k in ("profile", "system", "selected"):
try:
set_atoms[k] = root_config.setconfig.getSetAtoms(k)
except portage.exception.PackageSetNotFound as e:
# A nested set could not be resolved, so ignore nested sets.
set_atoms[k] = root_config.sets[k].getAtoms()
writemsg_level(
f"!!! The set '{k}' contains a non-existent set named '{e}'.\n",
level=logging.ERROR,
noiselevel=-1,
)
set_error = True
# Support @profile as an alternative to @system.
if not (set_atoms["system"] or set_atoms["profile"]):
writemsg_level(
_("!!! You have no system list.\n"), level=logging.WARNING, noiselevel=-1
)
if not set_atoms["selected"]:
writemsg_level(
_("!!! You have no world file.\n"), level=logging.WARNING, noiselevel=-1
)
# Suppress world file warnings unless @world is completely empty,
# since having an empty world file can be a valid state.
try:
world_atoms = bool(root_config.setconfig.getSetAtoms("world"))
except portage.exception.PackageSetNotFound as e:
writemsg_level(
f"!!! The set 'world' contains a non-existent set named '{e}'.\n",
level=logging.ERROR,
noiselevel=-1,
)
set_error = True
else:
if not world_atoms:
writemsg_level(
_("!!! Your @world set is empty.\n"), level=logging.ERROR, noiselevel=-1
)
set_error = True
if set_error:
writemsg_level(
_("!!! Aborting due to set configuration " "errors displayed above.\n"),
level=logging.ERROR,
noiselevel=-1,
)
return _depclean_result(1, [], False, 0, None)
if action == "depclean":
emergelog(xterm_titles, " >>> depclean")