Clone of official tools

Committer:
The Other Jimmy
Date:
Thu Jun 22 11:12:28 2017 -0500
Revision:
36:96847d42f010
Parent:
31:8ea194f6145b
Child:
43:2a7da56ebd24
Tools release 5.5.1

Who changed what in which revision?

UserRevisionLine numberNew contents of line
screamer 0:66f3b5499f7f 1 """
screamer 0:66f3b5499f7f 2 mbed SDK
screamer 0:66f3b5499f7f 3 Copyright (c) 2011-2013 ARM Limited
screamer 0:66f3b5499f7f 4
screamer 0:66f3b5499f7f 5 Licensed under the Apache License, Version 2.0 (the "License");
screamer 0:66f3b5499f7f 6 you may not use this file except in compliance with the License.
screamer 0:66f3b5499f7f 7 You may obtain a copy of the License at
screamer 0:66f3b5499f7f 8
screamer 0:66f3b5499f7f 9 http://www.apache.org/licenses/LICENSE-2.0
screamer 0:66f3b5499f7f 10
screamer 0:66f3b5499f7f 11 Unless required by applicable law or agreed to in writing, software
screamer 0:66f3b5499f7f 12 distributed under the License is distributed on an "AS IS" BASIS,
screamer 0:66f3b5499f7f 13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
screamer 0:66f3b5499f7f 14 See the License for the specific language governing permissions and
screamer 0:66f3b5499f7f 15 limitations under the License.
screamer 0:66f3b5499f7f 16 """
screamer 0:66f3b5499f7f 17 import sys
screamer 0:66f3b5499f7f 18 import inspect
screamer 0:66f3b5499f7f 19 import os
screamer 22:9e85236d8716 20 import argparse
screamer 22:9e85236d8716 21 import math
screamer 0:66f3b5499f7f 22 from os import listdir, remove, makedirs
screamer 0:66f3b5499f7f 23 from shutil import copyfile
screamer 29:1210849dba19 24 from os.path import isdir, join, exists, split, relpath, splitext, abspath
The Other Jimmy 31:8ea194f6145b 25 from os.path import commonprefix, normpath, dirname
screamer 0:66f3b5499f7f 26 from subprocess import Popen, PIPE, STDOUT, call
The Other Jimmy 31:8ea194f6145b 27 from math import ceil
screamer 7:5af61d55adbe 28 import json
screamer 7:5af61d55adbe 29 from collections import OrderedDict
screamer 19:3604ee113e2d 30 import logging
The Other Jimmy 36:96847d42f010 31 from intelhex import IntelHex
screamer 0:66f3b5499f7f 32
The Other Jimmy 31:8ea194f6145b 33 def remove_if_in(lst, thing):
The Other Jimmy 31:8ea194f6145b 34 if thing in lst:
The Other Jimmy 31:8ea194f6145b 35 lst.remove(thing)
The Other Jimmy 31:8ea194f6145b 36
screamer 21:4fdf0dd04f6f 37 def compile_worker(job):
screamer 29:1210849dba19 38 """Standard task runner used for compiling
screamer 29:1210849dba19 39
screamer 29:1210849dba19 40 Positional argumets:
screamer 29:1210849dba19 41 job - a dict containing a list of commands and the remaining arguments
screamer 29:1210849dba19 42 to run_cmd
screamer 29:1210849dba19 43 """
screamer 21:4fdf0dd04f6f 44 results = []
screamer 21:4fdf0dd04f6f 45 for command in job['commands']:
screamer 21:4fdf0dd04f6f 46 try:
screamer 29:1210849dba19 47 _, _stderr, _rc = run_cmd(command, work_dir=job['work_dir'],
screamer 29:1210849dba19 48 chroot=job['chroot'])
screamer 29:1210849dba19 49 except KeyboardInterrupt:
screamer 21:4fdf0dd04f6f 50 raise ToolException
screamer 21:4fdf0dd04f6f 51
screamer 21:4fdf0dd04f6f 52 results.append({
screamer 21:4fdf0dd04f6f 53 'code': _rc,
screamer 21:4fdf0dd04f6f 54 'output': _stderr,
screamer 21:4fdf0dd04f6f 55 'command': command
screamer 21:4fdf0dd04f6f 56 })
screamer 21:4fdf0dd04f6f 57
screamer 21:4fdf0dd04f6f 58 return {
screamer 21:4fdf0dd04f6f 59 'source': job['source'],
screamer 21:4fdf0dd04f6f 60 'object': job['object'],
screamer 21:4fdf0dd04f6f 61 'commands': job['commands'],
screamer 21:4fdf0dd04f6f 62 'results': results
screamer 21:4fdf0dd04f6f 63 }
screamer 21:4fdf0dd04f6f 64
screamer 29:1210849dba19 65 def cmd(command, check=True, verbose=False, shell=False, cwd=None):
screamer 29:1210849dba19 66 """A wrapper to run a command as a blocking job"""
screamer 29:1210849dba19 67 text = command if shell else ' '.join(command)
screamer 0:66f3b5499f7f 68 if verbose:
screamer 0:66f3b5499f7f 69 print text
screamer 29:1210849dba19 70 return_code = call(command, shell=shell, cwd=cwd)
screamer 29:1210849dba19 71 if check and return_code != 0:
screamer 29:1210849dba19 72 raise Exception('ERROR %d: "%s"' % (return_code, text))
screamer 0:66f3b5499f7f 73
screamer 0:66f3b5499f7f 74
screamer 17:04753e1e329d 75 def run_cmd(command, work_dir=None, chroot=None, redirect=False):
screamer 29:1210849dba19 76 """Run a command in the forground
screamer 29:1210849dba19 77
screamer 29:1210849dba19 78 Positional arguments:
screamer 29:1210849dba19 79 command - the command to run
screamer 29:1210849dba19 80
screamer 29:1210849dba19 81 Keyword arguments:
screamer 29:1210849dba19 82 work_dir - the working directory to run the command in
screamer 29:1210849dba19 83 chroot - the chroot to run the command in
screamer 29:1210849dba19 84 redirect - redirect the stderr to a pipe to be used later
screamer 29:1210849dba19 85 """
screamer 17:04753e1e329d 86 if chroot:
screamer 17:04753e1e329d 87 # Conventions managed by the web team for the mbed.org build system
screamer 17:04753e1e329d 88 chroot_cmd = [
screamer 17:04753e1e329d 89 '/usr/sbin/chroot', '--userspec=33:33', chroot
screamer 17:04753e1e329d 90 ]
screamer 29:1210849dba19 91 for element in command:
screamer 29:1210849dba19 92 chroot_cmd += [element.replace(chroot, '')]
screamer 17:04753e1e329d 93
screamer 29:1210849dba19 94 logging.debug("Running command %s", ' '.join(chroot_cmd))
screamer 17:04753e1e329d 95 command = chroot_cmd
screamer 21:4fdf0dd04f6f 96 work_dir = None
screamer 17:04753e1e329d 97
screamer 0:66f3b5499f7f 98 try:
screamer 29:1210849dba19 99 process = Popen(command, stdout=PIPE,
screamer 29:1210849dba19 100 stderr=STDOUT if redirect else PIPE, cwd=work_dir)
screamer 29:1210849dba19 101 _stdout, _stderr = process.communicate()
screamer 29:1210849dba19 102 except OSError:
screamer 0:66f3b5499f7f 103 print "[OS ERROR] Command: "+(' '.join(command))
screamer 0:66f3b5499f7f 104 raise
screamer 13:ab47a20b66f0 105
screamer 29:1210849dba19 106 return _stdout, _stderr, process.returncode
screamer 0:66f3b5499f7f 107
screamer 0:66f3b5499f7f 108
screamer 0:66f3b5499f7f 109 def run_cmd_ext(command):
screamer 29:1210849dba19 110 """ A version of run command that checks if the command exists befor running
screamer 29:1210849dba19 111
screamer 29:1210849dba19 112 Positional arguments:
screamer 29:1210849dba19 113 command - the command line you are trying to invoke
screamer 29:1210849dba19 114 """
screamer 0:66f3b5499f7f 115 assert is_cmd_valid(command[0])
screamer 29:1210849dba19 116 process = Popen(command, stdout=PIPE, stderr=PIPE)
screamer 29:1210849dba19 117 _stdout, _stderr = process.communicate()
screamer 29:1210849dba19 118 return _stdout, _stderr, process.returncode
screamer 0:66f3b5499f7f 119
screamer 0:66f3b5499f7f 120
screamer 29:1210849dba19 121 def is_cmd_valid(command):
screamer 29:1210849dba19 122 """ Verify that a command exists and is executable
screamer 29:1210849dba19 123
screamer 29:1210849dba19 124 Positional arguments:
screamer 29:1210849dba19 125 command - the command to check
screamer 29:1210849dba19 126 """
screamer 0:66f3b5499f7f 127 caller = get_caller_name()
screamer 29:1210849dba19 128 cmd_path = find_cmd_abspath(command)
screamer 29:1210849dba19 129 if not cmd_path:
screamer 29:1210849dba19 130 error("%s: Command '%s' can't be found" % (caller, command))
screamer 29:1210849dba19 131 if not is_exec(cmd_path):
screamer 29:1210849dba19 132 error("%s: Command '%s' resolves to file '%s' which is not executable"
screamer 29:1210849dba19 133 % (caller, command, cmd_path))
screamer 0:66f3b5499f7f 134 return True
screamer 0:66f3b5499f7f 135
screamer 0:66f3b5499f7f 136
screamer 0:66f3b5499f7f 137 def is_exec(path):
screamer 29:1210849dba19 138 """A simple check to verify that a path to an executable exists
screamer 29:1210849dba19 139
screamer 29:1210849dba19 140 Positional arguments:
screamer 29:1210849dba19 141 path - the executable
screamer 29:1210849dba19 142 """
screamer 0:66f3b5499f7f 143 return os.access(path, os.X_OK) or os.access(path+'.exe', os.X_OK)
screamer 0:66f3b5499f7f 144
screamer 0:66f3b5499f7f 145
screamer 29:1210849dba19 146 def find_cmd_abspath(command):
screamer 0:66f3b5499f7f 147 """ Returns the absolute path to a command.
screamer 0:66f3b5499f7f 148 None is returned if no absolute path was found.
screamer 29:1210849dba19 149
screamer 29:1210849dba19 150 Positional arguhments:
screamer 29:1210849dba19 151 command - the command to find the path of
screamer 0:66f3b5499f7f 152 """
screamer 29:1210849dba19 153 if exists(command) or exists(command + '.exe'):
screamer 29:1210849dba19 154 return os.path.abspath(command)
screamer 0:66f3b5499f7f 155 if not 'PATH' in os.environ:
screamer 29:1210849dba19 156 raise Exception("Can't find command path for current platform ('%s')"
screamer 29:1210849dba19 157 % sys.platform)
screamer 29:1210849dba19 158 path_env = os.environ['PATH']
screamer 29:1210849dba19 159 for path in path_env.split(os.pathsep):
screamer 29:1210849dba19 160 cmd_path = '%s/%s' % (path, command)
screamer 29:1210849dba19 161 if exists(cmd_path) or exists(cmd_path + '.exe'):
screamer 29:1210849dba19 162 return cmd_path
screamer 0:66f3b5499f7f 163
screamer 0:66f3b5499f7f 164
screamer 0:66f3b5499f7f 165 def mkdir(path):
screamer 29:1210849dba19 166 """ a wrapped makedirs that only tries to create a directory if it does not
screamer 29:1210849dba19 167 exist already
screamer 29:1210849dba19 168
screamer 29:1210849dba19 169 Positional arguments:
screamer 29:1210849dba19 170 path - the path to maybe create
screamer 29:1210849dba19 171 """
screamer 0:66f3b5499f7f 172 if not exists(path):
screamer 0:66f3b5499f7f 173 makedirs(path)
screamer 0:66f3b5499f7f 174
screamer 0:66f3b5499f7f 175
screamer 0:66f3b5499f7f 176 def copy_file(src, dst):
screamer 0:66f3b5499f7f 177 """ Implement the behaviour of "shutil.copy(src, dst)" without copying the
screamer 29:1210849dba19 178 permissions (this was causing errors with directories mounted with samba)
screamer 29:1210849dba19 179
screamer 29:1210849dba19 180 Positional arguments:
screamer 29:1210849dba19 181 src - the source of the copy operation
screamer 29:1210849dba19 182 dst - the destination of the copy operation
screamer 0:66f3b5499f7f 183 """
screamer 0:66f3b5499f7f 184 if isdir(dst):
screamer 29:1210849dba19 185 _, base = split(src)
screamer 29:1210849dba19 186 dst = join(dst, base)
screamer 0:66f3b5499f7f 187 copyfile(src, dst)
screamer 0:66f3b5499f7f 188
screamer 0:66f3b5499f7f 189
screamer 29:1210849dba19 190 def delete_dir_files(directory):
screamer 29:1210849dba19 191 """ A function that does rm -rf
screamer 29:1210849dba19 192
screamer 29:1210849dba19 193 Positional arguments:
screamer 29:1210849dba19 194 directory - the directory to remove
screamer 29:1210849dba19 195 """
screamer 29:1210849dba19 196 if not exists(directory):
screamer 0:66f3b5499f7f 197 return
screamer 0:66f3b5499f7f 198
screamer 29:1210849dba19 199 for element in listdir(directory):
screamer 29:1210849dba19 200 to_remove = join(directory, element)
screamer 29:1210849dba19 201 if not isdir(to_remove):
screamer 0:66f3b5499f7f 202 remove(file)
screamer 0:66f3b5499f7f 203
screamer 0:66f3b5499f7f 204
screamer 0:66f3b5499f7f 205 def get_caller_name(steps=2):
screamer 0:66f3b5499f7f 206 """
screamer 0:66f3b5499f7f 207 When called inside a function, it returns the name
screamer 0:66f3b5499f7f 208 of the caller of that function.
screamer 29:1210849dba19 209
screamer 29:1210849dba19 210 Keyword arguments:
screamer 29:1210849dba19 211 steps - the number of steps up the stack the calling function is
screamer 0:66f3b5499f7f 212 """
screamer 0:66f3b5499f7f 213 return inspect.stack()[steps][3]
screamer 0:66f3b5499f7f 214
screamer 0:66f3b5499f7f 215
screamer 0:66f3b5499f7f 216 def error(msg):
screamer 29:1210849dba19 217 """Fatal error, abort hard
screamer 29:1210849dba19 218
screamer 29:1210849dba19 219 Positional arguments:
screamer 29:1210849dba19 220 msg - the message to print before crashing
screamer 29:1210849dba19 221 """
screamer 0:66f3b5499f7f 222 print("ERROR: %s" % msg)
screamer 0:66f3b5499f7f 223 sys.exit(1)
screamer 0:66f3b5499f7f 224
screamer 0:66f3b5499f7f 225
screamer 0:66f3b5499f7f 226 def rel_path(path, base, dot=False):
screamer 29:1210849dba19 227 """Relative path calculation that optionaly always starts with a dot
screamer 29:1210849dba19 228
screamer 29:1210849dba19 229 Positional arguments:
screamer 29:1210849dba19 230 path - the path to make relative
screamer 29:1210849dba19 231 base - what to make the path relative to
screamer 29:1210849dba19 232
screamer 29:1210849dba19 233 Keyword arguments:
screamer 29:1210849dba19 234 dot - if True, the path will always start with a './'
screamer 29:1210849dba19 235 """
screamer 29:1210849dba19 236 final_path = relpath(path, base)
screamer 29:1210849dba19 237 if dot and not final_path.startswith('.'):
screamer 29:1210849dba19 238 final_path = './' + final_path
screamer 29:1210849dba19 239 return final_path
screamer 0:66f3b5499f7f 240
screamer 0:66f3b5499f7f 241
screamer 0:66f3b5499f7f 242 class ToolException(Exception):
screamer 29:1210849dba19 243 """A class representing an exception throw by the tools"""
screamer 0:66f3b5499f7f 244 pass
screamer 0:66f3b5499f7f 245
screamer 0:66f3b5499f7f 246 class NotSupportedException(Exception):
screamer 29:1210849dba19 247 """A class a toolchain not supporting a particular target"""
screamer 0:66f3b5499f7f 248 pass
screamer 0:66f3b5499f7f 249
screamer 24:25bff2709c20 250 class InvalidReleaseTargetException(Exception):
screamer 24:25bff2709c20 251 pass
screamer 24:25bff2709c20 252
screamer 0:66f3b5499f7f 253 def split_path(path):
screamer 29:1210849dba19 254 """spilt a file name into it's directory name, base name, and extension
screamer 29:1210849dba19 255
screamer 29:1210849dba19 256 Positional arguments:
screamer 29:1210849dba19 257 path - the file name to split
screamer 29:1210849dba19 258 """
screamer 29:1210849dba19 259 base, has_ext = split(path)
screamer 29:1210849dba19 260 name, ext = splitext(has_ext)
screamer 0:66f3b5499f7f 261 return base, name, ext
screamer 0:66f3b5499f7f 262
screamer 0:66f3b5499f7f 263
screamer 24:25bff2709c20 264 def get_path_depth(path):
screamer 24:25bff2709c20 265 """ Given a path, return the number of directory levels present.
screamer 24:25bff2709c20 266 This roughly translates to the number of path separators (os.sep) + 1.
screamer 24:25bff2709c20 267 Ex. Given "path/to/dir", this would return 3
screamer 24:25bff2709c20 268 Special cases: "." and "/" return 0
screamer 29:1210849dba19 269
screamer 29:1210849dba19 270 Positional arguments:
screamer 29:1210849dba19 271 path - the path to calculate the depth of
screamer 24:25bff2709c20 272 """
screamer 24:25bff2709c20 273 normalized_path = normpath(path)
screamer 24:25bff2709c20 274 path_depth = 0
screamer 24:25bff2709c20 275 head, tail = split(normalized_path)
screamer 24:25bff2709c20 276
screamer 29:1210849dba19 277 while tail and tail != '.':
screamer 24:25bff2709c20 278 path_depth += 1
screamer 24:25bff2709c20 279 head, tail = split(head)
screamer 24:25bff2709c20 280
screamer 24:25bff2709c20 281 return path_depth
screamer 24:25bff2709c20 282
screamer 24:25bff2709c20 283
screamer 0:66f3b5499f7f 284 def args_error(parser, message):
screamer 29:1210849dba19 285 """Abort with an error that was generated by the arguments to a CLI program
screamer 29:1210849dba19 286
screamer 29:1210849dba19 287 Positional arguments:
screamer 29:1210849dba19 288 parser - the ArgumentParser object that parsed the command line
screamer 29:1210849dba19 289 message - what went wrong
screamer 29:1210849dba19 290 """
The Other Jimmy 31:8ea194f6145b 291 parser.error(message)
The Other Jimmy 31:8ea194f6145b 292 sys.exit(2)
screamer 0:66f3b5499f7f 293
screamer 0:66f3b5499f7f 294
screamer 0:66f3b5499f7f 295 def construct_enum(**enums):
screamer 29:1210849dba19 296 """ Create your own pseudo-enums
screamer 29:1210849dba19 297
screamer 29:1210849dba19 298 Keyword arguments:
screamer 29:1210849dba19 299 * - a member of the Enum you are creating and it's value
screamer 29:1210849dba19 300 """
screamer 0:66f3b5499f7f 301 return type('Enum', (), enums)
screamer 0:66f3b5499f7f 302
screamer 0:66f3b5499f7f 303
screamer 0:66f3b5499f7f 304 def check_required_modules(required_modules, verbose=True):
screamer 29:1210849dba19 305 """ Function checks for Python modules which should be "importable"
screamer 0:66f3b5499f7f 306 before test suite can be used.
screamer 0:66f3b5499f7f 307 @return returns True if all modules are installed already
screamer 0:66f3b5499f7f 308 """
screamer 0:66f3b5499f7f 309 import imp
screamer 0:66f3b5499f7f 310 not_installed_modules = []
screamer 0:66f3b5499f7f 311 for module_name in required_modules:
screamer 0:66f3b5499f7f 312 try:
screamer 0:66f3b5499f7f 313 imp.find_module(module_name)
screamer 29:1210849dba19 314 except ImportError:
screamer 0:66f3b5499f7f 315 # We also test against a rare case: module is an egg file
screamer 0:66f3b5499f7f 316 try:
screamer 0:66f3b5499f7f 317 __import__(module_name)
screamer 29:1210849dba19 318 except ImportError as exc:
screamer 0:66f3b5499f7f 319 not_installed_modules.append(module_name)
screamer 0:66f3b5499f7f 320 if verbose:
screamer 29:1210849dba19 321 print "Error: %s" % exc
screamer 0:66f3b5499f7f 322
screamer 0:66f3b5499f7f 323 if verbose:
screamer 0:66f3b5499f7f 324 if not_installed_modules:
screamer 29:1210849dba19 325 print ("Warning: Module(s) %s not installed. Please install " + \
screamer 29:1210849dba19 326 "required module(s) before using this script.")\
screamer 29:1210849dba19 327 % (', '.join(not_installed_modules))
screamer 0:66f3b5499f7f 328
screamer 0:66f3b5499f7f 329 if not_installed_modules:
screamer 0:66f3b5499f7f 330 return False
screamer 0:66f3b5499f7f 331 else:
screamer 0:66f3b5499f7f 332 return True
screamer 7:5af61d55adbe 333
screamer 29:1210849dba19 334 def dict_to_ascii(dictionary):
screamer 29:1210849dba19 335 """ Utility function: traverse a dictionary and change all the strings in
screamer 29:1210849dba19 336 the dictionary to ASCII from Unicode. Useful when reading ASCII JSON data,
screamer 29:1210849dba19 337 because the JSON decoder always returns Unicode string. Based on
screamer 29:1210849dba19 338 http://stackoverflow.com/a/13105359
screamer 29:1210849dba19 339
screamer 29:1210849dba19 340 Positional arguments:
screamer 29:1210849dba19 341 dictionary - The dict that contains some Unicode that should be ASCII
screamer 29:1210849dba19 342 """
screamer 29:1210849dba19 343 if isinstance(dictionary, dict):
screamer 29:1210849dba19 344 return OrderedDict([(dict_to_ascii(key), dict_to_ascii(value))
screamer 29:1210849dba19 345 for key, value in dictionary.iteritems()])
screamer 29:1210849dba19 346 elif isinstance(dictionary, list):
screamer 29:1210849dba19 347 return [dict_to_ascii(element) for element in dictionary]
screamer 29:1210849dba19 348 elif isinstance(dictionary, unicode):
screamer 29:1210849dba19 349 return dictionary.encode('ascii')
screamer 7:5af61d55adbe 350 else:
screamer 29:1210849dba19 351 return dictionary
screamer 29:1210849dba19 352
screamer 29:1210849dba19 353 def json_file_to_dict(fname):
screamer 29:1210849dba19 354 """ Read a JSON file and return its Python representation, transforming all
screamer 29:1210849dba19 355 the strings from Unicode to ASCII. The order of keys in the JSON file is
screamer 29:1210849dba19 356 preserved.
screamer 7:5af61d55adbe 357
screamer 29:1210849dba19 358 Positional arguments:
screamer 29:1210849dba19 359 fname - the name of the file to parse
screamer 29:1210849dba19 360 """
screamer 22:9e85236d8716 361 try:
screamer 29:1210849dba19 362 with open(fname, "r") as file_obj:
screamer 29:1210849dba19 363 return dict_to_ascii(json.load(file_obj,
screamer 29:1210849dba19 364 object_pairs_hook=OrderedDict))
screamer 22:9e85236d8716 365 except (ValueError, IOError):
screamer 22:9e85236d8716 366 sys.stderr.write("Error parsing '%s':\n" % fname)
screamer 22:9e85236d8716 367 raise
screamer 22:9e85236d8716 368
screamer 22:9e85236d8716 369 # Wowza, double closure
screamer 29:1210849dba19 370 def argparse_type(casedness, prefer_hyphen=False):
screamer 29:1210849dba19 371 def middle(lst, type_name):
screamer 22:9e85236d8716 372 def parse_type(string):
screamer 29:1210849dba19 373 """ validate that an argument passed in (as string) is a member of
screamer 29:1210849dba19 374 the list of possible arguments. Offer a suggestion if the case of
screamer 29:1210849dba19 375 the string, or the hyphens/underscores do not match the expected
screamer 29:1210849dba19 376 style of the argument.
screamer 29:1210849dba19 377 """
screamer 29:1210849dba19 378 if prefer_hyphen:
screamer 29:1210849dba19 379 newstring = casedness(string).replace("_", "-")
screamer 29:1210849dba19 380 else:
screamer 29:1210849dba19 381 newstring = casedness(string).replace("-", "_")
screamer 29:1210849dba19 382 if string in lst:
screamer 22:9e85236d8716 383 return string
screamer 29:1210849dba19 384 elif string not in lst and newstring in lst:
screamer 29:1210849dba19 385 raise argparse.ArgumentTypeError(
screamer 29:1210849dba19 386 "{0} is not a supported {1}. Did you mean {2}?".format(
screamer 29:1210849dba19 387 string, type_name, newstring))
screamer 22:9e85236d8716 388 else:
screamer 29:1210849dba19 389 raise argparse.ArgumentTypeError(
screamer 29:1210849dba19 390 "{0} is not a supported {1}. Supported {1}s are:\n{2}".
screamer 29:1210849dba19 391 format(string, type_name, columnate(lst)))
screamer 22:9e85236d8716 392 return parse_type
screamer 22:9e85236d8716 393 return middle
screamer 22:9e85236d8716 394
screamer 22:9e85236d8716 395 # short cuts for the argparse_type versions
screamer 22:9e85236d8716 396 argparse_uppercase_type = argparse_type(str.upper, False)
screamer 22:9e85236d8716 397 argparse_lowercase_type = argparse_type(str.lower, False)
screamer 22:9e85236d8716 398 argparse_uppercase_hyphen_type = argparse_type(str.upper, True)
screamer 22:9e85236d8716 399 argparse_lowercase_hyphen_type = argparse_type(str.lower, True)
screamer 22:9e85236d8716 400
screamer 22:9e85236d8716 401 def argparse_force_type(case):
screamer 29:1210849dba19 402 """ validate that an argument passed in (as string) is a member of the list
screamer 29:1210849dba19 403 of possible arguments after converting it's case.
screamer 29:1210849dba19 404 """
screamer 29:1210849dba19 405 def middle(lst, type_name):
screamer 29:1210849dba19 406 """ The parser type generator"""
screamer 22:9e85236d8716 407 def parse_type(string):
screamer 29:1210849dba19 408 """ The parser type"""
screamer 29:1210849dba19 409 for option in lst:
screamer 22:9e85236d8716 410 if case(string) == case(option):
screamer 22:9e85236d8716 411 return option
screamer 29:1210849dba19 412 raise argparse.ArgumentTypeError(
screamer 29:1210849dba19 413 "{0} is not a supported {1}. Supported {1}s are:\n{2}".
screamer 29:1210849dba19 414 format(string, type_name, columnate(lst)))
screamer 22:9e85236d8716 415 return parse_type
screamer 22:9e85236d8716 416 return middle
screamer 22:9e85236d8716 417
screamer 22:9e85236d8716 418 # these two types convert the case of their arguments _before_ validation
screamer 22:9e85236d8716 419 argparse_force_uppercase_type = argparse_force_type(str.upper)
screamer 22:9e85236d8716 420 argparse_force_lowercase_type = argparse_force_type(str.lower)
screamer 22:9e85236d8716 421
screamer 29:1210849dba19 422 def argparse_many(func):
screamer 29:1210849dba19 423 """ An argument parser combinator that takes in an argument parser and
screamer 29:1210849dba19 424 creates a new parser that accepts a comma separated list of the same thing.
screamer 29:1210849dba19 425 """
screamer 22:9e85236d8716 426 def wrap(string):
screamer 29:1210849dba19 427 """ The actual parser"""
screamer 29:1210849dba19 428 return [func(s) for s in string.split(",")]
screamer 22:9e85236d8716 429 return wrap
screamer 22:9e85236d8716 430
screamer 29:1210849dba19 431 def argparse_filestring_type(string):
screamer 29:1210849dba19 432 """ An argument parser that verifies that a string passed in corresponds
screamer 29:1210849dba19 433 to a file"""
screamer 29:1210849dba19 434 if exists(string):
screamer 22:9e85236d8716 435 return string
screamer 29:1210849dba19 436 else:
screamer 29:1210849dba19 437 raise argparse.ArgumentTypeError(
screamer 29:1210849dba19 438 "{0}"" does not exist in the filesystem.".format(string))
screamer 29:1210849dba19 439
The Other Jimmy 31:8ea194f6145b 440 def argparse_profile_filestring_type(string):
The Other Jimmy 31:8ea194f6145b 441 """ An argument parser that verifies that a string passed in is either
The Other Jimmy 31:8ea194f6145b 442 absolute path or a file name (expanded to
The Other Jimmy 31:8ea194f6145b 443 mbed-os/tools/profiles/<fname>.json) of a existing file"""
The Other Jimmy 31:8ea194f6145b 444 fpath = join(dirname(__file__), "profiles/{}.json".format(string))
The Other Jimmy 31:8ea194f6145b 445 if exists(string):
The Other Jimmy 31:8ea194f6145b 446 return string
The Other Jimmy 31:8ea194f6145b 447 elif exists(fpath):
The Other Jimmy 31:8ea194f6145b 448 return fpath
The Other Jimmy 31:8ea194f6145b 449 else:
The Other Jimmy 31:8ea194f6145b 450 raise argparse.ArgumentTypeError(
The Other Jimmy 31:8ea194f6145b 451 "{0} does not exist in the filesystem.".format(string))
The Other Jimmy 31:8ea194f6145b 452
screamer 29:1210849dba19 453 def columnate(strings, separator=", ", chars=80):
screamer 29:1210849dba19 454 """ render a list of strings as a in a bunch of columns
screamer 22:9e85236d8716 455
screamer 29:1210849dba19 456 Positional arguments:
screamer 29:1210849dba19 457 strings - the strings to columnate
screamer 29:1210849dba19 458
screamer 29:1210849dba19 459 Keyword arguments;
screamer 29:1210849dba19 460 separator - the separation between the columns
screamer 29:1210849dba19 461 chars - the maximum with of a row
screamer 29:1210849dba19 462 """
screamer 22:9e85236d8716 463 col_width = max(len(s) for s in strings)
screamer 29:1210849dba19 464 total_width = col_width + len(separator)
screamer 22:9e85236d8716 465 columns = math.floor(chars / total_width)
screamer 22:9e85236d8716 466 output = ""
screamer 29:1210849dba19 467 for i, string in zip(range(len(strings)), strings):
screamer 29:1210849dba19 468 append = string
screamer 22:9e85236d8716 469 if i != len(strings) - 1:
screamer 29:1210849dba19 470 append += separator
screamer 22:9e85236d8716 471 if i % columns == columns - 1:
screamer 22:9e85236d8716 472 append += "\n"
screamer 22:9e85236d8716 473 else:
screamer 22:9e85236d8716 474 append = append.ljust(total_width)
screamer 22:9e85236d8716 475 output += append
screamer 22:9e85236d8716 476 return output
screamer 24:25bff2709c20 477
screamer 24:25bff2709c20 478 def argparse_dir_not_parent(other):
screamer 29:1210849dba19 479 """fail if argument provided is a parent of the specified directory"""
screamer 24:25bff2709c20 480 def parse_type(not_parent):
screamer 29:1210849dba19 481 """The parser type"""
screamer 24:25bff2709c20 482 abs_other = abspath(other)
screamer 24:25bff2709c20 483 abs_not_parent = abspath(not_parent)
screamer 24:25bff2709c20 484 if abs_not_parent == commonprefix([abs_not_parent, abs_other]):
screamer 29:1210849dba19 485 raise argparse.ArgumentTypeError(
screamer 29:1210849dba19 486 "{0} may not be a parent directory of {1}".format(
screamer 29:1210849dba19 487 not_parent, other))
screamer 24:25bff2709c20 488 else:
screamer 24:25bff2709c20 489 return not_parent
screamer 24:25bff2709c20 490 return parse_type
The Other Jimmy 31:8ea194f6145b 491
The Other Jimmy 31:8ea194f6145b 492 def argparse_deprecate(replacement_message):
The Other Jimmy 31:8ea194f6145b 493 """fail if argument is provided with deprecation warning"""
The Other Jimmy 31:8ea194f6145b 494 def parse_type(_):
The Other Jimmy 31:8ea194f6145b 495 """The parser type"""
The Other Jimmy 31:8ea194f6145b 496 raise argparse.ArgumentTypeError("Deprecated." + replacement_message)
The Other Jimmy 31:8ea194f6145b 497 return parse_type
The Other Jimmy 31:8ea194f6145b 498
The Other Jimmy 31:8ea194f6145b 499 def print_large_string(large_string):
The Other Jimmy 31:8ea194f6145b 500 """ Breaks a string up into smaller pieces before print them
The Other Jimmy 31:8ea194f6145b 501
The Other Jimmy 31:8ea194f6145b 502 This is a limitation within Windows, as detailed here:
The Other Jimmy 31:8ea194f6145b 503 https://bugs.python.org/issue11395
The Other Jimmy 31:8ea194f6145b 504
The Other Jimmy 31:8ea194f6145b 505 Positional arguments:
The Other Jimmy 31:8ea194f6145b 506 large_string - the large string to print
The Other Jimmy 31:8ea194f6145b 507 """
The Other Jimmy 31:8ea194f6145b 508 string_limit = 1000
The Other Jimmy 31:8ea194f6145b 509 large_string_len = len(large_string)
The Other Jimmy 31:8ea194f6145b 510 num_parts = int(ceil(float(large_string_len) / float(string_limit)))
The Other Jimmy 31:8ea194f6145b 511 for string_part in range(num_parts):
The Other Jimmy 31:8ea194f6145b 512 start_index = string_part * string_limit
The Other Jimmy 31:8ea194f6145b 513 if string_part == num_parts - 1:
The Other Jimmy 36:96847d42f010 514 sys.stdout.write(large_string[start_index:])
The Other Jimmy 31:8ea194f6145b 515 else:
The Other Jimmy 36:96847d42f010 516 sys.stdout.write(large_string[start_index:
The Other Jimmy 36:96847d42f010 517 start_index + string_limit])
The Other Jimmy 36:96847d42f010 518 sys.stdout.write("\n")
The Other Jimmy 36:96847d42f010 519
The Other Jimmy 36:96847d42f010 520 def intelhex_offset(filename, offset):
The Other Jimmy 36:96847d42f010 521 """Load a hex or bin file at a particular offset"""
The Other Jimmy 36:96847d42f010 522 _, inteltype = splitext(filename)
The Other Jimmy 36:96847d42f010 523 ih = IntelHex()
The Other Jimmy 36:96847d42f010 524 if inteltype == ".bin":
The Other Jimmy 36:96847d42f010 525 ih.loadbin(filename, offset=offset)
The Other Jimmy 36:96847d42f010 526 elif inteltype == ".hex":
The Other Jimmy 36:96847d42f010 527 ih.loadhex(filename)
The Other Jimmy 36:96847d42f010 528 else:
The Other Jimmy 36:96847d42f010 529 raise ToolException("File %s does not have a known binary file type"
The Other Jimmy 36:96847d42f010 530 % filename)
The Other Jimmy 36:96847d42f010 531 return ih