Clone of official tools

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