rootfs.py: Typofixes in comments and prints.
[matrix.git] / matrix / rootfs.py
1 # Copyright (C) 2007-2008 Movial Oy
2 # Authors: Kalle Vahlman <kalle.vahlman@movial.fi>
3 #          Timo Savola <tsavola@movial.fi>
4
5 import errno
6 import optparse
7 import os
8 import re
9 import shutil
10 import sys
11
12 from config import config
13 from config import parse as config_parse
14
15 image_type_list = (
16         'jffs2',
17 )
18 image_type_default = 'jffs2'
19
20 def main():
21         parser = optparse.OptionParser(usage='%prog [<options>]')
22
23         parser.add_option(
24                 '-n', '--no-clean',
25                 help='do not clean env.faked and stripped directory',
26                 dest='clean', action='store_false', default=True)
27
28         parser.add_option(
29                 '-i', '--image-only',
30                 help='generate only a filesystem image',
31                 dest='image_only', action='store_true')
32
33         parser.add_option(
34                 '-T', '--image-type',
35                 help='type of generated filesystem image: %s [default: %s]' % \
36                      (', '.join(image_type_list), image_type_default),
37                 dest='image_type', metavar='TYPE',
38                 choices=image_type_list, default=image_type_default)
39
40         parser.add_option(
41                 '-r', '--rootfs-only',
42                 help='generate only a stripped archive',
43                 dest='rootfs_only', action='store_true')
44
45         parser.add_option(
46                 '-d', '--devrootfs-only',
47                 help='generate only an archive with development files',
48                 dest='devrootfs_only', action='store_true')
49
50         opts, args = parser.parse_args()
51         if args:
52                 parser.print_help()
53                 sys.exit(1)
54
55         config_parse('config.local')
56         config_parse('config')
57         config_parse('boards')
58         config_parse('rootfs')
59
60         f = os.popen('sb-conf cu')
61         target = f.read()
62         f.close()
63
64         build = Builder(target.strip())
65         build.include_paths(config.include_paths)
66         build.include_files(config.include_files)
67         build.filter_paths(config.exclude_paths)
68         build.filter_files(config.exclude_files)
69         build.filter_expressions(config.exclude_expressions)
70         build.add_paths(config.created_paths)
71         build.set_devices(config.devices)
72         build.set_change_owner(config.change_owner)
73         build.set_erase_size(config.boards[config.board].flash_erase_size)
74         build.set_pad_size(config.boards[config.board].flash_pad_size)
75
76         if opts.rootfs_only:
77                 build.generate(opts.clean, build_target="rootfs")
78         elif opts.image_only:
79                 build.generate(opts.clean, build_target=opts.image_type)
80         elif opts.devrootfs_only:
81                 build.generate(opts.clean, build_target="devrootfs")
82         else:
83                 build.generate(opts.clean, build_target="all")
84
85 class Builder(object):
86         builddir = "/tmp"
87         target = None
88
89         # Tools
90         strip = "strip"
91         mknod = "fakeroot -i /tmp/env.faked -s /tmp/env.faked mknod"
92         chmod = "fakeroot -i /tmp/env.faked -s /tmp/env.faked chmod"
93         chown = "fakeroot -i /tmp/env.faked -s /tmp/env.faked chown"
94         mkfs = "fakeroot -i /tmp/env.faked -s /tmp/env.faked mkfs.jffs2"
95         tar = "fakeroot -i /tmp/env.faked -s /tmp/env.faked tar"
96         flash_erase_size = 0x4000
97         flash_pad_size = 0x3e00000
98
99         file_list = []
100         remove_list = []
101
102         created_paths = []
103         devices = {}
104         chown_owner = {}
105
106         def __init__(self, mytarget):
107                 self.target = mytarget
108
109         def include_paths(self, paths):
110                 old_dir = os.getcwd()
111                 os.chdir("/targets")
112
113                 # Traverse include_paths hierarchies and grab all files there
114                 for path in paths:
115                         for root, dirs, files in os.walk(self.target + path):
116                                 for f in files:
117                                         self.file_list.append("%s/%s" % (root, f))
118                                 if len(files) == 0:
119                                         self.file_list.append(root)
120
121                 os.chdir(old_dir)
122
123         def include_files(self, files):
124                 old_dir = os.getcwd()
125                 os.chdir("/targets")
126
127                 # Append included individual files
128                 for f in files:
129                         self.file_list.append(self.target + f)
130
131                 os.chdir(old_dir)
132
133         def filter_paths(self, paths):
134                 # Filter out files in excluded paths
135                 for f in self.file_list:
136                         for d in paths:
137                                 if f == self.target + d or f.startswith(self.target + d + '/'):
138                                         self.remove_list.append(f)
139
140         def filter_files(self, files):
141                 # Filter out excluded files
142                 for f in files:
143                         self.remove_list.append(self.target + f)
144
145         def filter_expressions(self, expressions):
146                 # Filter out files matching excluded patterns
147                 compiled_patterns = []
148                 for pattern_str in expressions:
149                         compiled_patterns.append(re.compile(pattern_str))
150
151                 for f in self.file_list:
152                         for pattern in compiled_patterns:
153                                 if pattern.search(f):
154                                         self.remove_list.append(f)
155
156         def add_paths(self, paths):
157                 for path in paths:
158                         self.created_paths.append(path)
159
160         def set_devices(self, devices):
161                 if devices is not None:
162                         self.devices = devices
163
164         def set_change_owner(self, change_owner):
165                 if change_owner is not None:
166                         self.change_owner= change_owner
167
168         def set_erase_size(self, size):
169                 if size is not None:
170                         self.flash_erase_size = size
171
172         def set_pad_size(self, size):
173                 if size is not None:
174                         self.flash_pad_size = size
175
176         def generate(self, clean, build_target="all"):
177                 copy_list = self.file_list
178
179                 for f in self.remove_list:
180                                 try:
181                                         copy_list.remove(f)
182                                 except:
183                                         pass
184
185                 n_files = len(copy_list)*1.0
186                 current_percent = 0.0
187                 current_file = 0.0
188
189                 old_dir = os.getcwd()
190                 os.chdir("/targets")
191
192                 if os.path.exists(os.path.join(self.builddir, self.target)):
193                         print "%s exists, please remove it and try again" % os.path.join(self.builddir, self.target)
194                         return
195
196                 # Copy the wanted files, strip binaries
197                 print "Copying files..."
198                 for f in copy_list:
199                         if f[0] == '/':
200                                 f = f[1:]
201                         path = os.path.join(self.builddir, os.path.dirname(f))
202                         try:
203                                 os.makedirs(path)
204                         except OSError, e:
205                                 if e.errno == errno.EEXIST:
206                                         pass
207
208                         if os.path.islink(f):
209                                 link_dest = os.readlink(f)
210                                 os.symlink(link_dest, os.path.join(self.builddir, f))
211                         elif os.path.isdir(f):
212                                 try:
213                                         os.makedirs(os.path.join(self.builddir, f))
214                                 except OSError, e:
215                                         if e.errno == errno.EEXIST:
216                                                 pass
217                         else:
218                                 shutil.copy(f, path)
219                                 if not f.endswith(".ko"):
220                                         os.system(self.strip + " " + os.path.join(path,os.path.basename(f)) + " &> /dev/null")
221
222                         if (current_file/n_files)*100 > current_percent:
223                                 current_percent += 10.0
224                                 print "%0.1f%%" % current_percent,
225                                 sys.stdout.flush()
226
227                         current_file += 1.0
228
229                 # Create extra directory structure
230                 print "\nCreating extra directories..."
231                 for d in self.created_paths:
232                         if d[0] == '/':
233                                 d = d[1:]
234                         path = os.path.join(self.builddir, self.target, d)
235                         try:
236                                 os.makedirs(path)
237                         except OSError, e:
238                                 if e.errno == errno.EEXIST:
239                                         pass
240
241                 # Remove always the fakeroot environment.
242                 if os.path.exists("/tmp/env.faked"):
243                         os.remove("/tmp/env.faked")
244                 os.system("touch /tmp/env.faked")
245
246                 # Create device nodes
247                 print "Creating device nodes..."
248                 for d in self.devices.keys():
249                         mode = "664"
250                         if len(self.devices[d]) == 3:
251                                 device_type,major,minor = self.devices[d]
252                         else:
253                                 device_type,major,minor,mode = self.devices[d]
254                         os.system("%s -m %s %s/%s/dev/%s %s %i %i" % (self.mknod, mode, self.builddir, self.target, d, device_type, major, minor))
255
256                 # Fix permissions
257                 print "Adjusting ownerships and permissions..."
258                 # Make the whole fs root-owned
259                 os.system("%s root.root -R %s/%s" % (self.chown, self.builddir, self.target))
260
261                 # For sshd:
262                 os.system("%s go-r %s/%s/etc/ssh*key*" % (self.chmod, self.builddir, self.target))
263
264                 # For /var/empty (sshd requires this):
265                 os.system("%s go-rw /%s/%s/var/empty" % (self.chmod, self.builddir, self.target))
266
267                 # /dev/null needs to be writable by all:
268                 os.system("%s a+rw /%s/%s/dev/null" % (self.chmod, self.builddir, self.target))
269
270                 # Set non-root owners
271                 print "Setting non-root ownerships..."
272                 for d in self.change_owner.keys():
273                         dir = d
274                         if dir[0] == '/':
275                                 dir = dir[1:]
276                         if os.path.exists(os.path.join(self.builddir, self.target, dir)):
277                                 user,group = self.change_owner[d]
278                                 os.system("%s %s.%s -R %s/%s/%s " % (self.chown, user, group, self.builddir, self.target, dir))
279
280                 if build_target is "all" or build_target is "rootfs":
281                         print "Creating a rootfs..."
282                         os.system("%s -c --one-file-system -C %s/%s -z -f %s/%s.tgz ." % (self.tar, self.builddir, self.target, self.builddir, self.target))
283                 if build_target is "all" or build_target is "jffs2":
284                         print "Creating a root image..."
285                         os.system("%s -p%d -n -e%d -r %s/%s -o %s/%s.jffs2" % (self.mkfs, self.flash_pad_size, self.flash_erase_size, self.builddir, self.target, self.builddir, self.target))
286                 if build_target is "all" or build_target is "devrootfs":
287                         print "Creating a rootstrap..."
288                         os.system("%s -c --one-file-system -C /targets/%s -z -f %s/%s-rootstrap.tgz ." % ("tar", self.target, self.builddir, self.target))
289
290                 os.chdir(old_dir)
291
292                 print "Build finished:"
293                 if build_target is "all" or build_target is "rootfs":
294                         s = os.stat(self.builddir + "/" + self.target + ".tgz");
295                         print "\t%s/%s.tgz\t%0.2f MiB" % (self.builddir, self.target, s.st_size/1024.0/1024.0)
296                 if build_target is "all" or build_target is "jffs2":
297                         s = os.stat(self.builddir + "/" + self.target + ".jffs2");
298                         print "\t%s/%s.jffs2\t%0.2f MiB" % (self.builddir, self.target, s.st_size/1024.0/1024.0)
299                 if build_target is "all" or build_target is "devrootfs":
300                         s = os.stat(self.builddir + "/" + self.target + "-rootstrap.tgz");
301                         print "\t%s/%s-rootstrap.tgz\t%0.2f MiB" % (self.builddir, self.target, s.st_size/1024.0/1024.0)
302
303                 if clean:
304                         print "Cleaning up"
305                         os.remove("/tmp/env.faked")
306                         os.system("rm -rf %s/%s" % (self.builddir, self.target))