mbed-os

Dependents:   cobaLCDJoyMotor_Thread odometry_omni_3roda_v3 odometry_omni_3roda_v1 odometry_omni_3roda_v2 ... more

Committer:
be_bryan
Date:
Mon Dec 11 17:54:04 2017 +0000
Revision:
0:b74591d5ab33
motor ++

Who changed what in which revision?

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