ebuild(1): fix ebuild modification check
[portage.git] / bin / ebuild
1 #!/usr/bin/python -O
2 # Copyright 1999-2011 Gentoo Foundation
3 # Distributed under the terms of the GNU General Public License v2
4
5 from __future__ import print_function
6
7 import signal
8 import sys
9 # This block ensures that ^C interrupts are handled quietly.
10 try:
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(128 + signum)
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(128 + signal.SIGINT)
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.realpath(os.path.join(portage.settings['EROOT'], 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
153 # Constrain eclass resolution to the master(s)
154 # that are specified in layout.conf (using an
155 # approach similar to repoman's).
156 myrepo = None
157 if ebuild_portdir != vdb_path:
158         myrepo = portage.portdb.getRepositoryName(ebuild_portdir)
159         repo_info = portage.portdb._repo_info[ebuild_portdir]
160         portage.portdb.porttrees = list(repo_info.eclass_db.porttrees)
161
162 if not os.path.exists(ebuild):
163         print("'%s' does not exist." % ebuild)
164         sys.exit(1)
165
166 ebuild_split = ebuild.split("/")
167 cpv = "%s/%s" % (ebuild_split[-3], pf)
168
169 if not portage.catpkgsplit(cpv):
170         print("!!! %s does not follow correct package syntax." % (cpv))
171         sys.exit(1)
172
173 if ebuild.startswith(vdb_path):
174         mytree = "vartree"
175
176         portage_ebuild = portage.db[portage.root][mytree].dbapi.findname(cpv, myrepo=myrepo)
177
178         if os.path.realpath(portage_ebuild) != ebuild:
179                 print("!!! Portage seems to think that %s is at %s" % (cpv, portage_ebuild))
180                 sys.exit(1)
181
182 else:
183         mytree = "porttree"
184
185         portage_ebuild = portage.portdb.findname(cpv, myrepo=myrepo)
186
187         if not portage_ebuild or portage_ebuild != ebuild:
188                 print("!!! %s does not seem to have a valid PORTDIR structure." % ebuild)
189                 sys.exit(1)
190
191 if len(pargs) > 1 and "config" in pargs:
192         print("config must be called on it's own, not combined with any other phase")
193         sys.exit(1)
194
195 def discard_digests(myebuild, mysettings, mydbapi):
196         """Discard all distfiles digests for the given ebuild.  This is useful when
197         upstream has changed the identity of the distfiles and the user would
198         otherwise have to manually remove the Manifest and files/digest-* files in
199         order to ensure correct results."""
200         try:
201                 portage._doebuild_manifest_exempt_depend += 1
202                 pkgdir = os.path.dirname(myebuild)
203                 fetchlist_dict = portage.FetchlistDict(pkgdir, mysettings, mydbapi)
204                 from portage.manifest import Manifest
205                 mf = Manifest(pkgdir, mysettings["DISTDIR"],
206                         fetchlist_dict=fetchlist_dict, manifest1_compat=False)
207                 mf.create(requiredDistfiles=None,
208                         assumeDistHashesSometimes=True, assumeDistHashesAlways=True)
209                 distfiles = fetchlist_dict[cpv]
210                 for myfile in distfiles:
211                         try:
212                                 del mf.fhashdict["DIST"][myfile]
213                         except KeyError:
214                                 pass
215                 mf.write()
216         finally:
217                 portage._doebuild_manifest_exempt_depend -= 1
218
219 portage.settings.validate() # generate warning messages if necessary
220
221 build_dir_phases = set(["setup", "unpack", "prepare", "configure", "compile",
222         "test", "install", "package", "rpm", "merge", "qmerge"])
223
224 # If the current metadata is invalid then force the ebuild to be
225 # sourced again even if $T/environment already exists.
226 ebuild_changed = False
227 if mytree == "porttree" and build_dir_phases.intersection(pargs):
228         metadata, st, emtime = \
229                 portage.portdb._pull_valid_cache(cpv, ebuild, ebuild_portdir)
230         if metadata is None:
231                 ebuild_changed = True
232
233 tmpsettings = portage.config(clone=portage.settings)
234 tmpsettings["PORTAGE_VERBOSE"] = "1"
235 tmpsettings.backup_changes("PORTAGE_VERBOSE")
236
237 if opts.skip_manifest:
238         tmpsettings["EBUILD_SKIP_MANIFEST"] = "1"
239         tmpsettings.backup_changes("EBUILD_SKIP_MANIFEST")
240
241 if opts.skip_manifest or \
242         "digest" in tmpsettings.features or \
243         "digest" in pargs or \
244         "manifest" in pargs:
245         portage._doebuild_manifest_exempt_depend += 1
246
247 if "test" in pargs:
248         # This variable is a signal to config.regenerate() to
249         # indicate that the test phase should be enabled regardless
250         # of problems such as masked "test" USE flag.
251         tmpsettings["EBUILD_FORCE_TEST"] = "1"
252         tmpsettings.backup_changes("EBUILD_FORCE_TEST")
253         tmpsettings["FEATURES"] = tmpsettings.get("FEATURES", "") + " " + "test"
254         tmpsettings.backup_changes("FEATURES")
255
256 tmpsettings["FEATURES"] = tmpsettings.get("FEATURES", "") + " " + "-fail-clean"
257 tmpsettings.backup_changes("FEATURES")
258
259 # Apply package.env settings. This allows per-package FEATURES and other
260 # variables (possibly PORTAGE_TMPDIR) to be available as soon as possible.
261 try:
262         tmpsettings.setcpv(cpv, mydb=portage.db[portage.root][mytree].dbapi)
263 except KeyError:
264         # aux_get failure, message should have been shown on stderr.
265         sys.exit(1)
266
267 def stale_env_warning():
268         if "clean" not in pargs and \
269                 "noauto" not in tmpsettings.features and \
270                 build_dir_phases.intersection(pargs):
271                 portage.doebuild_environment(ebuild, "setup", portage.root,
272                         tmpsettings, debug, 1, portage.portdb)
273                 env_filename = os.path.join(tmpsettings["T"], "environment")
274                 if os.path.exists(env_filename):
275                         msg = ("Existing ${T}/environment for '%s' will be sourced. " + \
276                                 "Run 'clean' to start with a fresh environment.") % \
277                                 (tmpsettings["PF"], )
278                         from textwrap import wrap
279                         msg = wrap(msg, 70)
280                         for x in msg:
281                                 portage.writemsg(">>> %s\n" % x)
282
283                         if ebuild_changed:
284                                 open(os.path.join(tmpsettings['PORTAGE_BUILDDIR'],
285                                         '.ebuild_changed'), 'w')
286
287 from portage.exception import PermissionDenied, \
288         PortagePackageException, UnsupportedAPIException
289
290 if 'digest' in tmpsettings.features and \
291         not set(["digest", "manifest"]).intersection(pargs):
292         pargs = ['digest'] + pargs
293
294 checked_for_stale_env = False
295
296 for arg in pargs:
297         try:
298                 if not checked_for_stale_env and arg not in ("digest","manifest"):
299                         # This has to go after manifest generation since otherwise
300                         # aux_get() might fail due to invalid ebuild digests.
301                         stale_env_warning()
302                         checked_for_stale_env = True
303
304                 if arg in ("digest", "manifest") and force:
305                         discard_digests(ebuild, tmpsettings, portage.portdb)
306                 a = portage.doebuild(ebuild, arg, portage.root, tmpsettings,
307                         debug=debug, tree=mytree,
308                         vartree=portage.db[portage.root]['vartree'])
309         except KeyboardInterrupt:
310                 print("Interrupted.")
311                 a = 1
312         except KeyError:
313                 # aux_get error
314                 a = 1
315         except UnsupportedAPIException as e:
316                 from textwrap import wrap
317                 msg = wrap(str(e), 70)
318                 del e
319                 for x in msg:
320                         portage.writemsg("!!! %s\n" % x, noiselevel=-1)
321                 a = 1
322         except PortagePackageException as e:
323                 portage.writemsg("!!! %s\n" % (e,), noiselevel=-1)
324                 a = 1
325         except PermissionDenied as e:
326                 portage.writemsg("!!! Permission Denied: %s\n" % (e,), noiselevel=-1)
327                 a = 1
328         if a == None:
329                 print("Could not run the required binary?")
330                 a = 127
331         if a:
332                 sys.exit(a)