BBR 1 Ebene

Committer:
borlanic
Date:
Mon May 14 11:29:06 2018 +0000
Revision:
0:fbdae7e6d805
BBR

Who changed what in which revision?

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