Make ebuild(1) detect ebuild/eclass changes and automatically source the
[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 # $Id$
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
86 if not ebuild.endswith(".ebuild"):
87         portage.writemsg("'%s' does not end with '.ebuild'.\n" % \
88                 (ebuild,), noiselevel=-1)
89         sys.exit(1)
90
91 if not os.path.isabs(ebuild):
92         mycwd = os.getcwd()
93         # Try to get the non-canonical path from the PWD evironment variable, since
94         # the canonical path returned from os.getcwd() may may be unusable in
95         # cases where the directory stucture is built from symlinks.
96         if "PWD" in os.environ and os.environ["PWD"] != mycwd and \
97                 os.path.realpath(os.environ["PWD"]) == mycwd:
98                 mycwd = portage.normalize_path(os.environ["PWD"])
99         ebuild = os.path.join(mycwd, ebuild)
100 ebuild = portage.normalize_path(ebuild)
101 # portdbapi uses the canonical path for the base of the portage tree, but
102 # subdirectories of the base can be built from symlinks (like crossdev does).
103 ebuild_portdir = os.path.realpath(
104         os.path.dirname(os.path.dirname(os.path.dirname(ebuild))))
105 ebuild = os.path.join(ebuild_portdir, *ebuild.split(os.path.sep)[-3:])
106
107 # Make sure that portdb.findname() returns the correct ebuild.
108 if ebuild_portdir not in portage.portdb.porttrees:
109         os.environ["PORTDIR_OVERLAY"] = \
110                 os.environ.get("PORTDIR_OVERLAY","") + " " + ebuild_portdir
111         print "Appending %s to PORTDIR_OVERLAY..." % ebuild_portdir
112         portage.close_portdbapi_caches()
113         reload(portage)
114 del portage.portdb.porttrees[1:]
115 if ebuild_portdir != portage.portdb.porttree_root:
116         portage.portdb.porttrees.append(ebuild_portdir)
117
118 if not os.path.exists(ebuild):
119         print "'%s' does not exist." % ebuild
120         sys.exit(1)
121
122 ebuild_split = ebuild.split("/")
123 del ebuild_split[-2]
124 cpv = "/".join(ebuild_split[-2:])[:-7]
125
126 if not portage.catpkgsplit(cpv):
127         print "!!! %s does not follow correct package syntax." % (cpv)
128         sys.exit(1)
129
130 if ebuild.startswith(portage.root + portage.const.VDB_PATH):
131         mytree = "vartree"
132
133         portage_ebuild = portage.db[portage.root][mytree].dbapi.findname(cpv)
134
135         if os.path.realpath(portage_ebuild) != ebuild:
136                 print "!!! Portage seems to think that %s is at %s" % (cpv, portage_ebuild)
137                 sys.exit(1)
138
139 else:
140         mytree = "porttree"
141
142         portage_ebuild = portage.portdb.findname(cpv)
143
144         if not portage_ebuild or portage_ebuild != ebuild:
145                 print "!!! %s does not seem to have a valid PORTDIR structure." % ebuild
146                 sys.exit(1)
147
148 if len(pargs) > 1 and "config" in pargs:
149         print "config must be called on it's own, not combined with any other phase"
150         sys.exit(1)
151
152 def discard_digests(myebuild, mysettings, mydbapi):
153         """Discard all distfiles digests for the given ebuild.  This is useful when
154         upstream has changed the identity of the distfiles and the user would
155         otherwise have to manually remove the Manifest and files/digest-* files in
156         order to ensure correct results."""
157         try:
158                 portage._doebuild_manifest_exempt_depend += 1
159                 pkgdir = os.path.dirname(myebuild)
160                 fetchlist_dict = portage.FetchlistDict(pkgdir, mysettings, mydbapi)
161                 cat, pkg = pkgdir.split(os.sep)[-2:]
162                 cpv = cat + "/" + os.path.basename(myebuild)[:-7]
163                 from portage.manifest import Manifest
164                 mf = Manifest(pkgdir, mysettings["DISTDIR"],
165                         fetchlist_dict=fetchlist_dict, manifest1_compat=False)
166                 mf.create(requiredDistfiles=None,
167                         assumeDistHashesSometimes=True, assumeDistHashesAlways=True)
168                 distfiles = fetchlist_dict[cpv]
169                 for myfile in distfiles:
170                         try:
171                                 del mf.fhashdict["DIST"][myfile]
172                         except KeyError:
173                                 pass
174                 mf.write()
175         finally:
176                 portage._doebuild_manifest_exempt_depend -= 1
177
178 portage.settings.validate() # generate warning messages if necessary
179 tmpsettings = portage.config(clone=portage.settings)
180 tmpsettings["PORTAGE_VERBOSE"] = "1"
181 tmpsettings.backup_changes("PORTAGE_VERBOSE")
182 if "test" in pargs:
183         # This variable is a signal to config.regenerate() to
184         # indicate that the test phase should be enabled regardless
185         # of problems such as masked "test" USE flag.
186         tmpsettings["EBUILD_FORCE_TEST"] = "1"
187         tmpsettings.backup_changes("EBUILD_FORCE_TEST")
188         if "test" not in tmpsettings.features:
189                 tmpsettings.features.add("test")
190                 tmpsettings["FEATURES"] = " ".join(sorted(tmpsettings.features))
191                 tmpsettings.backup_changes("FEATURES")
192
193 if opts.skip_manifest:
194         tmpsettings["EBUILD_SKIP_MANIFEST"] = "1"
195         tmpsettings.backup_changes("EBUILD_SKIP_MANIFEST")
196         portage._doebuild_manifest_exempt_depend += 1
197
198 # If the current metadata is invalid then force the ebuild to be
199 # sourced again even if $T/environment already exists.
200 ebuild_changed = False
201 metadata, st, emtime = \
202         portage.portdb._pull_valid_cache(cpv, ebuild, ebuild_portdir)
203 if metadata is None:
204         ebuild_changed = True
205
206 build_dir_phases = set(["setup", "unpack", "prepare", "configure", "compile",
207         "test", "install", "package", "rpm"])
208
209 def stale_env_warning():
210         if "clean" not in pargs and \
211                 "noauto" not in tmpsettings.features and \
212                 build_dir_phases.intersection(pargs):
213                 portage.doebuild_environment(ebuild, "setup", portage.root,
214                         tmpsettings, debug, 1, portage.portdb)
215                 env_filename = os.path.join(tmpsettings["T"], "environment")
216                 if os.path.exists(env_filename):
217                         msg = ("Existing ${T}/environment for '%s' will be sourced. " + \
218                                 "Run 'clean' to start with a fresh environment.") % \
219                                 (tmpsettings["PF"], )
220                         from textwrap import wrap
221                         msg = wrap(msg, 70)
222                         for x in msg:
223                                 portage.writemsg(">>> %s\n" % x)
224
225                         if ebuild_changed:
226                                 open(os.path.join(tmpsettings['PORTAGE_BUILDDIR'],
227                                         '.ebuild_changed'), 'w')
228
229 from portage.exception import PermissionDenied, \
230         PortagePackageException, UnsupportedAPIException
231 checked_for_stale_env = False
232
233 for arg in pargs:
234         try:
235                 if not checked_for_stale_env and arg not in ("digest","manifest"):
236                         # This has to go after manifest generation since otherwise
237                         # aux_get() might fail due to invalid ebuild digests.
238                         stale_env_warning()
239                         checked_for_stale_env = True
240
241                 if arg in ("digest", "manifest") and force:
242                         discard_digests(ebuild, tmpsettings, portage.portdb)
243                 a = portage.doebuild(ebuild, arg, portage.root, tmpsettings,
244                         debug=debug, tree=mytree)
245         except KeyboardInterrupt:
246                 print "Interrupted."
247                 a = 1
248         except KeyError:
249                 # aux_get error
250                 a = 1
251         except UnsupportedAPIException, e:
252                 from textwrap import wrap
253                 msg = wrap(str(e), 70)
254                 del e
255                 for x in msg:
256                         portage.writemsg("!!! %s\n" % x, noiselevel=-1)
257                 a = 1
258         except PortagePackageException, e:
259                 portage.writemsg("!!! %s\n" % (e,), noiselevel=-1)
260                 a = 1
261         except PermissionDenied, e:
262                 portage.writemsg("!!! Permission Denied: %s\n" % (e,), noiselevel=-1)
263                 a = 1
264         if a == None:
265                 print "Could not run the required binary?"
266                 a = 127
267         if a:
268                 sys.exit(a)