2 # This script makes the declaration of builds declarative. In the past, our build system involved constructing a set of command lines
3 # that would get executed with parameters such as the name of the distribution, the kernel version and so on. Unfortunately, the code
4 # that went into creating these command lines was shared between people and often when someone modified his build, other builds would
5 # break. With this script, each build is declared as a Python dict, such as in the following example:
7 # caglars_k32_build = {
8 # 'tags':'planetlab-k32-tags.mk',
9 # 'fcdistro':['centos5', 'f12','f8'],
10 # 'personality':['linux32','linux64'],
15 # This declaration corresponds to 6 builds - with static values of 'tags', 'test' and 'release' and every combination of the values provided for
16 # 'fcdistro' and 'personality', i.e. 3x2.
18 # More complex dependencies can be added, e.g. to build linux64 only for f12, you can set the values of the options to functions:
20 # caglars_k32_build = {
21 # 'tags':'planetlab-k32-tags.mk',
22 # 'fcdistro':['centos5', 'f12','f8'],
23 # 'personality': lambda build: if (build['fcdistro']=='f12') then return ['linux32', 'linux64'] else return ['linux32']
28 # Naturally, you can achieve the same result by breaking the above declaration into two dicts, rather than using only one
36 from optparse import OptionParser
38 PARALLEL_BUILD = False
40 # Assemble a list of builds from a single build spec
41 def interpret_build(build, param_names, current_concrete_build={}, concrete_build_list=[]):
43 concrete_build_list.extend([current_concrete_build])
45 (cur_param_name,remaining_param_names)=(param_names[0],param_names[1:])
46 cur_param = build[cur_param_name]
48 # If it's a list, produce a concrete build for each element of the list
49 if (type(cur_param)==type([])):
50 for value in cur_param:
51 new_concrete_build = current_concrete_build.copy()
52 new_concrete_build[cur_param_name] = value
53 concrete_build_list = interpret_build(build, remaining_param_names, new_concrete_build, concrete_build_list)
55 # If not, just tack on the value and move on
57 current_concrete_build[cur_param_name] = cur_param
58 concrete_build_list = interpret_build(build, remaining_param_names, current_concrete_build,concrete_build_list)
60 return concrete_build_list
63 # Fill in parameters that are not defined in __default_build__
64 def complete_build_spec_with_defaults (build, default_build):
65 for default_param in default_build.keys():
66 if (not build.has_key(default_param)):
67 build[default_param]=default_build[default_param]
71 # Turn a concrete build into a commandline
73 def concrete_build_to_commandline(concrete_build):
77 -b pl-%(fcdistro)s-%(arch)s-%(myplcversion)s-%(release)s-%(date)s
85 -w %(webpath)s/%(pldistro)s/%(fcdistro)s
86 %(runtests)s'''.replace('\n','')
88 cmdline = cmdline % concrete_build
90 purge_spaces = re.compile('\s+')
92 return purge_spaces.sub(' ', cmdline)
95 # reduce dependencies in a build
96 def reduce_dependencies(concrete_build):
97 for b in concrete_build.keys():
98 val = concrete_build[b]
99 if (type(val)==type(lambda x:x)):
100 concrete_build[b] = val(concrete_build)
101 return concrete_build
104 # Turn build parameter dicts into commandlines and execute them
105 def process_builds (builds, build_names, default_build, options):
106 for build_name in build_names:
107 build = complete_build_spec_with_defaults (builds[build_name], default_build)
108 concrete_builds_without_deps = interpret_build (build, build.keys(), {}, [])
109 concrete_builds = map(reduce_dependencies, concrete_builds_without_deps)
110 commandlines = map(concrete_build_to_commandline, concrete_builds)
111 for commandline in commandlines:
112 if PARALLEL_BUILD == True:
113 args = shlex.split(commandline)
114 subprocess.Popen(args)
115 # work around the vserver race
118 if (build_name.startswith(options.prefix) and not options.pretend):
119 os.system(commandline)
121 print "### Skipping the following build###\n"
125 parser = OptionParser()
126 parser.add_option("-c", "--config-file", dest="config_file",
127 help="Config file with build declarations", metavar="FILE", default = '/etc/build-conf-planetlab.py')
128 parser.add_option("-p", "--pretend",
129 dest="pretend", default=False, action="store_true",
130 help="don't run only print")
132 parser.add_option("-o", "--only-build", dest="prefix",
133 help="Only build declarations starting with this prefix", metavar="PREFIX", default = '')
135 (options, args) = parser.parse_args ()
137 config_file = options.config_file
141 execfile(config_file, builds)
143 raise IOError, "Could not open %s\n" % config_file
146 config_file_attributes = builds.keys()
147 build_names = [e for e in config_file_attributes if not e.startswith('__')]
150 default_build = builds['__default_build__']
152 raise KeyError, "Please define the default build config in %s\n" % config_file
154 process_builds(builds, build_names, default_build, options)
157 if __name__ == "__main__":