getting closer to working prototype
[catalyst.git] / catalyst
1 #!/usr/bin/python
2
3 import os,sys,imp,string
4
5 def die(msg=None):
6         if msg:
7                 print "catalyst: "+msg
8         sys.exit(1)
9
10 def warn(msg):
11         print "catalyst: "+msg
12
13 def usage():
14         print "usage: meep!"
15
16 if len(sys.argv)==1 or sys.argv[1] in ["-h","--help"]:
17         usage()
18         sys.exit(1)
19
20 def arg_parse(mydict, myvalids):
21         "very wimpy argument parsing, just for the prototype"
22         for x in sys.argv[1:]:
23                 foo=string.split(x,"=")
24                 if len(foo)!=2:
25                         die("Invalid arg syntax: "+x)
26                 else:
27                         if foo[0] not in myvalids:
28                                 die("Invalid arg name: "+foo[0])
29                         mydict[foo[0]]=foo[1]
30                         
31 def spec_dump(myspec):
32         for x in myspec.keys():
33                 print x+": "+repr(myspec[x])
34
35 """
36 Overview of catalyst operation
37 ==============================
38
39 * The program starts, and the local machine type is detected. 
40
41 * Based on this information, catalyst determines what kind of machine types
42   it can build for (amd64 and ia64 can build for x86 as well, for example.)
43   The appropriate arch plugins are loaded, which contain builder classes
44   for each supported sub-arch.
45
46 * Command-line arguments are parsed. If specified, a spec file is read.
47
48 * These build variables are stored in an internal "spec" object, which will
49   be a standard python dictionary. This spec dictionary contains all relevant
50   build-related information.
51
52 * The spec object is passed to the appropriate subarch builder constructor.
53   The subarch builder constructor updates the spec object with variables
54   relevant to the sub-arch (pentium4, g3, etc.)
55
56 * The spec object is passed to the appropriate target constructor.
57   The target constructor updates the spec object to contain data relevant
58   to the particular target (stage1, stage3, grp, etc.)
59
60 * The full data of the spec object is written to disc, so there is a complete
61   record of all variables that will be used to build what we're building.  This
62   will allow for another person to re-use this information to replicate our
63   work (it should be possible to distribute a spec file along with a portage
64   snapshot and a starter tarball, and our build can be replicated exactly on
65   any machine.) The spec object contains data like CFLAGS, CHOST, subarch,
66   mainarch, the profile used to build, and for GRP and LiveCDs the complete
67   package build list. This is important to allow work to be replicated. It's
68   possible that the stage1/2/3.sh and other scripts should be distributed as
69   well, to allow proper replication of work.
70
71 * The build process begins by calling the appropriate method of the builder
72   instance. This includes cleanup, setup of chroot, entering the chroot,
73   running the appropriate bash build script, checking for error conditions,
74   and finishing up.
75   
76 * The catalyst process is now complete :)
77 """
78
79 #This allows plugins to import modules in the /modules dir
80 sys.path.append(os.getcwd()+"/modules")
81
82 #map current machine information from uname() to the mainarch we are running
83 #under
84
85 machinemap={    "i386" : "x86",
86                 "i486" : "x86",
87                 "i586" : "x86",
88                 "i686" : "x86",
89                 "x86_64" : "amd64"
90         }
91
92 # map the mainarch we are running under to the mainarches we support for
93 # building stages and LiveCDs. (for example, on amd64, we can build stages for
94 # x86 or amd64.
95
96 targetmap={     "x86" : ["x86"],
97                 "amd64" : ["x86","amd64"]
98         }
99                 
100 mymachine=os.uname()[4]
101 if not machinemap.has_key(mymachine):
102         print "Unknown machine type:",mymachine
103         sys.exit(1)
104 hostarch=machinemap[mymachine]
105 print "Host architecture:",hostarch
106 print "Supported architectures for targets:",string.join(targetmap[hostarch])
107 print "Loading plugins:",
108 archmap={}
109 subarchmap={}
110 for x in targetmap[hostarch]:
111         fh=open("arch/"+x+".py")
112         #this next line loads the plugin as a module and assigns it to archmap[x]
113         archmap[x]=imp.load_module(x,fh,"arch/"+x+".py",(".py","r",imp.PY_SOURCE))
114         #this next line registers all the subarches supported in the plugin
115         archmap[x].register(subarchmap)
116         fh.close()      
117         print x,
118 print
119 print "Available subarches:",string.join(subarchmap.keys())
120
121 import targets
122 targetmap={}
123 targets.register(targetmap)
124
125 print "Available targets:",string.join(targetmap.keys())
126
127 if os.getuid()!=0:
128         #non-root callers can't go any further than here. 
129         die("This script requires root privileges to operate.") 
130
131 #the spec begins!
132 validspec=["version_stamp","target","subarch","rel_type","rel_version","snapshot","source_subpath"]
133 myspec={}
134
135 """
136 local variables from spec:
137
138 version_stamp                   20031016                                        user (from spec)
139 target                          stage3                                          user (from spec)
140 subarch                         pentium4                                        user (from spec)
141 rel_type                        default                                         user (from spec) (was BUILDTYPE)
142 rel_version                     1.4                                             user (from spec) (was MAINVERSION)
143 snapshot                        20031016                                        user (from spec)
144 source_subpath                  default-x86-1.4/stage2-pentium4-20031016        user (from spec)
145 """
146
147 arg_parse(myspec,validspec)
148 #need to verify that we have all required args here. Leaving this out for the prototype.
149 if myspec["subarch"] not in subarchmap.keys():
150         die("Sub-arch "+myspec["subarch"]+" not available.")
151
152 #call builder constructor:
153 mybuilder=subarchmap[myspec["subarch"]](myspec)
154 if myspec["target"] not in targetmap.keys():
155         die("Target "+myspec["target"]+" not available.")
156
157 #these would come from /etc/catalyst.conf:
158 myspec["storedir"]="/var/tmp/catalyst"
159 myspec["sharedir"]="/usr/share/catalyst"
160 #these would come from there too?:
161 myspec["distdir"]="/usr/portage/distfiles"
162 myspec["portdir"]="/usr/portage"
163
164 #call target constructor:
165 mytarget=targetmap[myspec["target"]](myspec)
166 print
167 spec_dump(myspec)
168
169 #to test this program, type:
170
171 # ./catalyst subarch=pentium4 version_stamp=20031016 target=stage3 rel_type=default rel_version=1.4 snapshot=20031016 source_subpath=default-x86-1.4/stage2-pentium4-20031016
172