Marco Zecchini
/
Example_RTOS
Rtos API example
Embed:
(wiki syntax)
Show/hide line numbers
utils.py
00001 """ 00002 mbed SDK 00003 Copyright (c) 2011-2013 ARM Limited 00004 00005 Licensed under the Apache License, Version 2.0 (the "License"); 00006 you may not use this file except in compliance with the License. 00007 You may obtain a copy of the License at 00008 00009 http://www.apache.org/licenses/LICENSE-2.0 00010 00011 Unless required by applicable law or agreed to in writing, software 00012 distributed under the License is distributed on an "AS IS" BASIS, 00013 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00014 See the License for the specific language governing permissions and 00015 limitations under the License. 00016 """ 00017 import sys 00018 import inspect 00019 import os 00020 import argparse 00021 import math 00022 from os import listdir, remove, makedirs 00023 from shutil import copyfile 00024 from os.path import isdir, join, exists, split, relpath, splitext, abspath 00025 from os.path import commonprefix, normpath, dirname 00026 from subprocess import Popen, PIPE, STDOUT, call 00027 from math import ceil 00028 import json 00029 from collections import OrderedDict 00030 import logging 00031 from intelhex import IntelHex 00032 00033 def remove_if_in(lst, thing): 00034 if thing in lst: 00035 lst.remove(thing) 00036 00037 def compile_worker (job): 00038 """Standard task runner used for compiling 00039 00040 Positional argumets: 00041 job - a dict containing a list of commands and the remaining arguments 00042 to run_cmd 00043 """ 00044 results = [] 00045 for command in job['commands']: 00046 try: 00047 _, _stderr, _rc = run_cmd(command, work_dir=job['work_dir'], 00048 chroot=job['chroot']) 00049 except KeyboardInterrupt: 00050 raise ToolException 00051 00052 results.append({ 00053 'code': _rc, 00054 'output': _stderr, 00055 'command': command 00056 }) 00057 00058 return { 00059 'source': job['source'], 00060 'object': job['object'], 00061 'commands': job['commands'], 00062 'results': results 00063 } 00064 00065 def cmd (command, check=True, verbose=False, shell=False, cwd=None): 00066 """A wrapper to run a command as a blocking job""" 00067 text = command if shell else ' '.join(command) 00068 if verbose: 00069 print text 00070 return_code = call(command, shell=shell, cwd=cwd) 00071 if check and return_code != 0: 00072 raise Exception('ERROR %d: "%s"' % (return_code, text)) 00073 00074 00075 def run_cmd (command, work_dir=None, chroot=None, redirect=False): 00076 """Run a command in the forground 00077 00078 Positional arguments: 00079 command - the command to run 00080 00081 Keyword arguments: 00082 work_dir - the working directory to run the command in 00083 chroot - the chroot to run the command in 00084 redirect - redirect the stderr to a pipe to be used later 00085 """ 00086 if chroot: 00087 # Conventions managed by the web team for the mbed.org build system 00088 chroot_cmd = [ 00089 '/usr/sbin/chroot', '--userspec=33:33', chroot 00090 ] 00091 for element in command: 00092 chroot_cmd += [element.replace(chroot, '')] 00093 00094 logging.debug("Running command %s", ' '.join(chroot_cmd)) 00095 command = chroot_cmd 00096 work_dir = None 00097 00098 try: 00099 process = Popen(command, stdout=PIPE, 00100 stderr=STDOUT if redirect else PIPE, cwd=work_dir) 00101 _stdout, _stderr = process.communicate() 00102 except OSError: 00103 print "[OS ERROR] Command: "+(' '.join(command)) 00104 raise 00105 00106 return _stdout, _stderr, process.returncode 00107 00108 00109 def run_cmd_ext (command): 00110 """ A version of run command that checks if the command exists befor running 00111 00112 Positional arguments: 00113 command - the command line you are trying to invoke 00114 """ 00115 assert is_cmd_valid(command[0]) 00116 process = Popen(command, stdout=PIPE, stderr=PIPE) 00117 _stdout, _stderr = process.communicate() 00118 return _stdout, _stderr, process.returncode 00119 00120 00121 def is_cmd_valid (command): 00122 """ Verify that a command exists and is executable 00123 00124 Positional arguments: 00125 command - the command to check 00126 """ 00127 caller = get_caller_name() 00128 cmd_path = find_cmd_abspath(command) 00129 if not cmd_path: 00130 error("%s: Command '%s' can't be found" % (caller, command)) 00131 if not is_exec(cmd_path): 00132 error("%s: Command '%s' resolves to file '%s' which is not executable" 00133 % (caller, command, cmd_path)) 00134 return True 00135 00136 00137 def is_exec (path): 00138 """A simple check to verify that a path to an executable exists 00139 00140 Positional arguments: 00141 path - the executable 00142 """ 00143 return os.access(path, os.X_OK) or os.access(path+'.exe', os.X_OK) 00144 00145 00146 def find_cmd_abspath (command): 00147 """ Returns the absolute path to a command. 00148 None is returned if no absolute path was found. 00149 00150 Positional arguhments: 00151 command - the command to find the path of 00152 """ 00153 if exists(command) or exists(command + '.exe'): 00154 return os.path.abspath(command) 00155 if not 'PATH' in os.environ: 00156 raise Exception("Can't find command path for current platform ('%s')" 00157 % sys.platform) 00158 path_env = os.environ['PATH'] 00159 for path in path_env.split(os.pathsep): 00160 cmd_path = '%s/%s' % (path, command) 00161 if exists(cmd_path) or exists(cmd_path + '.exe'): 00162 return cmd_path 00163 00164 00165 def mkdir (path): 00166 """ a wrapped makedirs that only tries to create a directory if it does not 00167 exist already 00168 00169 Positional arguments: 00170 path - the path to maybe create 00171 """ 00172 if not exists(path): 00173 makedirs(path) 00174 00175 00176 def copy_file (src, dst): 00177 """ Implement the behaviour of "shutil.copy(src, dst)" without copying the 00178 permissions (this was causing errors with directories mounted with samba) 00179 00180 Positional arguments: 00181 src - the source of the copy operation 00182 dst - the destination of the copy operation 00183 """ 00184 if isdir(dst): 00185 _, base = split(src) 00186 dst = join(dst, base) 00187 copyfile(src, dst) 00188 00189 00190 def delete_dir_files (directory): 00191 """ A function that does rm -rf 00192 00193 Positional arguments: 00194 directory - the directory to remove 00195 """ 00196 if not exists(directory): 00197 return 00198 00199 for element in listdir(directory): 00200 to_remove = join(directory, element) 00201 if not isdir(to_remove): 00202 remove(to_remove) 00203 00204 00205 def get_caller_name (steps=2): 00206 """ 00207 When called inside a function, it returns the name 00208 of the caller of that function. 00209 00210 Keyword arguments: 00211 steps - the number of steps up the stack the calling function is 00212 """ 00213 return inspect.stack()[steps][3] 00214 00215 00216 def error (msg): 00217 """Fatal error, abort hard 00218 00219 Positional arguments: 00220 msg - the message to print before crashing 00221 """ 00222 print("ERROR: %s" % msg) 00223 sys.exit(1) 00224 00225 00226 def rel_path (path, base, dot=False): 00227 """Relative path calculation that optionaly always starts with a dot 00228 00229 Positional arguments: 00230 path - the path to make relative 00231 base - what to make the path relative to 00232 00233 Keyword arguments: 00234 dot - if True, the path will always start with a './' 00235 """ 00236 final_path = relpath(path, base) 00237 if dot and not final_path.startswith('.'): 00238 final_path = './' + final_path 00239 return final_path 00240 00241 00242 class ToolException (Exception): 00243 """A class representing an exception throw by the tools""" 00244 pass 00245 00246 class NotSupportedException(Exception): 00247 """A class a toolchain not supporting a particular target""" 00248 pass 00249 00250 class InvalidReleaseTargetException(Exception): 00251 pass 00252 00253 def split_path (path): 00254 """spilt a file name into it's directory name, base name, and extension 00255 00256 Positional arguments: 00257 path - the file name to split 00258 """ 00259 base, has_ext = split(path) 00260 name, ext = splitext(has_ext) 00261 return base, name, ext 00262 00263 00264 def get_path_depth (path): 00265 """ Given a path, return the number of directory levels present. 00266 This roughly translates to the number of path separators (os.sep) + 1. 00267 Ex. Given "path/to/dir", this would return 3 00268 Special cases: "." and "/" return 0 00269 00270 Positional arguments: 00271 path - the path to calculate the depth of 00272 """ 00273 normalized_path = normpath(path) 00274 path_depth = 0 00275 head, tail = split(normalized_path) 00276 00277 while tail and tail != '.': 00278 path_depth += 1 00279 head, tail = split(head) 00280 00281 return path_depth 00282 00283 00284 def args_error (parser, message): 00285 """Abort with an error that was generated by the arguments to a CLI program 00286 00287 Positional arguments: 00288 parser - the ArgumentParser object that parsed the command line 00289 message - what went wrong 00290 """ 00291 parser.error(message) 00292 sys.exit(2) 00293 00294 00295 def construct_enum (**enums): 00296 """ Create your own pseudo-enums 00297 00298 Keyword arguments: 00299 * - a member of the Enum you are creating and it's value 00300 """ 00301 return type('Enum', (), enums) 00302 00303 00304 def check_required_modules (required_modules, verbose=True): 00305 """ Function checks for Python modules which should be "importable" 00306 before test suite can be used. 00307 @return returns True if all modules are installed already 00308 """ 00309 import imp 00310 not_installed_modules = [] 00311 for module_name in required_modules: 00312 try: 00313 imp.find_module(module_name) 00314 except ImportError: 00315 # We also test against a rare case: module is an egg file 00316 try: 00317 __import__(module_name) 00318 except ImportError as exc: 00319 not_installed_modules.append(module_name) 00320 if verbose: 00321 print "Error: %s" % exc 00322 00323 if verbose: 00324 if not_installed_modules: 00325 print ("Warning: Module(s) %s not installed. Please install " + \ 00326 "required module(s) before using this script.")\ 00327 % (', '.join(not_installed_modules)) 00328 00329 if not_installed_modules: 00330 return False 00331 else: 00332 return True 00333 00334 def dict_to_ascii (dictionary): 00335 """ Utility function: traverse a dictionary and change all the strings in 00336 the dictionary to ASCII from Unicode. Useful when reading ASCII JSON data, 00337 because the JSON decoder always returns Unicode string. Based on 00338 http://stackoverflow.com/a/13105359 00339 00340 Positional arguments: 00341 dictionary - The dict that contains some Unicode that should be ASCII 00342 """ 00343 if isinstance(dictionary, dict): 00344 return OrderedDict([(dict_to_ascii(key), dict_to_ascii(value)) 00345 for key, value in dictionary.iteritems()]) 00346 elif isinstance(dictionary, list): 00347 return [dict_to_ascii(element) for element in dictionary] 00348 elif isinstance(dictionary, unicode): 00349 return dictionary.encode('ascii') 00350 else: 00351 return dictionary 00352 00353 def json_file_to_dict (fname): 00354 """ Read a JSON file and return its Python representation, transforming all 00355 the strings from Unicode to ASCII. The order of keys in the JSON file is 00356 preserved. 00357 00358 Positional arguments: 00359 fname - the name of the file to parse 00360 """ 00361 try: 00362 with open(fname, "r") as file_obj: 00363 return dict_to_ascii(json.load(file_obj, 00364 object_pairs_hook=OrderedDict)) 00365 except (ValueError, IOError): 00366 sys.stderr.write("Error parsing '%s':\n" % fname) 00367 raise 00368 00369 # Wowza, double closure 00370 def argparse_type(casedness, prefer_hyphen=False): 00371 def middle(lst, type_name): 00372 def parse_type(string): 00373 """ validate that an argument passed in (as string) is a member of 00374 the list of possible arguments. Offer a suggestion if the case of 00375 the string, or the hyphens/underscores do not match the expected 00376 style of the argument. 00377 """ 00378 if prefer_hyphen: 00379 newstring = casedness(string).replace("_", "-") 00380 else: 00381 newstring = casedness(string).replace("-", "_") 00382 if string in lst: 00383 return string 00384 elif string not in lst and newstring in lst: 00385 raise argparse.ArgumentTypeError( 00386 "{0} is not a supported {1}. Did you mean {2}?".format( 00387 string, type_name, newstring)) 00388 else: 00389 raise argparse.ArgumentTypeError( 00390 "{0} is not a supported {1}. Supported {1}s are:\n{2}". 00391 format(string, type_name, columnate(lst))) 00392 return parse_type 00393 return middle 00394 00395 # short cuts for the argparse_type versions 00396 argparse_uppercase_type = argparse_type(str.upper, False) 00397 argparse_lowercase_type = argparse_type(str.lower, False) 00398 argparse_uppercase_hyphen_type = argparse_type(str.upper, True) 00399 argparse_lowercase_hyphen_type = argparse_type(str.lower, True) 00400 00401 def argparse_force_type (case): 00402 """ validate that an argument passed in (as string) is a member of the list 00403 of possible arguments after converting it's case. 00404 """ 00405 def middle(lst, type_name): 00406 """ The parser type generator""" 00407 def parse_type(string): 00408 """ The parser type""" 00409 for option in lst: 00410 if case(string) == case(option): 00411 return option 00412 raise argparse.ArgumentTypeError( 00413 "{0} is not a supported {1}. Supported {1}s are:\n{2}". 00414 format(string, type_name, columnate(lst))) 00415 return parse_type 00416 return middle 00417 00418 # these two types convert the case of their arguments _before_ validation 00419 argparse_force_uppercase_type = argparse_force_type(str.upper) 00420 argparse_force_lowercase_type = argparse_force_type(str.lower) 00421 00422 def argparse_many (func): 00423 """ An argument parser combinator that takes in an argument parser and 00424 creates a new parser that accepts a comma separated list of the same thing. 00425 """ 00426 def wrap(string): 00427 """ The actual parser""" 00428 return [func(s) for s in string.split(",")] 00429 return wrap 00430 00431 def argparse_filestring_type (string): 00432 """ An argument parser that verifies that a string passed in corresponds 00433 to a file""" 00434 if exists(string): 00435 return string 00436 else: 00437 raise argparse.ArgumentTypeError( 00438 "{0}"" does not exist in the filesystem.".format(string)) 00439 00440 def argparse_profile_filestring_type (string): 00441 """ An argument parser that verifies that a string passed in is either 00442 absolute path or a file name (expanded to 00443 mbed-os/tools/profiles/<fname>.json) of a existing file""" 00444 fpath = join(dirname(__file__), "profiles/{}.json".format(string)) 00445 if exists(string): 00446 return string 00447 elif exists(fpath): 00448 return fpath 00449 else: 00450 raise argparse.ArgumentTypeError( 00451 "{0} does not exist in the filesystem.".format(string)) 00452 00453 def columnate (strings, separator=", ", chars=80): 00454 """ render a list of strings as a in a bunch of columns 00455 00456 Positional arguments: 00457 strings - the strings to columnate 00458 00459 Keyword arguments; 00460 separator - the separation between the columns 00461 chars - the maximum with of a row 00462 """ 00463 col_width = max(len(s) for s in strings) 00464 total_width = col_width + len(separator) 00465 columns = math.floor(chars / total_width) 00466 output = "" 00467 for i, string in zip(range(len(strings)), strings): 00468 append = string 00469 if i != len(strings) - 1: 00470 append += separator 00471 if i % columns == columns - 1: 00472 append += "\n" 00473 else: 00474 append = append.ljust(total_width) 00475 output += append 00476 return output 00477 00478 def argparse_dir_not_parent (other): 00479 """fail if argument provided is a parent of the specified directory""" 00480 def parse_type(not_parent): 00481 """The parser type""" 00482 abs_other = abspath(other) 00483 abs_not_parent = abspath(not_parent) 00484 if abs_not_parent == commonprefix([abs_not_parent, abs_other]): 00485 raise argparse.ArgumentTypeError( 00486 "{0} may not be a parent directory of {1}".format( 00487 not_parent, other)) 00488 else: 00489 return not_parent 00490 return parse_type 00491 00492 def argparse_deprecate (replacement_message): 00493 """fail if argument is provided with deprecation warning""" 00494 def parse_type(_): 00495 """The parser type""" 00496 raise argparse.ArgumentTypeError("Deprecated." + replacement_message) 00497 return parse_type 00498 00499 def print_large_string (large_string): 00500 """ Breaks a string up into smaller pieces before print them 00501 00502 This is a limitation within Windows, as detailed here: 00503 https://bugs.python.org/issue11395 00504 00505 Positional arguments: 00506 large_string - the large string to print 00507 """ 00508 string_limit = 1000 00509 large_string_len = len(large_string) 00510 num_parts = int(ceil(float(large_string_len) / float(string_limit))) 00511 for string_part in range(num_parts): 00512 start_index = string_part * string_limit 00513 if string_part == num_parts - 1: 00514 sys.stdout.write(large_string[start_index:]) 00515 else: 00516 sys.stdout.write(large_string[start_index: 00517 start_index + string_limit]) 00518 sys.stdout.write("\n") 00519 00520 def intelhex_offset (filename, offset): 00521 """Load a hex or bin file at a particular offset""" 00522 _, inteltype = splitext(filename) 00523 ih = IntelHex() 00524 if inteltype == ".bin": 00525 ih.loadbin(filename, offset=offset) 00526 elif inteltype == ".hex": 00527 ih.loadhex(filename) 00528 else: 00529 raise ToolException("File %s does not have a known binary file type" 00530 % filename) 00531 return ih
Generated on Sun Jul 17 2022 08:25:33 by 1.7.2