Add ebuild(1) support for FEATURES=digest.
[portage.git] / bin / ebuild
1 #!/usr/bin/python -O
2 # Copyright 1999-2010 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
267 if 'digest' in tmpsettings.features and \
268         not set(["digest", "manifest"]).intersection(pargs):
269         pargs = ['digest'] + pargs
270
271 checked_for_stale_env = False
272
273 for arg in pargs:
274         try:
275                 if not checked_for_stale_env and arg not in ("digest","manifest"):
276                         # This has to go after manifest generation since otherwise
277                         # aux_get() might fail due to invalid ebuild digests.
278                         stale_env_warning()
279                         checked_for_stale_env = True
280
281                 if arg in ("digest", "manifest") and force:
282                         discard_digests(ebuild, tmpsettings, portage.portdb)
283                 a = portage.doebuild(ebuild, arg, portage.root, tmpsettings,
284                         debug=debug, tree=mytree,
285                         vartree=portage.db[portage.root]['vartree'])
286         except KeyboardInterrupt:
287                 print("Interrupted.")
288                 a = 1
289         except KeyError:
290                 # aux_get error
291                 a = 1
292         except UnsupportedAPIException as e:
293                 from textwrap import wrap
294                 msg = wrap(str(e), 70)
295                 del e
296                 for x in msg:
297                         portage.writemsg("!!! %s\n" % x, noiselevel=-1)
298                 a = 1
299         except PortagePackageException as e:
300                 portage.writemsg("!!! %s\n" % (e,), noiselevel=-1)
301                 a = 1
302         except PermissionDenied as e:
303                 portage.writemsg("!!! Permission Denied: %s\n" % (e,), noiselevel=-1)
304                 a = 1
305         if a == None:
306                 print("Could not run the required binary?")
307                 a = 127
308         if a:
309                 sys.exit(a)