blob: b92b4755e6165c227942e10d20678719a0d300a8 [file] [log] [blame]
Simon Glass50883f92011-07-12 16:19:16 -07001#!/usr/bin/python
2
3# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""This implements calling of scripts and other utilities/tools.
8
9We support running inside and outside the chroot. To do this, we permit
10a ## prefix which resolves to the chroot. Within the chroot this will be
11/ but outside it will be the full path to the chroot.
12
13So we can use filenames like this:
14
15 ##/usr/share/vboot/bitmaps/make_bmp_images.sh
16
17"""
18
19import optparse
20import os
21import sys
22
23from cros_build_lib import RunCommandCaptureOutput
24import cros_build_lib
25
26
27class CmdError(Exception):
28 """An error in the execution of a command."""
29 pass
30
31
32class Tools:
33 """A class to encapsulate the external tools we want to run
34
35 This provides convenient functions for running tools inside/outside the
36 chroot. It also provides common paths:
37
38 chroot_path: chroot directory
39 src_path: source directory
40 script_path: scripts directory (src/scripts)
41 overlay_path: overlays directory (src/overlays)
42 priv_overlay_path: private overlays directory (src/private-overlays)
43 board_path: build directory (/build in chroot)
44 third_party_path: third_parth directory (src/third_party)
45 cros_overlay_path: Chromium OS overlay (src/chromiumos-overlay)
46 """
47 def __init__(self, verbose):
48 """Set up the tools system.
49
50 Args:
51 in_chroot: True if in the chroot.
52 verbose: Verbosity level (0-4).
53 """
54 # Detect whether we're inside a chroot or not
55 self.in_chroot = cros_build_lib.IsInsideChroot()
56 self.verbose = verbose
57 repo = cros_build_lib.FindRepoDir()
58 if not repo:
59 raise IOError('Cannot find .repo directory (must be below cwd level)')
60 self._SetRoot(os.path.dirname(repo))
61
62 if verbose >= 3:
63 print "Chroot is at '%s'" % self.chroot_path
64 self._tools = {
65 'make_bmp_image' : '##/usr/share/vboot/bitmaps/make_bmp_images.sh'
66 }
67
68 def _SetRoot(self, root_dir):
69 """Sets the root directory for the build envionrment.
70
71 The root directory is the one containing .repo, chroot and src.
72
73 This should be called once the root is known. All other parts are
74 calculated from this.
75
76 Args:
77 dir: The path to the root directory.
78 """
79 self._root = os.path.normpath(root_dir)
80
81 # Set up the path to prepend to get to the chroot
82 if self.in_chroot:
83 self.chroot_path = '/'
84 else:
85 self.chroot_path = cros_build_lib.PrependChrootPath('')
86 self.src_path = os.path.join(self._root, 'src')
87 self.script_path = os.path.join(self.src_path, 'scripts')
88 self.overlay_path = os.path.join(self.src_path, 'overlays')
89 self.priv_overlay_path = os.path.join(self.src_path,
90 'private-overlays')
91 self.board_path = os.path.join(self.chroot_path, 'build')
92 self.third_party_path = os.path.join(self.src_path, 'third_party')
93 self.cros_overlay_path = os.path.join(self.third_party_path,
94 'chromiumos-overlay')
95
96 def Filename(self, fname):
97 """Resolve a chroot-relative filename to an absolute path.
98
99 This looks for ## at the beginning of the filename, and changes it to
100 the chroot directory, which will be / if inside the chroot, or a path
101 to the chroot if not.
102
103 Args:
104 fname: Filename to convert.
105
106 Returns
107 Absolute path to filename.
108 """
109 if fname.startswith('##/'):
110 fname = os.path.join(self.chroot_path, fname[3:])
111 return fname
112
113 def Run(self, tool, args, cwd=None):
114 """Run a tool with given arguments.
115
116 The tool name may be used unchanged or substituted with a full path if
117 required.
118
119 The tool and arguments can use ##/ to signify the chroot (at the beginning
120 of the tool/argument).
121
122 Args:
123 tool: Name of tool to run.
124 args: List of arguments to pass to tool.
125 cwd: Directory to change into before running tool (None if none).
126
127 Returns:
128 Output of tool (stdout).
129
130 Raises
131 CmdError if running the tool, or the tool itself creates an error"""
132 if tool in self._tools:
133 tool = self._tools[tool]
134 tool = self.Filename(tool)
135 args = [self.Filename(arg) for arg in args]
136 cmd = [tool] + args
137 try:
138 rc, out, err = RunCommandCaptureOutput(cmd, print_cmd=self.verbose > 3,
139 cwd=cwd)
140 except OSError:
141 raise CmdError('Command not found: %s' % (' '.join(cmd)))
142 if rc:
143 raise CmdError('Command failed: %s\n%s' % (' '.join(cmd), out))
144 if self.verbose > 3:
145 print out
146 return out
147
148 def ReadFile(self, fname):
149 """Read and return the contents of a file.
150
151 Args:
152 fname: path to filename to read, where ## signifiies the chroot.
153
154 Returns:
155 data read from file, as a string.
156 """
157 fd = open(self.Filename(fname), 'rb')
158 data = fd.read()
159 fd.close()
160 if self.verbose >= 3:
161 print "Read file '%s' size %d (%#0x)" % (fname, len(data), len(data))
162 return data
163
164 def WriteFile(self, fname, data):
165 """Write data into a file.
166
167 Args:
168 fname: path to filename to write, where ## signifiies the chroot.
169 data: data to write to file, as a string.
170 """
171 if self.verbose >= 3:
172 print "Write file '%s' size %d (%#0x)" % (fname, len(data), len(data))
173 fd = open(self.Filename(fname), 'wb')
174 fd.write(data)
175 fd.close()
176
177 def GetChromeosVersion(self):
178 """Returns the ChromeOS version string
179
180 This works by finding and executing the version script:
181
182 src/third_party/chromiumos-overlay/chromeos/config/chromeos_version.sh
183
184 Returns:
185 Version string in the form '0.14.726.2011_07_07_1635'
186
187 Raises:
188 CmdError: If the version script cannot be found, or is found but cannot
189 be executed.
190 """
191 version_script = os.path.join(self.cros_overlay_path, 'chromeos', 'config',
192 'chromeos_version.sh')
193
194 if os.path.exists(version_script):
195 str = self.Run('sh', ['-c', '. %s >/dev/null; '
196 'echo ${CHROMEOS_VERSION_STRING}'
197 % version_script])
198 return str.strip()
199 raise CmdError("Cannot find version script 'chromeos_version.sh'")
200
201 def CheckTool(self, filename, ebuild=None):
202 """Check that the specified tool exists.
203
204 If it does not exist in the PATH, then generate a useful error message
205 indicating how to install the ebuild that contains the required tool.
206
207 Args:
208 filename: filename of tool to look for on path.
209 ebuild: name of ebuild which should be emerged to install this tool,
210 or None if it is the same as the filename.
211
212 Raises:
213 CmdError(msg) if the tool is not found.
214 """
215 try:
216 if filename in self._tools:
217 filename = self._tools[filename]
218 filename = self.Filename(filename)
219 self.Run('which', [filename])
220 except CmdError as err:
221 raise CmdError("The '%s' utility was not found in your path. "
222 "Run the following command in \nyour chroot to install it: "
223 "sudo -E emerge %s" % (filename, ebuild or filename))
224
225def _Test():
226 """Run any built-in tests."""
227 import doctest
228 doctest.testmod()
229
230def main():
231 """Main function for tools.
232
233 We provide a way to call a few of our useful functions.
234
235 TODO(sjg) Move into the Chromite libraries when these are ready.
236 """
237 parser = optparse.OptionParser()
238 parser.add_option('-v', '--verbosity', dest='verbosity', default=1,
239 type='int', help='Control verbosity: 0=silent, 1=progress, 3=full, '
240 '4=debug')
241
242 help_str = '%s [options] cmd [args]\n\nAvailable commands:\n' % sys.argv[0]
243 help_str += '\tchromeos-version\tDisplay Chrome OS version'
244 parser.usage = help_str
245
246 (options, args) = parser.parse_args(sys.argv)
247 args = args[1:]
248
249 tools = Tools(options.verbosity)
250 if not args:
251 parser.error('No command provided')
252 elif args[0] == 'chromeos-version':
253 print tools.GetChromeosVersion()
254 else:
255 parser.error("Unknown command '%s'" % args[0])
256
257if __name__ == '__main__':
258 if sys.argv[1:2] == ["--test"]:
259 _Test(*sys.argv[2:])
260 else:
261 main()