Bug #225285 - Add a --skip-manifest option that disables all interaction
[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 # $Header: /var/cvsroot/gentoo-src/portage/bin/ebuild,v 1.18.2.3 2005/05/07 04:32:59 ferringb Exp $
5
6 import optparse
7 import os
8 import sys
9
10 description = "See the ebuild(1) man page for more info"
11 usage = "Usage: ebuild <ebuild file> <command> [command] ..."
12 parser = optparse.OptionParser(description=description, usage=usage)
13
14 force_help = "When used together with the digest or manifest " + \
15         "command, this option forces regeneration of digests for all " + \
16         "distfiles associated with the current ebuild. Any distfiles " + \
17         "that do not already exist in ${DISTDIR} will be automatically fetched."
18
19 parser.add_option("--force", help=force_help, action="store_true", dest="force")
20 parser.add_option("--debug", help="show debug output",
21         action="store_true", dest="debug")
22 parser.add_option("--skip-manifest", help="skip all manifest checks",
23         action="store_true", dest="skip_manifest")
24
25 opts, pargs = parser.parse_args(args=sys.argv[1:])
26
27 if len(pargs) < 2:
28         parser.error("missing required args")
29
30 debug = opts.debug
31 force = opts.force
32
33 if "merge" in pargs:
34         print "Disabling noauto in features... merge disables it. (qmerge doesn't)"
35         os.environ["FEATURES"] = os.environ.get("FEATURES", "") + " -noauto"
36
37 os.environ["PORTAGE_CALLER"]="ebuild"
38 try:
39         import portage
40 except ImportError:
41         from os import path as osp
42         sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym"))
43         import portage
44
45 import portage.util, portage.const
46 import portage.dep
47 portage.dep._dep_check_strict = True
48
49 # do this _after_ 'import portage' to prevent unnecessary tracing
50 if debug and "python-trace" in portage.features:
51         import portage.debug
52         portage.debug.set_trace(True)
53
54 if portage.settings["NOCOLOR"] in ("yes","true") or not sys.stdout.isatty():
55         portage.output.nocolor()
56
57 ebuild = pargs.pop(0)
58 if not os.path.isabs(ebuild):
59         mycwd = os.getcwd()
60         # Try to get the non-canonical path from the PWD evironment variable, since
61         # the canonical path returned from os.getcwd() may may be unusable in
62         # cases where the directory stucture is built from symlinks.
63         if "PWD" in os.environ and os.environ["PWD"] != mycwd and \
64                 os.path.realpath(os.environ["PWD"]) == mycwd:
65                 mycwd = portage.normalize_path(os.environ["PWD"])
66         ebuild = os.path.join(mycwd, ebuild)
67 ebuild = portage.normalize_path(ebuild)
68 # portdbapi uses the canonical path for the base of the portage tree, but
69 # subdirectories of the base can be built from symlinks (like crossdev does).
70 ebuild_portdir = os.path.realpath(
71         os.path.dirname(os.path.dirname(os.path.dirname(ebuild))))
72 ebuild = os.path.join(ebuild_portdir, *ebuild.split(os.path.sep)[-3:])
73
74 # Make sure that portdb.findname() returns the correct ebuild.
75 if ebuild_portdir not in portage.portdb.porttrees:
76         os.environ["PORTDIR_OVERLAY"] = \
77                 os.environ.get("PORTDIR_OVERLAY","") + " " + ebuild_portdir
78         print "Appending %s to PORTDIR_OVERLAY..." % ebuild_portdir
79         portage.close_portdbapi_caches()
80         reload(portage)
81 del portage.portdb.porttrees[1:]
82 if ebuild_portdir != portage.portdb.porttree_root:
83         portage.portdb.porttrees.append(ebuild_portdir)
84
85 if not os.path.exists(ebuild):
86         print "'%s' does not exist." % ebuild
87         sys.exit(1)
88
89 ebuild_split = ebuild.split("/")
90 del ebuild_split[-2]
91 cpv = "/".join(ebuild_split[-2:])[:-7]
92
93 if not portage.catpkgsplit(cpv):
94         print "!!! %s does not follow correct package syntax." % (cpv)
95         sys.exit(1)
96
97 if ebuild.startswith(portage.root + portage.const.VDB_PATH):
98         mytree = "vartree"
99
100         portage_ebuild = portage.db[portage.root][mytree].dbapi.findname(cpv)
101
102         if os.path.realpath(portage_ebuild) != ebuild:
103                 print "!!! Portage seems to think that %s is at %s" % (cpv, portage_ebuild)
104                 sys.exit(1)
105
106 else:
107         mytree = "porttree"
108
109         portage_ebuild = portage.portdb.findname(cpv)
110
111         if not portage_ebuild or portage_ebuild != ebuild:
112                 print "!!! %s does not seem to have a valid PORTDIR structure." % ebuild
113                 sys.exit(1)
114
115 if len(pargs) > 1 and "config" in pargs:
116         print "config must be called on it's own, not combined with any other phase"
117         sys.exit(1)
118
119 def discard_digests(myebuild, mysettings, mydbapi):
120         """Discard all distfiles digests for the given ebuild.  This is useful when
121         upstream has changed the identity of the distfiles and the user would
122         otherwise have to manually remove the Manifest and files/digest-* files in
123         order to ensure correct results."""
124         try:
125                 portage._doebuild_manifest_exempt_depend += 1
126                 pkgdir = os.path.dirname(myebuild)
127                 fetchlist_dict = portage.FetchlistDict(pkgdir, mysettings, mydbapi)
128                 cat, pkg = pkgdir.split(os.sep)[-2:]
129                 cpv = cat + "/" + os.path.basename(myebuild)[:-7]
130                 from portage.manifest import Manifest
131                 mf = Manifest(pkgdir, mysettings["DISTDIR"],
132                         fetchlist_dict=fetchlist_dict, manifest1_compat=False)
133                 mf.create(requiredDistfiles=None,
134                         assumeDistHashesSometimes=True, assumeDistHashesAlways=True)
135                 distfiles = fetchlist_dict[cpv]
136                 for myfile in distfiles:
137                         try:
138                                 del mf.fhashdict["DIST"][myfile]
139                         except KeyError:
140                                 pass
141                 mf.write()
142         finally:
143                 portage._doebuild_manifest_exempt_depend -= 1
144
145 tmpsettings = portage.config(clone=portage.settings)
146 if "test" in pargs:
147         # This variable is a signal to config.regenerate() to
148         # indicate that the test phase should be enabled regardless
149         # of problems such as masked "test" USE flag.
150         tmpsettings["EBUILD_FORCE_TEST"] = "1"
151         tmpsettings.backup_changes("EBUILD_FORCE_TEST")
152         if "test" not in tmpsettings.features:
153                 tmpsettings.features.append("test")
154                 tmpsettings.features.sort()
155                 tmpsettings["FEATURES"] = " ".join(tmpsettings.features)
156                 tmpsettings.backup_changes("FEATURES")
157
158 if opts.skip_manifest:
159         tmpsettings["EBUILD_SKIP_MANIFEST"] = "1"
160         tmpsettings.backup_changes("EBUILD_SKIP_MANIFEST")
161         portage._doebuild_manifest_exempt_depend += 1
162
163 build_dir_phases = set(["setup", "unpack", "compile",
164         "test", "install", "package", "rpm"])
165
166 def stale_env_warning():
167         if "clean" not in pargs and \
168                 "noauto" not in tmpsettings.features and \
169                 tmpsettings.get("PORTAGE_QUIET") != "1" and \
170                 build_dir_phases.intersection(pargs):
171                 portage.doebuild_environment(ebuild, "setup", portage.root,
172                         tmpsettings, debug, 1, portage.portdb)
173                 env_filename = os.path.join(tmpsettings["T"], "environment")
174                 if os.path.exists(env_filename):
175                         msg = ("Existing ${T}/environment for '%s' will be sourced. " + \
176                                 "Run 'clean' to start with a fresh environment.") % \
177                                 (tmpsettings["PF"], )
178                         from textwrap import wrap
179                         msg = wrap(msg, 70)
180                         for x in msg:
181                                 portage.writemsg(">>> %s\n" % x)
182
183 from portage.exception import UnsupportedAPIException
184 checked_for_stale_env = False
185
186 for arg in pargs:
187         try:
188                 if not checked_for_stale_env and arg not in ("digest","manifest"):
189                         # This has to go after manifest generation since otherwise
190                         # aux_get() might fail due to invalid ebuild digests.
191                         stale_env_warning()
192                         checked_for_stale_env = True
193
194                 if arg in ("digest", "manifest") and force:
195                         discard_digests(ebuild, tmpsettings, portage.portdb)
196                 a = portage.doebuild(ebuild, arg, portage.root, tmpsettings,
197                         debug=debug, tree=mytree)
198         except KeyboardInterrupt:
199                 print "Interrupted."
200                 a = 1
201         except KeyError:
202                 # aux_get error
203                 a = 1
204         except UnsupportedAPIException, e:
205                 from textwrap import wrap
206                 msg = wrap(str(e), 70)
207                 del e
208                 for x in msg:
209                         portage.writemsg("!!! %s\n" % x, noiselevel=-1)
210                 a = 1
211         if a == None:
212                 print "Could not run the required binary?"
213                 a = 127
214         if a:
215                 sys.exit(a)