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