Add SIGUSR1 signal handlers that call pdb.set_trace(). Thanks to Brian Harring for
[portage.git] / bin / ebuild
1 #!/usr/bin/python -O
2 # Copyright 1999-2006 Gentoo Foundation
3 # Distributed under the terms of the GNU General Public License v2
4 # $Header: /var/cvsroot/gentoo-src/portage/bin/ebuild,v 1.18.2.3 2005/05/07 04:32:59 ferringb Exp $
5
6 import sys
7 # This block ensures that ^C interrupts are handled quietly.
8 try:
9         import signal
10
11         def exithandler(signum,frame):
12                 signal.signal(signal.SIGINT, signal.SIG_IGN)
13                 signal.signal(signal.SIGTERM, signal.SIG_IGN)
14                 sys.exit(1)
15
16         signal.signal(signal.SIGINT, exithandler)
17         signal.signal(signal.SIGTERM, exithandler)
18
19 except KeyboardInterrupt:
20         sys.exit(1)
21
22 def debug_signal(signum, frame):
23         import pdb
24         pdb.set_trace()
25 signal.signal(signal.SIGUSR1, debug_signal)
26
27 import optparse
28 import os
29
30 description = "See the ebuild(1) man page for more info"
31 usage = "Usage: ebuild <ebuild file> <command> [command] ..."
32 parser = optparse.OptionParser(description=description, usage=usage)
33
34 force_help = "When used together with the digest or manifest " + \
35         "command, this option forces regeneration of digests for all " + \
36         "distfiles associated with the current ebuild. Any distfiles " + \
37         "that do not already exist in ${DISTDIR} will be automatically fetched."
38
39 parser.add_option("--force", help=force_help, action="store_true", dest="force")
40 parser.add_option("--debug", help="show debug output",
41         action="store_true", dest="debug")
42 parser.add_option("--ignore-default-opts",
43         action="store_true",
44         help="do not use the EBUILD_DEFAULT_OPTS environment variable")
45 parser.add_option("--skip-manifest", help="skip all manifest checks",
46         action="store_true", dest="skip_manifest")
47
48 opts, pargs = parser.parse_args(args=sys.argv[1:])
49
50 if len(pargs) < 2:
51         parser.error("missing required args")
52
53 if "merge" in pargs:
54         print "Disabling noauto in features... merge disables it. (qmerge doesn't)"
55         os.environ["FEATURES"] = os.environ.get("FEATURES", "") + " -noauto"
56
57 os.environ["PORTAGE_CALLER"]="ebuild"
58 try:
59         import portage
60 except ImportError:
61         from os import path as osp
62         sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym"))
63         import portage
64
65 if not opts.ignore_default_opts:
66         default_opts = portage.settings.get("EBUILD_DEFAULT_OPTS", "").split()
67         opts, pargs = parser.parse_args(default_opts + sys.argv[1:])
68
69 debug = opts.debug
70 force = opts.force
71
72 import portage.util, portage.const
73 import portage.dep
74 portage.dep._dep_check_strict = True
75
76 # do this _after_ 'import portage' to prevent unnecessary tracing
77 if debug and "python-trace" in portage.features:
78         import portage.debug
79         portage.debug.set_trace(True)
80
81 if portage.settings["NOCOLOR"] in ("yes","true") or not sys.stdout.isatty():
82         portage.output.nocolor()
83
84 ebuild = pargs.pop(0)
85 if not os.path.isabs(ebuild):
86         mycwd = os.getcwd()
87         # Try to get the non-canonical path from the PWD evironment variable, since
88         # the canonical path returned from os.getcwd() may may be unusable in
89         # cases where the directory stucture is built from symlinks.
90         if "PWD" in os.environ and os.environ["PWD"] != mycwd and \
91                 os.path.realpath(os.environ["PWD"]) == mycwd:
92                 mycwd = portage.normalize_path(os.environ["PWD"])
93         ebuild = os.path.join(mycwd, ebuild)
94 ebuild = portage.normalize_path(ebuild)
95 # portdbapi uses the canonical path for the base of the portage tree, but
96 # subdirectories of the base can be built from symlinks (like crossdev does).
97 ebuild_portdir = os.path.realpath(
98         os.path.dirname(os.path.dirname(os.path.dirname(ebuild))))
99 ebuild = os.path.join(ebuild_portdir, *ebuild.split(os.path.sep)[-3:])
100
101 # Make sure that portdb.findname() returns the correct ebuild.
102 if ebuild_portdir not in portage.portdb.porttrees:
103         os.environ["PORTDIR_OVERLAY"] = \
104                 os.environ.get("PORTDIR_OVERLAY","") + " " + ebuild_portdir
105         print "Appending %s to PORTDIR_OVERLAY..." % ebuild_portdir
106         portage.close_portdbapi_caches()
107         reload(portage)
108 del portage.portdb.porttrees[1:]
109 if ebuild_portdir != portage.portdb.porttree_root:
110         portage.portdb.porttrees.append(ebuild_portdir)
111
112 if not os.path.exists(ebuild):
113         print "'%s' does not exist." % ebuild
114         sys.exit(1)
115
116 ebuild_split = ebuild.split("/")
117 del ebuild_split[-2]
118 cpv = "/".join(ebuild_split[-2:])[:-7]
119
120 if not portage.catpkgsplit(cpv):
121         print "!!! %s does not follow correct package syntax." % (cpv)
122         sys.exit(1)
123
124 if ebuild.startswith(portage.root + portage.const.VDB_PATH):
125         mytree = "vartree"
126
127         portage_ebuild = portage.db[portage.root][mytree].dbapi.findname(cpv)
128
129         if os.path.realpath(portage_ebuild) != ebuild:
130                 print "!!! Portage seems to think that %s is at %s" % (cpv, portage_ebuild)
131                 sys.exit(1)
132
133 else:
134         mytree = "porttree"
135
136         portage_ebuild = portage.portdb.findname(cpv)
137
138         if not portage_ebuild or portage_ebuild != ebuild:
139                 print "!!! %s does not seem to have a valid PORTDIR structure." % ebuild
140                 sys.exit(1)
141
142 if len(pargs) > 1 and "config" in pargs:
143         print "config must be called on it's own, not combined with any other phase"
144         sys.exit(1)
145
146 def discard_digests(myebuild, mysettings, mydbapi):
147         """Discard all distfiles digests for the given ebuild.  This is useful when
148         upstream has changed the identity of the distfiles and the user would
149         otherwise have to manually remove the Manifest and files/digest-* files in
150         order to ensure correct results."""
151         try:
152                 portage._doebuild_manifest_exempt_depend += 1
153                 pkgdir = os.path.dirname(myebuild)
154                 fetchlist_dict = portage.FetchlistDict(pkgdir, mysettings, mydbapi)
155                 cat, pkg = pkgdir.split(os.sep)[-2:]
156                 cpv = cat + "/" + os.path.basename(myebuild)[:-7]
157                 from portage.manifest import Manifest
158                 mf = Manifest(pkgdir, mysettings["DISTDIR"],
159                         fetchlist_dict=fetchlist_dict, manifest1_compat=False)
160                 mf.create(requiredDistfiles=None,
161                         assumeDistHashesSometimes=True, assumeDistHashesAlways=True)
162                 distfiles = fetchlist_dict[cpv]
163                 for myfile in distfiles:
164                         try:
165                                 del mf.fhashdict["DIST"][myfile]
166                         except KeyError:
167                                 pass
168                 mf.write()
169         finally:
170                 portage._doebuild_manifest_exempt_depend -= 1
171
172 tmpsettings = portage.config(clone=portage.settings)
173 if "test" in pargs:
174         # This variable is a signal to config.regenerate() to
175         # indicate that the test phase should be enabled regardless
176         # of problems such as masked "test" USE flag.
177         tmpsettings["EBUILD_FORCE_TEST"] = "1"
178         tmpsettings.backup_changes("EBUILD_FORCE_TEST")
179         if "test" not in tmpsettings.features:
180                 tmpsettings.features.append("test")
181                 tmpsettings.features.sort()
182                 tmpsettings["FEATURES"] = " ".join(tmpsettings.features)
183                 tmpsettings.backup_changes("FEATURES")
184
185 if opts.skip_manifest:
186         tmpsettings["EBUILD_SKIP_MANIFEST"] = "1"
187         tmpsettings.backup_changes("EBUILD_SKIP_MANIFEST")
188         portage._doebuild_manifest_exempt_depend += 1
189
190 build_dir_phases = set(["setup", "unpack", "configure", "compile",
191         "test", "install", "package", "rpm"])
192
193 def stale_env_warning():
194         if "clean" not in pargs and \
195                 "noauto" not in tmpsettings.features and \
196                 tmpsettings.get("PORTAGE_QUIET") != "1" and \
197                 build_dir_phases.intersection(pargs):
198                 portage.doebuild_environment(ebuild, "setup", portage.root,
199                         tmpsettings, debug, 1, portage.portdb)
200                 env_filename = os.path.join(tmpsettings["T"], "environment")
201                 if os.path.exists(env_filename):
202                         msg = ("Existing ${T}/environment for '%s' will be sourced. " + \
203                                 "Run 'clean' to start with a fresh environment.") % \
204                                 (tmpsettings["PF"], )
205                         from textwrap import wrap
206                         msg = wrap(msg, 70)
207                         for x in msg:
208                                 portage.writemsg(">>> %s\n" % x)
209
210 from portage.exception import PermissionDenied, \
211         PortagePackageException, UnsupportedAPIException
212 checked_for_stale_env = False
213
214 for arg in pargs:
215         try:
216                 if not checked_for_stale_env and arg not in ("digest","manifest"):
217                         # This has to go after manifest generation since otherwise
218                         # aux_get() might fail due to invalid ebuild digests.
219                         stale_env_warning()
220                         checked_for_stale_env = True
221
222                 if arg in ("digest", "manifest") and force:
223                         discard_digests(ebuild, tmpsettings, portage.portdb)
224                 a = portage.doebuild(ebuild, arg, portage.root, tmpsettings,
225                         debug=debug, tree=mytree)
226         except KeyboardInterrupt:
227                 print "Interrupted."
228                 a = 1
229         except KeyError:
230                 # aux_get error
231                 a = 1
232         except UnsupportedAPIException, e:
233                 from textwrap import wrap
234                 msg = wrap(str(e), 70)
235                 del e
236                 for x in msg:
237                         portage.writemsg("!!! %s\n" % x, noiselevel=-1)
238                 a = 1
239         except PortagePackageException, e:
240                 portage.writemsg("!!! %s\n" % (e,), noiselevel=-1)
241                 a = 1
242         except PermissionDenied, e:
243                 portage.writemsg("!!! Permission Denied: %s\n" % (e,), noiselevel=-1)
244                 a = 1
245         if a == None:
246                 print "Could not run the required binary?"
247                 a = 127
248         if a:
249                 sys.exit(a)