Automatically prepend build dir path to livecd/iso if it's not an absolute path
[catalyst.git] / catalyst
1 #!/usr/bin/python -OO
2
3 # Maintained in full by:
4 # Andrew Gaffney <agaffney@gentoo.org>
5 # Chris Gianelloni <wolf31o2@gentoo.org>
6
7 import os,sys,imp,string,getopt
8 import pdb
9
10 sys.path.append("./modules")
11
12 import catalyst.config
13 import catalyst.util
14
15 __maintainer__="Chris Gianelloni <wolf31o2@gentoo.org>"
16 __version__="2.0.6"
17
18 conf_values={}
19
20 def usage():
21         print "Usage catalyst [options] [-C variable=value...] [ -s identifier]"
22         print " -a --clear-autoresume   clear autoresume flags"
23         print " -c --config             use specified configuration file"
24         print " -C --cli                catalyst commandline (MUST BE LAST OPTION)"
25         print " -d --debug              enable debugging"
26         print " -f --file               read specfile"
27         print " -F --fetchonly          fetch files only"
28         print " -h --help               print this help message"
29         print " -p --purge              clear tmp dirs,package cache and autoresume flags"
30         print " -s --snapshot           generate a release snapshot"
31         print " -V --version            display version information"
32         print " -v --verbose            verbose output"
33         print
34         print "Usage examples:"
35         print
36         print "Using the commandline option (-C, --cli) to build a Portage snapshot:"
37         print "catalyst -C target=snapshot version_stamp=my_date"
38         print
39         print "Using the snapshot option (-s, --snapshot) to build a release snapshot:"
40         print "catalyst -s 20071121"
41         print
42         print "Using the specfile option (-f, --file) to build a stage target:"
43         print "catalyst -f stage1-specfile.spec"
44
45 def version():
46         print "Catalyst, version "+__version__
47         print "Copyright 2003-2008 Gentoo Foundation"
48         print "Copyright 2008 various authors"
49         print "Distributed under the GNU General Public License version 2.1\n"
50
51 def parse_config(myconfig):
52         # search a couple of different areas for the main config file
53         myconf={}
54         config_file=""
55
56         confdefaults={ "storedir":"/var/tmp/catalyst",\
57                 "sharedir":"/usr/share/catalyst","distdir":"/usr/portage/distfiles",\
58                 "portdir":"/usr/portage","options":"",\
59                 "snapshot_cache":"/var/tmp/catalyst/snapshot_cache",\
60                 "hash_function":"crc32"}
61                 
62         # first, try the one passed (presumably from the cmdline)
63         if myconfig:
64                 if os.path.exists(myconfig):
65                         print "Using command line specified Catalyst configuration file, "+myconfig
66                         config_file=myconfig
67
68                 else:
69                         print "!!! catalyst: Could not use specified configuration file "+\
70                                 myconfig
71                         sys.exit(1)
72         
73         # next, try the default location
74         elif os.path.exists("/etc/catalyst/catalyst.conf"):
75                 print "Using default Catalyst configuration file, /etc/catalyst/catalyst.conf"
76                 config_file="/etc/catalyst/catalyst.conf"
77         
78         # can't find a config file (we are screwed), so bail out
79         else:
80                 print "!!! catalyst: Could not find a suitable configuration file"
81                 sys.exit(1)
82
83         # now, try and parse the config file "config_file"
84         try:
85 #               execfile(config_file, myconf, myconf)
86                 myconfig = catalyst.config.ConfigParser(config_file)
87                 myconf.update(myconfig.get_values())
88         
89         except:
90                 print "!!! catalyst: Unable to parse configuration file, "+myconfig
91                 sys.exit(1)
92         
93         # now, load up the values into conf_values so that we can use them
94         for x in confdefaults.keys():
95                 if myconf.has_key(x):
96                         print "Setting",x,"to config file value \""+myconf[x]+"\""
97                         conf_values[x]=myconf[x]
98                 else:
99                         print "Setting",x,"to default value \""+confdefaults[x]+"\""
100                         conf_values[x]=confdefaults[x]
101
102         # parse out the rest of the options from the config file
103         if "autoresume" in string.split(conf_values["options"]):
104                 print "Autoresuming support enabled."
105                 conf_values["AUTORESUME"]="1"
106
107         if "ccache" in string.split(conf_values["options"]):
108                 print "Compiler cache support enabled."
109                 conf_values["CCACHE"]="1"
110
111         if "clear-autoresume" in string.split(conf_values["options"]):
112                 print "Cleaning autoresume flags support enabled."
113                 conf_values["CLEAR_AUTORESUME"]="1"
114
115 #       if "compress" in string.split(conf_values["options"]):
116 #               print "Compression enabled."
117 #               conf_values["COMPRESS"]="1"
118
119         if "distcc" in string.split(conf_values["options"]):
120                 print "Distcc support enabled."
121                 conf_values["DISTCC"]="1"
122
123         if "icecream" in string.split(conf_values["options"]):
124                 print "Icecream compiler cluster support enabled."
125                 conf_values["ICECREAM"]="1"
126
127         if "kerncache" in string.split(conf_values["options"]):
128                 print "Kernel cache support enabled."
129                 conf_values["KERNCACHE"]="1"
130
131         if "pkgcache" in string.split(conf_values["options"]):
132                 print "Package cache support enabled."
133                 conf_values["PKGCACHE"]="1"
134
135         if "purge" in string.split(conf_values["options"]):
136                 print "Purge support enabled."
137                 conf_values["PURGE"]="1"
138
139         if "seedcache" in string.split(conf_values["options"]):
140                 print "Seed cache support enabled."
141                 conf_values["SEEDCACHE"]="1"
142
143         if "snapcache" in string.split(conf_values["options"]):
144                 print "Snapshot cache support enabled."
145                 conf_values["SNAPCACHE"]="1"
146
147         if "metadata_overlay" in conf_values["options"].split():
148                 print "Use of metadata_overlay module for portage enabled."
149                 conf_values["METADATA_OVERLAY"]="1"
150
151 #       if "tarball" in string.split(conf_values["options"]):
152 #               print "Tarball creation enabled."
153 #               conf_values["TARBALL"]="1"
154
155         if myconf.has_key("digests"):
156                 conf_values["digests"]=myconf["digests"]
157         if myconf.has_key("contents"):
158                 conf_values["contents"]=myconf["contents"]
159
160         if myconf.has_key("envscript"):
161                 print "Envscript support enabled."
162                 conf_values["ENVSCRIPT"]=myconf["envscript"]
163
164 def import_modules():
165         # import catalyst's own modules (i.e. catalyst_support and the arch modules)
166         targetmap={}
167
168         try:
169                 for x in required_build_targets:
170                         try:
171                                 fh=open(conf_values["sharedir"]+"/modules/"+x+".py")
172                                 module=imp.load_module(x,fh,"modules/"+x+".py",(".py","r",imp.PY_SOURCE))
173                                 fh.close()
174                 
175                         except IOError:
176                                 raise CatalystError,"Can't find "+x+".py plugin in "+\
177                                         conf_values.settings["sharedir"]+"/modules/"
178
179                 for x in valid_build_targets:
180                         try:
181                                 fh=open(conf_values["sharedir"]+"/modules/"+x+".py")
182                                 module=imp.load_module(x,fh,"modules/"+x+".py",(".py","r",imp.PY_SOURCE))
183                                 module.register(targetmap)
184                                 fh.close()
185                 
186                         except IOError:
187                                 raise CatalystError,"Can't find "+x+".py plugin in "+\
188                                         conf_values.settings["sharedir"]+"/modules/"
189
190         except ImportError:
191                 print "!!! catalyst: Python modules not found in "+\
192                         conf_values["sharedir"]+"/modules; exiting."
193                 sys.exit(1)
194
195         return targetmap
196
197 def build_target(addlargs, targetmap):
198         try:
199                 if not targetmap.has_key(addlargs["target"]):
200                         raise CatalystError,"Target \""+addlargs["target"]+"\" not available."
201                 
202                 mytarget=targetmap[addlargs["target"]](conf_values, addlargs)
203         
204                 mytarget.run()
205
206         except:
207                 catalyst.util.print_traceback()
208                 print "!!! catalyst: Error encountered during run of target " + addlargs["target"]
209                 sys.exit(1)
210
211 if __name__ == "__main__":
212         targetmap={}
213         
214         version()
215         if os.getuid() != 0:
216                 # catalyst cannot be run as a normal user due to chroots, mounts, etc
217                 print "!!! catalyst: This script requires root privileges to operate"
218                 sys.exit(2)
219
220         # we need some options in order to work correctly
221         if len(sys.argv) < 2:
222                 usage()
223                 sys.exit(2)
224
225         # parse out the command line arguments
226         try:
227                 opts,args = getopt.getopt(sys.argv[1:], "aphvdc:C:f:FVs:", ["purge","help", "version", "debug",\
228                         "clear-autoresume", "config=", "cli=", "file=", "fetch", "verbose","snapshot="])
229         
230         except getopt.GetoptError:
231                 usage()
232                 sys.exit(2)
233         
234         # defaults for commandline opts
235         debug=False
236         verbose=False
237         fetch=False
238         myconfig=""
239         myspecfile=""
240         mycmdline=[]
241         myopts=[]
242
243         # check preconditions
244         if len(opts) == 0:
245                 print "!!! catalyst: please specify one of either -f or -C\n"
246                 usage()
247                 sys.exit(2)
248         run=0   
249         for o, a in opts:
250                 if o in ("-h", "--help"):
251                         usage()
252                         sys.exit(1)
253                 
254                 if o in ("-V", "--version"):
255                         print "Catalyst version "+__version__
256                         sys.exit(1)
257
258                 if o in ("-d", "--debug"):
259                         if len(sys.argv) < 3:
260                                 print "!!! catalyst: please specify one of either -f or -C\n"
261                                 usage()
262                                 sys.exit(2)
263                         else:
264                                 conf_values["DEBUG"]="1"
265                                 conf_values["VERBOSE"]="1"
266
267                 if o in ("-c", "--config"):
268                         if len(sys.argv) < 3:
269                                 print "!!! catalyst: please specify one of either -f or -C\n"
270                                 usage()
271                                 sys.exit(2)
272                         else:
273                                 myconfig=a
274
275                 if o in ("-C", "--cli"):
276                         run=1   
277                         x=sys.argv.index(o)+1
278                         while x < len(sys.argv):
279                                 mycmdline.append(sys.argv[x])
280                                 x=x+1
281                         
282                 if o in ("-f", "--file"):
283                         run=1   
284                         myspecfile=a
285
286                 if o in ("-F", "--fetchonly"):
287                         if len(sys.argv) < 3:
288                                 print "!!! catalyst: please specify one of either -f or -C\n"
289                                 usage()
290                                 sys.exit(2)
291                         else:
292                                 conf_values["FETCH"]="1"
293                         
294                 if o in ("-v", "--verbose"):
295                         if len(sys.argv) < 3:
296                                 print "!!! catalyst: please specify one of either -f or -C\n"
297                                 usage()
298                                 sys.exit(2)
299                         else:
300                                 conf_values["VERBOSE"]="1"
301
302                 if o in ("-s", "--snapshot"):
303                         if len(sys.argv) < 3:
304                                 print "!!! catalyst: missing snapshot identifier\n"
305                                 usage()
306                                 sys.exit(2)
307                         else:
308                                 run=1
309                                 mycmdline.append("target=snapshot")
310                                 mycmdline.append("version_stamp="+a)
311                 
312                 if o in ("-p", "--purge"):
313                         if len(sys.argv) < 3:
314                                 print "!!! catalyst: please specify one of either -f or -C\n"
315                                 usage()
316                                 sys.exit(2)
317                         else:
318                                 conf_values["PURGE"]="1"
319                 if o in ("-a", "--clear-autoresume"):
320                         if len(sys.argv) < 3:
321                                 print "!!! catalyst: please specify one of either -f or -C\n"
322                                 usage()
323                                 sys.exit(2)
324                         else:
325                                 conf_values["CLEAR_AUTORESUME"]="1"
326         if run != 1:
327                 print "!!! catalyst: please specify one of either -f or -C\n"
328                 usage()
329                 sys.exit(2)
330
331         # import configuration file and import our main module using those settings
332         parse_config(myconfig)
333         sys.path.append(conf_values["sharedir"]+"/modules")
334         from catalyst_support import *
335         
336         # Start checking that digests are valid now that the hash_map was imported from catalyst_support
337         if conf_values.has_key("digests"):
338                 for i in conf_values["digests"].split():
339                         if not hash_map.has_key(i):
340                                 print
341                                 print i+" is not a valid digest entry"
342                                 print "Valid digest entries:"
343                                 print hash_map.keys()
344                                 print
345                                 print "Catalyst aborting...."
346                                 sys.exit(2)
347                         if find_binary(hash_map[i][1]) == None:
348                                 print
349                                 print "digest="+i
350                                 print "\tThe "+hash_map[i][1]+\
351                                         " binary was not found. It needs to be in your system path"
352                                 print
353                                 print "Catalyst aborting...."
354                                 sys.exit(2)
355         if conf_values.has_key("hash_function"):
356                 if not hash_map.has_key(conf_values["hash_function"]):
357                         print
358                         print conf_values["hash_function"]+" is not a valid hash_function entry"
359                         print "Valid hash_function entries:"
360                         print hash_map.keys()
361                         print
362                         print "Catalyst aborting...."
363                         sys.exit(2)
364                 if find_binary(hash_map[conf_values["hash_function"]][1]) == None:
365                         print
366                         print "hash_function="+conf_values["hash_function"]
367                         print "\tThe "+hash_map[conf_values["hash_function"]][1]+\
368                                 " binary was not found. It needs to be in your system path"
369                         print
370                         print "Catalyst aborting...."
371                         sys.exit(2)
372
373         # import the rest of the catalyst modules
374         targetmap=import_modules()
375
376         addlargs={}
377         
378         if myspecfile:
379                 spec = catalyst.config.SpecParser(myspecfile)
380                 addlargs.update(spec.get_values())
381         
382         if mycmdline:
383                 try:
384                         cmdline = catalyst.config.ConfigParser()
385                         cmdline.parse_lines(mycmdline)
386                         addlargs.update(cmdline.get_values())
387                 except CatalystError:
388                         print "!!! catalyst: Could not parse commandline, exiting."
389                         sys.exit(1)
390
391         if not addlargs.has_key("target"):
392                 raise CatalystError, "Required value \"target\" not specified."
393
394         # everything is setup, so the build is a go
395         try:
396                 build_target(addlargs, targetmap)
397                         
398         except CatalystError:
399                 print
400                 print "Catalyst aborting...."
401                 sys.exit(2)
402         except KeyboardInterrupt:
403                 print "\nCatalyst build aborted due to user interrupt ( Ctrl-C )"
404                 print
405                 print "Catalyst aborting...."
406                 sys.exit(2)
407         except LockInUse:
408                 print "Catalyst aborting...."
409                 sys.exit(2)
410         except:
411                 print "Catalyst aborting...."
412                 raise
413                 sys.exit(2)
414
415         #except CatalystError:
416         #       print
417         #       print "Catalyst aborting...."
418         #       sys.exit(2)
419         #except KeyError:
420         #       print "\nproblem with command line or spec file ( Key Error )"
421         #       print "Key: "+str(sys.exc_value)+" was not found"
422         #       print "Catalyst aborting...."
423         #       sys.exit(2)
424         #except UnboundLocalError:
425         #       print
426         #       print "UnboundLocalError: "+str(sys.exc_value)+" was not found"
427         #       raise
428         #       print
429         #       print "Catalyst aborting...."
430         #       sys.exit(2)