Committer:
borlanic
Date:
Fri Mar 30 14:07:05 2018 +0000
Revision:
4:75df35ef4fb6
Parent:
0:380207fcb5c1
commentar

Who changed what in which revision?

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