Clone of official tools

Committer:
theotherjimmy
Date:
Tue Sep 25 13:43:09 2018 -0500
Revision:
43:2a7da56ebd24
Parent:
36:96847d42f010
Release 5.10.0

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