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