Add a wrapper around config.features that provides the following
[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
5 from __future__ import print_function
6
7 import sys
8 # This block ensures that ^C interrupts are handled quietly.
9 try:
10         import signal
11
12         def exithandler(signum,frame):
13                 signal.signal(signal.SIGINT, signal.SIG_IGN)
14                 signal.signal(signal.SIGTERM, signal.SIG_IGN)
15                 sys.exit(1)
16
17         signal.signal(signal.SIGINT, exithandler)
18         signal.signal(signal.SIGTERM, exithandler)
19         # Prevent "[Errno 32] Broken pipe" exceptions when
20         # writing to a pipe.
21         signal.signal(signal.SIGPIPE, signal.SIG_DFL)
22
23 except KeyboardInterrupt:
24         sys.exit(1)
25
26 def debug_signal(signum, frame):
27         import pdb
28         pdb.set_trace()
29 signal.signal(signal.SIGUSR1, debug_signal)
30
31 import imp
32 import optparse
33 import os
34
35 description = "See the ebuild(1) man page for more info"
36 usage = "Usage: ebuild <ebuild file> <command> [command] ..."
37 parser = optparse.OptionParser(description=description, usage=usage)
38
39 force_help = "When used together with the digest or manifest " + \
40         "command, this option forces regeneration of digests for all " + \
41         "distfiles associated with the current ebuild. Any distfiles " + \
42         "that do not already exist in ${DISTDIR} will be automatically fetched."
43
44 parser.add_option("--force", help=force_help, action="store_true", dest="force")
45 parser.add_option("--color", help="enable or disable color output",
46         type="choice", choices=("y", "n"))
47 parser.add_option("--debug", help="show debug output",
48         action="store_true", dest="debug")
49 parser.add_option("--ignore-default-opts",
50         action="store_true",
51         help="do not use the EBUILD_DEFAULT_OPTS environment variable")
52 parser.add_option("--skip-manifest", help="skip all manifest checks",
53         action="store_true", dest="skip_manifest")
54
55 opts, pargs = parser.parse_args(args=sys.argv[1:])
56
57 if len(pargs) < 2:
58         parser.error("missing required args")
59
60 if "merge" in pargs:
61         print("Disabling noauto in features... merge disables it. (qmerge doesn't)")
62         os.environ["FEATURES"] = os.environ.get("FEATURES", "") + " -noauto"
63
64 os.environ["PORTAGE_CALLER"]="ebuild"
65 try:
66         import portage
67 except ImportError:
68         from os import path as osp
69         sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym"))
70         import portage
71
72 portage.dep._internal_warnings = True
73 from portage import os
74 from portage import _encodings
75 from portage import _shell_quote
76 from portage import _unicode_decode
77 from portage import _unicode_encode
78 from portage.const import VDB_PATH
79
80 if not opts.ignore_default_opts:
81         default_opts = portage.settings.get("EBUILD_DEFAULT_OPTS", "").split()
82         opts, pargs = parser.parse_args(default_opts + sys.argv[1:])
83
84 debug = opts.debug
85 force = opts.force
86
87 import portage.util, portage.const
88
89 # do this _after_ 'import portage' to prevent unnecessary tracing
90 if debug and "python-trace" in portage.features:
91         import portage.debug
92         portage.debug.set_trace(True)
93
94 if not opts.color == 'y' and \
95         (opts.color == 'n' or \
96         portage.settings.get('NOCOLOR') in ('yes', 'true') or \
97         portage.settings.get('TERM') == 'dumb' or \
98         not sys.stdout.isatty()):
99         portage.output.nocolor()
100         portage.settings.unlock()
101         portage.settings['NOCOLOR'] = 'true'
102         portage.settings.lock()
103
104 ebuild = pargs.pop(0)
105
106 pf = None
107 if ebuild.endswith(".ebuild"):
108         pf = os.path.basename(ebuild)[:-7]
109
110 if pf is None:
111         portage.writemsg("'%s' does not end with '.ebuild'.\n" % \
112                 (ebuild,), noiselevel=-1)
113         sys.exit(1)
114
115 if not os.path.isabs(ebuild):
116         mycwd = os.getcwd()
117         # Try to get the non-canonical path from the PWD evironment variable, since
118         # the canonical path returned from os.getcwd() may may be unusable in
119         # cases where the directory stucture is built from symlinks.
120         pwd = os.environ.get('PWD', '')
121         if sys.hexversion < 0x3000000:
122                 pwd = _unicode_decode(pwd, encoding=_encodings['content'],
123                         errors='strict')
124         if pwd and pwd != mycwd and \
125                 os.path.realpath(pwd) == mycwd:
126                 mycwd = portage.normalize_path(pwd)
127         ebuild = os.path.join(mycwd, ebuild)
128 ebuild = portage.normalize_path(ebuild)
129 # portdbapi uses the canonical path for the base of the portage tree, but
130 # subdirectories of the base can be built from symlinks (like crossdev does).
131 ebuild_portdir = os.path.realpath(
132         os.path.dirname(os.path.dirname(os.path.dirname(ebuild))))
133 ebuild = os.path.join(ebuild_portdir, *ebuild.split(os.path.sep)[-3:])
134 vdb_path = os.path.join(portage.settings['ROOT'], VDB_PATH)
135
136 # Make sure that portdb.findname() returns the correct ebuild.
137 if ebuild_portdir != vdb_path and \
138         ebuild_portdir not in portage.portdb.porttrees:
139         if sys.hexversion >= 0x3000000:
140                 os.environ["PORTDIR_OVERLAY"] = \
141                         os.environ.get("PORTDIR_OVERLAY","") + \
142                         " " + _shell_quote(ebuild_portdir)
143         else:
144                 os.environ["PORTDIR_OVERLAY"] = \
145                         os.environ.get("PORTDIR_OVERLAY","") + \
146                         " " + _unicode_encode(_shell_quote(ebuild_portdir),
147                         encoding=_encodings['content'], errors='strict')
148
149         print("Appending %s to PORTDIR_OVERLAY..." % ebuild_portdir)
150         portage.close_portdbapi_caches()
151         imp.reload(portage)
152 del portage.portdb.porttrees[1:]
153 if ebuild_portdir != portage.portdb.porttree_root:
154         portage.portdb.porttrees.append(ebuild_portdir)
155
156 if not os.path.exists(ebuild):
157         print("'%s' does not exist." % ebuild)
158         sys.exit(1)
159
160 ebuild_split = ebuild.split("/")
161 cpv = "%s/%s" % (ebuild_split[-3], pf)
162
163 if not portage.catpkgsplit(cpv):
164         print("!!! %s does not follow correct package syntax." % (cpv))
165         sys.exit(1)
166
167 if ebuild.startswith(os.path.join(portage.root, portage.const.VDB_PATH)):
168         mytree = "vartree"
169
170         portage_ebuild = portage.db[portage.root][mytree].dbapi.findname(cpv)
171
172         if os.path.realpath(portage_ebuild) != ebuild:
173                 print("!!! Portage seems to think that %s is at %s" % (cpv, portage_ebuild))
174                 sys.exit(1)
175
176 else:
177         mytree = "porttree"
178
179         portage_ebuild = portage.portdb.findname(cpv)
180
181         if not portage_ebuild or portage_ebuild != ebuild:
182                 print("!!! %s does not seem to have a valid PORTDIR structure." % ebuild)
183                 sys.exit(1)
184
185 if len(pargs) > 1 and "config" in pargs:
186         print("config must be called on it's own, not combined with any other phase")
187         sys.exit(1)
188
189 def discard_digests(myebuild, mysettings, mydbapi):
190         """Discard all distfiles digests for the given ebuild.  This is useful when
191         upstream has changed the identity of the distfiles and the user would
192         otherwise have to manually remove the Manifest and files/digest-* files in
193         order to ensure correct results."""
194         try:
195                 portage._doebuild_manifest_exempt_depend += 1
196                 pkgdir = os.path.dirname(myebuild)
197                 fetchlist_dict = portage.FetchlistDict(pkgdir, mysettings, mydbapi)
198                 from portage.manifest import Manifest
199                 mf = Manifest(pkgdir, mysettings["DISTDIR"],
200                         fetchlist_dict=fetchlist_dict, manifest1_compat=False)
201                 mf.create(requiredDistfiles=None,
202                         assumeDistHashesSometimes=True, assumeDistHashesAlways=True)
203                 distfiles = fetchlist_dict[cpv]
204                 for myfile in distfiles:
205                         try:
206                                 del mf.fhashdict["DIST"][myfile]
207                         except KeyError:
208                                 pass
209                 mf.write()
210         finally:
211                 portage._doebuild_manifest_exempt_depend -= 1
212
213 portage.settings.validate() # generate warning messages if necessary
214 tmpsettings = portage.config(clone=portage.settings)
215 tmpsettings["PORTAGE_VERBOSE"] = "1"
216 tmpsettings.backup_changes("PORTAGE_VERBOSE")
217 if "test" in pargs:
218         # This variable is a signal to config.regenerate() to
219         # indicate that the test phase should be enabled regardless
220         # of problems such as masked "test" USE flag.
221         tmpsettings["EBUILD_FORCE_TEST"] = "1"
222         tmpsettings.backup_changes("EBUILD_FORCE_TEST")
223         tmpsettings.features.add("test")
224
225 tmpsettings.features.discard('fail-clean')
226
227 if opts.skip_manifest:
228         tmpsettings["EBUILD_SKIP_MANIFEST"] = "1"
229         tmpsettings.backup_changes("EBUILD_SKIP_MANIFEST")
230         portage._doebuild_manifest_exempt_depend += 1
231
232 build_dir_phases = set(["setup", "unpack", "prepare", "configure", "compile",
233         "test", "install", "package", "rpm", "merge", "qmerge"])
234
235 # If the current metadata is invalid then force the ebuild to be
236 # sourced again even if $T/environment already exists.
237 ebuild_changed = False
238 if build_dir_phases.intersection(pargs):
239         metadata, st, emtime = \
240                 portage.portdb._pull_valid_cache(cpv, ebuild, ebuild_portdir)
241         if metadata is None:
242                 ebuild_changed = True
243
244 def stale_env_warning():
245         if "clean" not in pargs and \
246                 "noauto" not in tmpsettings.features and \
247                 build_dir_phases.intersection(pargs):
248                 portage.doebuild_environment(ebuild, "setup", portage.root,
249                         tmpsettings, debug, 1, portage.portdb)
250                 env_filename = os.path.join(tmpsettings["T"], "environment")
251                 if os.path.exists(env_filename):
252                         msg = ("Existing ${T}/environment for '%s' will be sourced. " + \
253                                 "Run 'clean' to start with a fresh environment.") % \
254                                 (tmpsettings["PF"], )
255                         from textwrap import wrap
256                         msg = wrap(msg, 70)
257                         for x in msg:
258                                 portage.writemsg(">>> %s\n" % x)
259
260                         if ebuild_changed:
261                                 open(os.path.join(tmpsettings['PORTAGE_BUILDDIR'],
262                                         '.ebuild_changed'), 'w')
263
264 from portage.exception import PermissionDenied, \
265         PortagePackageException, UnsupportedAPIException
266 checked_for_stale_env = False
267
268 for arg in pargs:
269         try:
270                 if not checked_for_stale_env and arg not in ("digest","manifest"):
271                         # This has to go after manifest generation since otherwise
272                         # aux_get() might fail due to invalid ebuild digests.
273                         stale_env_warning()
274                         checked_for_stale_env = True
275
276                 if arg in ("digest", "manifest") and force:
277                         discard_digests(ebuild, tmpsettings, portage.portdb)
278                 a = portage.doebuild(ebuild, arg, portage.root, tmpsettings,
279                         debug=debug, tree=mytree,
280                         vartree=portage.db[portage.root]['vartree'])
281         except KeyboardInterrupt:
282                 print("Interrupted.")
283                 a = 1
284         except KeyError:
285                 # aux_get error
286                 a = 1
287         except UnsupportedAPIException as e:
288                 from textwrap import wrap
289                 msg = wrap(str(e), 70)
290                 del e
291                 for x in msg:
292                         portage.writemsg("!!! %s\n" % x, noiselevel=-1)
293                 a = 1
294         except PortagePackageException as e:
295                 portage.writemsg("!!! %s\n" % (e,), noiselevel=-1)
296                 a = 1
297         except PermissionDenied as e:
298                 portage.writemsg("!!! Permission Denied: %s\n" % (e,), noiselevel=-1)
299                 a = 1
300         if a == None:
301                 print("Could not run the required binary?")
302                 a = 127
303         if a:
304                 sys.exit(a)