BA
/
BaBoRo_test2
Backup 1
mbed-os/tools/check_release.py@0:02dd72d1d465, 2018-04-24 (annotated)
- Committer:
- borlanic
- Date:
- Tue Apr 24 11:45:18 2018 +0000
- Revision:
- 0:02dd72d1d465
BaBoRo_test2 - backup 1
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
borlanic | 0:02dd72d1d465 | 1 | # Script to check a new mbed 2 release by compiling a set of specified test apps |
borlanic | 0:02dd72d1d465 | 2 | # for all currently supported platforms. Each test app must include an mbed library. |
borlanic | 0:02dd72d1d465 | 3 | # This can either be the pre-compiled version 'mbed' or the source version 'mbed-dev'. |
borlanic | 0:02dd72d1d465 | 4 | # |
borlanic | 0:02dd72d1d465 | 5 | # Setup: |
borlanic | 0:02dd72d1d465 | 6 | # 1. Set up your global .hgrc file |
borlanic | 0:02dd72d1d465 | 7 | # |
borlanic | 0:02dd72d1d465 | 8 | # If you don't already have a .hgrc file in your $HOME directory, create one there. |
borlanic | 0:02dd72d1d465 | 9 | # Then add the following section: |
borlanic | 0:02dd72d1d465 | 10 | # |
borlanic | 0:02dd72d1d465 | 11 | # [auth] |
borlanic | 0:02dd72d1d465 | 12 | # x.prefix = * |
borlanic | 0:02dd72d1d465 | 13 | # x.username = <put your mbed org username here> |
borlanic | 0:02dd72d1d465 | 14 | # x.password = <put your mbed org password here> |
borlanic | 0:02dd72d1d465 | 15 | # |
borlanic | 0:02dd72d1d465 | 16 | # This has 2 purposes, the first means you don't get prompted for your password |
borlanic | 0:02dd72d1d465 | 17 | # whenever you run hg commands on the commandline. The second is that this script |
borlanic | 0:02dd72d1d465 | 18 | # reads these details in order to fully automate the Mercurial commands. |
borlanic | 0:02dd72d1d465 | 19 | # |
borlanic | 0:02dd72d1d465 | 20 | # Edit "check_release.json". This has the following structure: |
borlanic | 0:02dd72d1d465 | 21 | #{ |
borlanic | 0:02dd72d1d465 | 22 | # "config" : { |
borlanic | 0:02dd72d1d465 | 23 | # "mbed_repo_path" : "C:/Users/annbri01/Work/Mercurial" |
borlanic | 0:02dd72d1d465 | 24 | # }, |
borlanic | 0:02dd72d1d465 | 25 | # "test_list" : [ |
borlanic | 0:02dd72d1d465 | 26 | # { |
borlanic | 0:02dd72d1d465 | 27 | # "name" : "test_compile_mbed_lib", |
borlanic | 0:02dd72d1d465 | 28 | # "lib" : "mbed" |
borlanic | 0:02dd72d1d465 | 29 | # }, |
borlanic | 0:02dd72d1d465 | 30 | # { |
borlanic | 0:02dd72d1d465 | 31 | # "name" : "test_compile_mbed_dev", |
borlanic | 0:02dd72d1d465 | 32 | # "lib" : "mbed-dev" |
borlanic | 0:02dd72d1d465 | 33 | # } |
borlanic | 0:02dd72d1d465 | 34 | # ], |
borlanic | 0:02dd72d1d465 | 35 | # "target_list" : [] |
borlanic | 0:02dd72d1d465 | 36 | #} |
borlanic | 0:02dd72d1d465 | 37 | # |
borlanic | 0:02dd72d1d465 | 38 | # The mbed_repo_path field should be changed to point to where your local |
borlanic | 0:02dd72d1d465 | 39 | # working directory is for Mercurial repositories. |
borlanic | 0:02dd72d1d465 | 40 | # For each test app you wish to run, add an entry to the test list. The example |
borlanic | 0:02dd72d1d465 | 41 | # above has 2 test apps |
borlanic | 0:02dd72d1d465 | 42 | # "test_compile_mbed_lib" and "test_compile_mbed_dev" |
borlanic | 0:02dd72d1d465 | 43 | # The lib field in each says which type of mbed 2 library the app contains. |
borlanic | 0:02dd72d1d465 | 44 | # These test apps MUST be available as repos in the user's online Mercurial area. |
borlanic | 0:02dd72d1d465 | 45 | # The target_list allows the user to override the set of targets/platforms used |
borlanic | 0:02dd72d1d465 | 46 | # for the compilation. |
borlanic | 0:02dd72d1d465 | 47 | # E.g to just compile for 2 targets, K64F and K22F : |
borlanic | 0:02dd72d1d465 | 48 | # "target_list" : ["K64F", "K22F"] |
borlanic | 0:02dd72d1d465 | 49 | # |
borlanic | 0:02dd72d1d465 | 50 | # Run the script from the mbed-os directory as follows: |
borlanic | 0:02dd72d1d465 | 51 | # > python tools/check_release.py |
borlanic | 0:02dd72d1d465 | 52 | # |
borlanic | 0:02dd72d1d465 | 53 | # It will look for local clones of the test app repos. If they don't exist |
borlanic | 0:02dd72d1d465 | 54 | # it will clone them. It will then read the latest versions of mbed and mbed-dev |
borlanic | 0:02dd72d1d465 | 55 | # (an assumption is made that both of these are already cloned in your Mercurial area). |
borlanic | 0:02dd72d1d465 | 56 | # The lib files within the test apps are then updated to the corresponding version in |
borlanic | 0:02dd72d1d465 | 57 | # the associated lib itself. The test apps are then committed and pushed back to the users |
borlanic | 0:02dd72d1d465 | 58 | # fork. |
borlanic | 0:02dd72d1d465 | 59 | # The test apps will then be compiled for all supported targets and a % result output at |
borlanic | 0:02dd72d1d465 | 60 | # the end. |
borlanic | 0:02dd72d1d465 | 61 | # |
borlanic | 0:02dd72d1d465 | 62 | # Uses the online compiler API at https://mbed.org/handbook/Compile-API |
borlanic | 0:02dd72d1d465 | 63 | # Based on the example from https://mbed.org/teams/mbed/code/mbed-API-helper/ |
borlanic | 0:02dd72d1d465 | 64 | |
borlanic | 0:02dd72d1d465 | 65 | |
borlanic | 0:02dd72d1d465 | 66 | import os, getpass, sys, json, time, requests, logging |
borlanic | 0:02dd72d1d465 | 67 | from os.path import dirname, abspath, basename, join |
borlanic | 0:02dd72d1d465 | 68 | import argparse |
borlanic | 0:02dd72d1d465 | 69 | import subprocess |
borlanic | 0:02dd72d1d465 | 70 | import re |
borlanic | 0:02dd72d1d465 | 71 | import hglib |
borlanic | 0:02dd72d1d465 | 72 | import argparse |
borlanic | 0:02dd72d1d465 | 73 | |
borlanic | 0:02dd72d1d465 | 74 | # Be sure that the tools directory is in the search path |
borlanic | 0:02dd72d1d465 | 75 | ROOT = abspath(join(dirname(__file__), "..")) |
borlanic | 0:02dd72d1d465 | 76 | sys.path.insert(0, ROOT) |
borlanic | 0:02dd72d1d465 | 77 | |
borlanic | 0:02dd72d1d465 | 78 | from tools.build_api import get_mbed_official_release |
borlanic | 0:02dd72d1d465 | 79 | |
borlanic | 0:02dd72d1d465 | 80 | OFFICIAL_MBED_LIBRARY_BUILD = get_mbed_official_release('2') |
borlanic | 0:02dd72d1d465 | 81 | |
borlanic | 0:02dd72d1d465 | 82 | def get_compilation_failure(messages): |
borlanic | 0:02dd72d1d465 | 83 | """ Reads the json formatted 'messages' and checks for compilation errors. |
borlanic | 0:02dd72d1d465 | 84 | If there is a genuine compilation error then there should be a new |
borlanic | 0:02dd72d1d465 | 85 | message containing a severity field = Error and an accompanying message |
borlanic | 0:02dd72d1d465 | 86 | with the compile error text. Any other combination is considered an |
borlanic | 0:02dd72d1d465 | 87 | internal compile engine failure |
borlanic | 0:02dd72d1d465 | 88 | Args: |
borlanic | 0:02dd72d1d465 | 89 | messages - json formatted text returned by the online compiler API. |
borlanic | 0:02dd72d1d465 | 90 | |
borlanic | 0:02dd72d1d465 | 91 | Returns: |
borlanic | 0:02dd72d1d465 | 92 | Either "Error" or "Internal" to indicate an actual compilation error or an |
borlanic | 0:02dd72d1d465 | 93 | internal IDE API fault. |
borlanic | 0:02dd72d1d465 | 94 | |
borlanic | 0:02dd72d1d465 | 95 | """ |
borlanic | 0:02dd72d1d465 | 96 | for m in messages: |
borlanic | 0:02dd72d1d465 | 97 | # Get message text if it exists |
borlanic | 0:02dd72d1d465 | 98 | try: |
borlanic | 0:02dd72d1d465 | 99 | message = m['message'] |
borlanic | 0:02dd72d1d465 | 100 | message = message + "\n" |
borlanic | 0:02dd72d1d465 | 101 | except KeyError: |
borlanic | 0:02dd72d1d465 | 102 | # Skip this message as it has no 'message' field |
borlanic | 0:02dd72d1d465 | 103 | continue |
borlanic | 0:02dd72d1d465 | 104 | |
borlanic | 0:02dd72d1d465 | 105 | # Get type of message text |
borlanic | 0:02dd72d1d465 | 106 | try: |
borlanic | 0:02dd72d1d465 | 107 | msg_type = m['type'] |
borlanic | 0:02dd72d1d465 | 108 | except KeyError: |
borlanic | 0:02dd72d1d465 | 109 | # Skip this message as it has no 'type' field |
borlanic | 0:02dd72d1d465 | 110 | continue |
borlanic | 0:02dd72d1d465 | 111 | |
borlanic | 0:02dd72d1d465 | 112 | if msg_type == 'error' or msg_type == 'tool_error': |
borlanic | 0:02dd72d1d465 | 113 | rel_log.error(message) |
borlanic | 0:02dd72d1d465 | 114 | return "Error" |
borlanic | 0:02dd72d1d465 | 115 | else: |
borlanic | 0:02dd72d1d465 | 116 | rel_log.debug(message) |
borlanic | 0:02dd72d1d465 | 117 | |
borlanic | 0:02dd72d1d465 | 118 | return "Internal" |
borlanic | 0:02dd72d1d465 | 119 | |
borlanic | 0:02dd72d1d465 | 120 | def invoke_api(payload, url, auth, polls, begin="start/"): |
borlanic | 0:02dd72d1d465 | 121 | """ Sends an API command request to the online IDE. Waits for a task completed |
borlanic | 0:02dd72d1d465 | 122 | response before returning the results. |
borlanic | 0:02dd72d1d465 | 123 | |
borlanic | 0:02dd72d1d465 | 124 | Args: |
borlanic | 0:02dd72d1d465 | 125 | payload - Configuration parameters to be passed to the API |
borlanic | 0:02dd72d1d465 | 126 | url - THe URL for the online compiler API |
borlanic | 0:02dd72d1d465 | 127 | auth - Tuple containing authentication credentials |
borlanic | 0:02dd72d1d465 | 128 | polls - Number of times to poll for results |
borlanic | 0:02dd72d1d465 | 129 | begin - Default value = "start/", start command to be appended to URL |
borlanic | 0:02dd72d1d465 | 130 | |
borlanic | 0:02dd72d1d465 | 131 | Returns: |
borlanic | 0:02dd72d1d465 | 132 | result - True/False indicating the success/failure of the compilation |
borlanic | 0:02dd72d1d465 | 133 | fail_type - the failure text if the compilation failed, else None |
borlanic | 0:02dd72d1d465 | 134 | """ |
borlanic | 0:02dd72d1d465 | 135 | |
borlanic | 0:02dd72d1d465 | 136 | # send task to api |
borlanic | 0:02dd72d1d465 | 137 | rel_log.debug(url + begin + "| data: " + str(payload)) |
borlanic | 0:02dd72d1d465 | 138 | r = requests.post(url + begin, data=payload, auth=auth) |
borlanic | 0:02dd72d1d465 | 139 | rel_log.debug(r.request.body) |
borlanic | 0:02dd72d1d465 | 140 | |
borlanic | 0:02dd72d1d465 | 141 | if r.status_code != 200: |
borlanic | 0:02dd72d1d465 | 142 | rel_log.error("HTTP code %d reported.", r.status_code) |
borlanic | 0:02dd72d1d465 | 143 | return False, "Internal" |
borlanic | 0:02dd72d1d465 | 144 | |
borlanic | 0:02dd72d1d465 | 145 | response = r.json() |
borlanic | 0:02dd72d1d465 | 146 | rel_log.debug(response) |
borlanic | 0:02dd72d1d465 | 147 | uuid = response['result']['data']['task_id'] |
borlanic | 0:02dd72d1d465 | 148 | rel_log.debug("Task accepted and given ID: %s", uuid) |
borlanic | 0:02dd72d1d465 | 149 | result = False |
borlanic | 0:02dd72d1d465 | 150 | fail_type = None |
borlanic | 0:02dd72d1d465 | 151 | |
borlanic | 0:02dd72d1d465 | 152 | # It currently seems to take the onlide IDE API ~30s to process the compile |
borlanic | 0:02dd72d1d465 | 153 | # request and provide a response. Set the poll time to half that in case it |
borlanic | 0:02dd72d1d465 | 154 | # does manage to compile quicker. |
borlanic | 0:02dd72d1d465 | 155 | poll_delay = 15 |
borlanic | 0:02dd72d1d465 | 156 | rel_log.debug("Running with a poll for response delay of: %ss", poll_delay) |
borlanic | 0:02dd72d1d465 | 157 | |
borlanic | 0:02dd72d1d465 | 158 | # poll for output |
borlanic | 0:02dd72d1d465 | 159 | for check in range(polls): |
borlanic | 0:02dd72d1d465 | 160 | time.sleep(poll_delay) |
borlanic | 0:02dd72d1d465 | 161 | |
borlanic | 0:02dd72d1d465 | 162 | try: |
borlanic | 0:02dd72d1d465 | 163 | r = requests.get(url + "output/%s" % uuid, auth=auth) |
borlanic | 0:02dd72d1d465 | 164 | |
borlanic | 0:02dd72d1d465 | 165 | except ConnectionError: |
borlanic | 0:02dd72d1d465 | 166 | return "Internal" |
borlanic | 0:02dd72d1d465 | 167 | |
borlanic | 0:02dd72d1d465 | 168 | response = r.json() |
borlanic | 0:02dd72d1d465 | 169 | |
borlanic | 0:02dd72d1d465 | 170 | data = response['result']['data'] |
borlanic | 0:02dd72d1d465 | 171 | if data['task_complete']: |
borlanic | 0:02dd72d1d465 | 172 | # Task completed. Now determine the result. Should be one of : |
borlanic | 0:02dd72d1d465 | 173 | # 1) Successful compilation |
borlanic | 0:02dd72d1d465 | 174 | # 2) Failed compilation with an error message |
borlanic | 0:02dd72d1d465 | 175 | # 3) Internal failure of the online compiler |
borlanic | 0:02dd72d1d465 | 176 | result = bool(data['compilation_success']) |
borlanic | 0:02dd72d1d465 | 177 | if result: |
borlanic | 0:02dd72d1d465 | 178 | rel_log.info("COMPILATION SUCCESSFUL\n") |
borlanic | 0:02dd72d1d465 | 179 | else: |
borlanic | 0:02dd72d1d465 | 180 | # Did this fail due to a genuine compilation error or a failue of |
borlanic | 0:02dd72d1d465 | 181 | # the api itself ? |
borlanic | 0:02dd72d1d465 | 182 | rel_log.info("COMPILATION FAILURE\n") |
borlanic | 0:02dd72d1d465 | 183 | fail_type = get_compilation_failure(data['new_messages']) |
borlanic | 0:02dd72d1d465 | 184 | break |
borlanic | 0:02dd72d1d465 | 185 | else: |
borlanic | 0:02dd72d1d465 | 186 | rel_log.info("COMPILATION FAILURE\n") |
borlanic | 0:02dd72d1d465 | 187 | |
borlanic | 0:02dd72d1d465 | 188 | if not result and fail_type == None: |
borlanic | 0:02dd72d1d465 | 189 | fail_type = "Internal" |
borlanic | 0:02dd72d1d465 | 190 | |
borlanic | 0:02dd72d1d465 | 191 | return result, fail_type |
borlanic | 0:02dd72d1d465 | 192 | |
borlanic | 0:02dd72d1d465 | 193 | |
borlanic | 0:02dd72d1d465 | 194 | def build_repo(target, program, user, pw, polls=25, |
borlanic | 0:02dd72d1d465 | 195 | url="https://developer.mbed.org/api/v2/tasks/compiler/"): |
borlanic | 0:02dd72d1d465 | 196 | """ Wrapper for sending an API command request to the online IDE. Sends a |
borlanic | 0:02dd72d1d465 | 197 | build request. |
borlanic | 0:02dd72d1d465 | 198 | |
borlanic | 0:02dd72d1d465 | 199 | Args: |
borlanic | 0:02dd72d1d465 | 200 | target - Target to be built |
borlanic | 0:02dd72d1d465 | 201 | program - Test program to build |
borlanic | 0:02dd72d1d465 | 202 | user - mbed username |
borlanic | 0:02dd72d1d465 | 203 | pw - mbed password |
borlanic | 0:02dd72d1d465 | 204 | polls - Number of times to poll for results |
borlanic | 0:02dd72d1d465 | 205 | url - THe URL for the online compiler API |
borlanic | 0:02dd72d1d465 | 206 | |
borlanic | 0:02dd72d1d465 | 207 | Returns: |
borlanic | 0:02dd72d1d465 | 208 | result - True/False indicating the success/failure of the compilation |
borlanic | 0:02dd72d1d465 | 209 | fail_type - the failure text if the compilation failed, else None |
borlanic | 0:02dd72d1d465 | 210 | """ |
borlanic | 0:02dd72d1d465 | 211 | payload = {'clean':True, 'target':target, 'program':program} |
borlanic | 0:02dd72d1d465 | 212 | auth = (user, pw) |
borlanic | 0:02dd72d1d465 | 213 | return invoke_api(payload, url, auth, polls) |
borlanic | 0:02dd72d1d465 | 214 | |
borlanic | 0:02dd72d1d465 | 215 | def run_cmd(command, exit_on_failure=False): |
borlanic | 0:02dd72d1d465 | 216 | """ Passes a command to the system and returns a True/False result once the |
borlanic | 0:02dd72d1d465 | 217 | command has been executed, indicating success/failure. Commands are passed |
borlanic | 0:02dd72d1d465 | 218 | as a list of tokens. |
borlanic | 0:02dd72d1d465 | 219 | E.g. The command 'git remote -v' would be passed in as ['git', 'remote', '-v'] |
borlanic | 0:02dd72d1d465 | 220 | |
borlanic | 0:02dd72d1d465 | 221 | Args: |
borlanic | 0:02dd72d1d465 | 222 | command - system command as a list of tokens |
borlanic | 0:02dd72d1d465 | 223 | exit_on_failure - If True exit the program on failure (default = False) |
borlanic | 0:02dd72d1d465 | 224 | |
borlanic | 0:02dd72d1d465 | 225 | Returns: |
borlanic | 0:02dd72d1d465 | 226 | result - True/False indicating the success/failure of the command |
borlanic | 0:02dd72d1d465 | 227 | """ |
borlanic | 0:02dd72d1d465 | 228 | rel_log.debug('[Exec] %s', ' '.join(command)) |
borlanic | 0:02dd72d1d465 | 229 | return_code = subprocess.call(command, shell=True) |
borlanic | 0:02dd72d1d465 | 230 | |
borlanic | 0:02dd72d1d465 | 231 | if return_code: |
borlanic | 0:02dd72d1d465 | 232 | rel_log.warning("The command '%s' failed with return code: %s", |
borlanic | 0:02dd72d1d465 | 233 | (' '.join(command), return_code)) |
borlanic | 0:02dd72d1d465 | 234 | if exit_on_failure: |
borlanic | 0:02dd72d1d465 | 235 | sys.exit(1) |
borlanic | 0:02dd72d1d465 | 236 | |
borlanic | 0:02dd72d1d465 | 237 | return return_code |
borlanic | 0:02dd72d1d465 | 238 | |
borlanic | 0:02dd72d1d465 | 239 | def run_cmd_with_output(command, exit_on_failure=False): |
borlanic | 0:02dd72d1d465 | 240 | """ Passes a command to the system and returns a True/False result once the |
borlanic | 0:02dd72d1d465 | 241 | command has been executed, indicating success/failure. If the command was |
borlanic | 0:02dd72d1d465 | 242 | successful then the output from the command is returned to the caller. |
borlanic | 0:02dd72d1d465 | 243 | Commands are passed as a list of tokens. |
borlanic | 0:02dd72d1d465 | 244 | E.g. The command 'git remote -v' would be passed in as ['git', 'remote', '-v'] |
borlanic | 0:02dd72d1d465 | 245 | |
borlanic | 0:02dd72d1d465 | 246 | Args: |
borlanic | 0:02dd72d1d465 | 247 | command - system command as a list of tokens |
borlanic | 0:02dd72d1d465 | 248 | exit_on_failure - If True exit the program on failure (default = False) |
borlanic | 0:02dd72d1d465 | 249 | |
borlanic | 0:02dd72d1d465 | 250 | Returns: |
borlanic | 0:02dd72d1d465 | 251 | result - True/False indicating the success/failure of the command |
borlanic | 0:02dd72d1d465 | 252 | output - The output of the command if it was successful, else empty string |
borlanic | 0:02dd72d1d465 | 253 | """ |
borlanic | 0:02dd72d1d465 | 254 | rel_log.debug('[Exec] %s', ' '.join(command)) |
borlanic | 0:02dd72d1d465 | 255 | returncode = 0 |
borlanic | 0:02dd72d1d465 | 256 | output = "" |
borlanic | 0:02dd72d1d465 | 257 | try: |
borlanic | 0:02dd72d1d465 | 258 | output = subprocess.check_output(command, shell=True) |
borlanic | 0:02dd72d1d465 | 259 | except subprocess.CalledProcessError as e: |
borlanic | 0:02dd72d1d465 | 260 | rel_log.warning("The command '%s' failed with return code: %s", |
borlanic | 0:02dd72d1d465 | 261 | (' '.join(command), e.returncode)) |
borlanic | 0:02dd72d1d465 | 262 | returncode = e.returncode |
borlanic | 0:02dd72d1d465 | 263 | if exit_on_failure: |
borlanic | 0:02dd72d1d465 | 264 | sys.exit(1) |
borlanic | 0:02dd72d1d465 | 265 | return returncode, output |
borlanic | 0:02dd72d1d465 | 266 | |
borlanic | 0:02dd72d1d465 | 267 | def upgrade_test_repo(test, user, library, ref, repo_path): |
borlanic | 0:02dd72d1d465 | 268 | """ Upgrades a local version of a test repo to the latest version of its |
borlanic | 0:02dd72d1d465 | 269 | embedded library. |
borlanic | 0:02dd72d1d465 | 270 | If the test repo is not present in the user area specified in the json |
borlanic | 0:02dd72d1d465 | 271 | config file, then it will first be cloned. |
borlanic | 0:02dd72d1d465 | 272 | Args: |
borlanic | 0:02dd72d1d465 | 273 | test - Mercurial test repo name |
borlanic | 0:02dd72d1d465 | 274 | user - Mercurial user name |
borlanic | 0:02dd72d1d465 | 275 | library - library name |
borlanic | 0:02dd72d1d465 | 276 | ref - SHA corresponding to the latest version of the library |
borlanic | 0:02dd72d1d465 | 277 | repo_path - path to the user's repo area |
borlanic | 0:02dd72d1d465 | 278 | |
borlanic | 0:02dd72d1d465 | 279 | Returns: |
borlanic | 0:02dd72d1d465 | 280 | updated - True if library was updated, False otherwise |
borlanic | 0:02dd72d1d465 | 281 | """ |
borlanic | 0:02dd72d1d465 | 282 | rel_log.info("Updating test repo: '%s' to SHA: %s", test, ref) |
borlanic | 0:02dd72d1d465 | 283 | cwd = os.getcwd() |
borlanic | 0:02dd72d1d465 | 284 | |
borlanic | 0:02dd72d1d465 | 285 | repo = "https://" + user + '@developer.mbed.org/users/' + user + '/code/' + test |
borlanic | 0:02dd72d1d465 | 286 | |
borlanic | 0:02dd72d1d465 | 287 | # Clone the repo if it doesn't already exist |
borlanic | 0:02dd72d1d465 | 288 | path = abspath(repo_path + '/' + test) |
borlanic | 0:02dd72d1d465 | 289 | if not os.path.exists(path): |
borlanic | 0:02dd72d1d465 | 290 | rel_log.info("Test repo doesn't exist, cloning...") |
borlanic | 0:02dd72d1d465 | 291 | os.chdir(abspath(repo_path)) |
borlanic | 0:02dd72d1d465 | 292 | clone_cmd = ['hg', 'clone', repo] |
borlanic | 0:02dd72d1d465 | 293 | run_cmd(clone_cmd, exit_on_failure=True) |
borlanic | 0:02dd72d1d465 | 294 | |
borlanic | 0:02dd72d1d465 | 295 | os.chdir(path) |
borlanic | 0:02dd72d1d465 | 296 | |
borlanic | 0:02dd72d1d465 | 297 | client = hglib.open(path) |
borlanic | 0:02dd72d1d465 | 298 | |
borlanic | 0:02dd72d1d465 | 299 | lib_file = library + '.lib' |
borlanic | 0:02dd72d1d465 | 300 | if os.path.isfile(lib_file): |
borlanic | 0:02dd72d1d465 | 301 | # Rename command will fail on some OS's if the target file already exist, |
borlanic | 0:02dd72d1d465 | 302 | # so ensure if it does, it is deleted first. |
borlanic | 0:02dd72d1d465 | 303 | bak_file = library + '_bak' |
borlanic | 0:02dd72d1d465 | 304 | if os.path.isfile(bak_file): |
borlanic | 0:02dd72d1d465 | 305 | os.remove(bak_file) |
borlanic | 0:02dd72d1d465 | 306 | |
borlanic | 0:02dd72d1d465 | 307 | os.rename(lib_file, bak_file) |
borlanic | 0:02dd72d1d465 | 308 | else: |
borlanic | 0:02dd72d1d465 | 309 | rel_log.error("Failure to backup lib file prior to updating.") |
borlanic | 0:02dd72d1d465 | 310 | return False |
borlanic | 0:02dd72d1d465 | 311 | |
borlanic | 0:02dd72d1d465 | 312 | # mbed 2 style lib file contains one line with the following format |
borlanic | 0:02dd72d1d465 | 313 | # e.g. https://developer.mbed.org/users/<user>/code/mbed-dev/#156823d33999 |
borlanic | 0:02dd72d1d465 | 314 | exp = 'https://developer.mbed.org/users/' + user + '/code/' + library + '/#[A-Za-z0-9]+' |
borlanic | 0:02dd72d1d465 | 315 | lib_re = re.compile(exp) |
borlanic | 0:02dd72d1d465 | 316 | updated = False |
borlanic | 0:02dd72d1d465 | 317 | |
borlanic | 0:02dd72d1d465 | 318 | # Scan through mbed-os.lib line by line, looking for lib version and update |
borlanic | 0:02dd72d1d465 | 319 | # it if found |
borlanic | 0:02dd72d1d465 | 320 | with open(bak_file, 'r') as ip, open(lib_file, 'w') as op: |
borlanic | 0:02dd72d1d465 | 321 | for line in ip: |
borlanic | 0:02dd72d1d465 | 322 | |
borlanic | 0:02dd72d1d465 | 323 | opline = line |
borlanic | 0:02dd72d1d465 | 324 | |
borlanic | 0:02dd72d1d465 | 325 | regexp = lib_re.match(line) |
borlanic | 0:02dd72d1d465 | 326 | if regexp: |
borlanic | 0:02dd72d1d465 | 327 | opline = 'https://developer.mbed.org/users/' + user + '/code/' + library + '/#' + ref |
borlanic | 0:02dd72d1d465 | 328 | updated = True |
borlanic | 0:02dd72d1d465 | 329 | |
borlanic | 0:02dd72d1d465 | 330 | op.write(opline) |
borlanic | 0:02dd72d1d465 | 331 | |
borlanic | 0:02dd72d1d465 | 332 | if updated: |
borlanic | 0:02dd72d1d465 | 333 | |
borlanic | 0:02dd72d1d465 | 334 | # Setup the default commit message |
borlanic | 0:02dd72d1d465 | 335 | commit_message = '"Updating ' + library + ' to ' + ref + '"' |
borlanic | 0:02dd72d1d465 | 336 | |
borlanic | 0:02dd72d1d465 | 337 | # Setup and run the commit command. Need to use the rawcommand in the hglib |
borlanic | 0:02dd72d1d465 | 338 | # for this in order to pass the string value to the -m option. run_cmd using |
borlanic | 0:02dd72d1d465 | 339 | # subprocess does not like this syntax. |
borlanic | 0:02dd72d1d465 | 340 | try: |
borlanic | 0:02dd72d1d465 | 341 | client.rawcommand(['commit','-m '+commit_message, lib_file]) |
borlanic | 0:02dd72d1d465 | 342 | |
borlanic | 0:02dd72d1d465 | 343 | cmd = ['hg', 'push', '-f', repo] |
borlanic | 0:02dd72d1d465 | 344 | run_cmd(cmd, exit_on_failure=True) |
borlanic | 0:02dd72d1d465 | 345 | |
borlanic | 0:02dd72d1d465 | 346 | except: |
borlanic | 0:02dd72d1d465 | 347 | rel_log.info("Lib file already up to date and thus nothing to commit") |
borlanic | 0:02dd72d1d465 | 348 | |
borlanic | 0:02dd72d1d465 | 349 | os.chdir(cwd) |
borlanic | 0:02dd72d1d465 | 350 | return updated |
borlanic | 0:02dd72d1d465 | 351 | |
borlanic | 0:02dd72d1d465 | 352 | def get_sha(repo_path, library): |
borlanic | 0:02dd72d1d465 | 353 | """ Gets the latest SHA for the library specified. The library is assumed to be |
borlanic | 0:02dd72d1d465 | 354 | located at the repo_path. If a SHA cannot be obtained this script will exit. |
borlanic | 0:02dd72d1d465 | 355 | |
borlanic | 0:02dd72d1d465 | 356 | Args: |
borlanic | 0:02dd72d1d465 | 357 | library - library name |
borlanic | 0:02dd72d1d465 | 358 | repo_path - path to the user's repo area |
borlanic | 0:02dd72d1d465 | 359 | |
borlanic | 0:02dd72d1d465 | 360 | Returns: |
borlanic | 0:02dd72d1d465 | 361 | sha - last commit SHA |
borlanic | 0:02dd72d1d465 | 362 | """ |
borlanic | 0:02dd72d1d465 | 363 | cwd = os.getcwd() |
borlanic | 0:02dd72d1d465 | 364 | sha = None |
borlanic | 0:02dd72d1d465 | 365 | os.chdir(abspath(repo_path + '/' + library)) |
borlanic | 0:02dd72d1d465 | 366 | |
borlanic | 0:02dd72d1d465 | 367 | cmd = ['hg', 'log', '-l', '1'] |
borlanic | 0:02dd72d1d465 | 368 | ret, output = run_cmd_with_output(cmd, exit_on_failure=True) |
borlanic | 0:02dd72d1d465 | 369 | |
borlanic | 0:02dd72d1d465 | 370 | # Output should contain a 4 line string of the form: |
borlanic | 0:02dd72d1d465 | 371 | # changeset: 135:176b8275d35d |
borlanic | 0:02dd72d1d465 | 372 | # tag: tip |
borlanic | 0:02dd72d1d465 | 373 | # user: <> |
borlanic | 0:02dd72d1d465 | 374 | # date: Thu Feb 02 16:02:30 2017 +0000 |
borlanic | 0:02dd72d1d465 | 375 | # summary: Release 135 of the mbed library |
borlanic | 0:02dd72d1d465 | 376 | # All we want is the changeset string after version number |
borlanic | 0:02dd72d1d465 | 377 | |
borlanic | 0:02dd72d1d465 | 378 | lines = output.split('\n') |
borlanic | 0:02dd72d1d465 | 379 | fields = lines[0].split(':') |
borlanic | 0:02dd72d1d465 | 380 | sha = fields[2] |
borlanic | 0:02dd72d1d465 | 381 | |
borlanic | 0:02dd72d1d465 | 382 | os.chdir(cwd) |
borlanic | 0:02dd72d1d465 | 383 | return sha |
borlanic | 0:02dd72d1d465 | 384 | |
borlanic | 0:02dd72d1d465 | 385 | def get_latest_library_versions(repo_path): |
borlanic | 0:02dd72d1d465 | 386 | """ Returns the latest library versions (SHAs) for 'mbed' and 'mbed-dev'. |
borlanic | 0:02dd72d1d465 | 387 | If the SHAs cannot be obtained this script will exit. |
borlanic | 0:02dd72d1d465 | 388 | |
borlanic | 0:02dd72d1d465 | 389 | Args: |
borlanic | 0:02dd72d1d465 | 390 | repo_path - path to the user's repo area |
borlanic | 0:02dd72d1d465 | 391 | |
borlanic | 0:02dd72d1d465 | 392 | Returns: |
borlanic | 0:02dd72d1d465 | 393 | mbed - last commit SHA for mbed library |
borlanic | 0:02dd72d1d465 | 394 | mbed_dev - last commit SHA for mbed_dev library |
borlanic | 0:02dd72d1d465 | 395 | |
borlanic | 0:02dd72d1d465 | 396 | """ |
borlanic | 0:02dd72d1d465 | 397 | |
borlanic | 0:02dd72d1d465 | 398 | mbed = get_sha(repo_path, 'mbed') |
borlanic | 0:02dd72d1d465 | 399 | mbed_dev = get_sha(repo_path, 'mbed-dev') |
borlanic | 0:02dd72d1d465 | 400 | |
borlanic | 0:02dd72d1d465 | 401 | return mbed, mbed_dev |
borlanic | 0:02dd72d1d465 | 402 | |
borlanic | 0:02dd72d1d465 | 403 | def log_results(lst, title): |
borlanic | 0:02dd72d1d465 | 404 | if len(lst) == 0: |
borlanic | 0:02dd72d1d465 | 405 | rel_log.info("%s - None", title) |
borlanic | 0:02dd72d1d465 | 406 | else: |
borlanic | 0:02dd72d1d465 | 407 | for entry in lst: |
borlanic | 0:02dd72d1d465 | 408 | rel_log.info("%s - Test: %s, Target: %s", title, entry[0], entry[1]) |
borlanic | 0:02dd72d1d465 | 409 | |
borlanic | 0:02dd72d1d465 | 410 | |
borlanic | 0:02dd72d1d465 | 411 | if __name__ == '__main__': |
borlanic | 0:02dd72d1d465 | 412 | |
borlanic | 0:02dd72d1d465 | 413 | parser = argparse.ArgumentParser(description=__doc__, |
borlanic | 0:02dd72d1d465 | 414 | formatter_class=argparse.RawDescriptionHelpFormatter) |
borlanic | 0:02dd72d1d465 | 415 | parser.add_argument('-l', '--log-level', |
borlanic | 0:02dd72d1d465 | 416 | help="Level for providing logging output", |
borlanic | 0:02dd72d1d465 | 417 | default='INFO') |
borlanic | 0:02dd72d1d465 | 418 | args = parser.parse_args() |
borlanic | 0:02dd72d1d465 | 419 | |
borlanic | 0:02dd72d1d465 | 420 | default = getattr(logging, 'INFO') |
borlanic | 0:02dd72d1d465 | 421 | level = getattr(logging, args.log_level.upper(), default) |
borlanic | 0:02dd72d1d465 | 422 | |
borlanic | 0:02dd72d1d465 | 423 | # Set logging level |
borlanic | 0:02dd72d1d465 | 424 | logging.basicConfig(level=level) |
borlanic | 0:02dd72d1d465 | 425 | rel_log = logging.getLogger("check-release") |
borlanic | 0:02dd72d1d465 | 426 | |
borlanic | 0:02dd72d1d465 | 427 | # Read configuration data |
borlanic | 0:02dd72d1d465 | 428 | with open(os.path.join(os.path.dirname(__file__), "check_release.json")) as config: |
borlanic | 0:02dd72d1d465 | 429 | json_data = json.load(config) |
borlanic | 0:02dd72d1d465 | 430 | |
borlanic | 0:02dd72d1d465 | 431 | supported_targets = [] |
borlanic | 0:02dd72d1d465 | 432 | |
borlanic | 0:02dd72d1d465 | 433 | if len(json_data["target_list"]) > 0: |
borlanic | 0:02dd72d1d465 | 434 | # Compile user supplied subset of targets |
borlanic | 0:02dd72d1d465 | 435 | supported_targets = json_data["target_list"] |
borlanic | 0:02dd72d1d465 | 436 | else: |
borlanic | 0:02dd72d1d465 | 437 | # Get a list of the officially supported mbed-os 2 targets |
borlanic | 0:02dd72d1d465 | 438 | for tgt in OFFICIAL_MBED_LIBRARY_BUILD: |
borlanic | 0:02dd72d1d465 | 439 | supported_targets.append(tgt[0]) |
borlanic | 0:02dd72d1d465 | 440 | |
borlanic | 0:02dd72d1d465 | 441 | ignore_list = [] |
borlanic | 0:02dd72d1d465 | 442 | |
borlanic | 0:02dd72d1d465 | 443 | if len(json_data["ignore_list"]) > 0: |
borlanic | 0:02dd72d1d465 | 444 | # List of tuples of (test, target) to be ignored in this test |
borlanic | 0:02dd72d1d465 | 445 | ignore_list = json_data["ignore_list"] |
borlanic | 0:02dd72d1d465 | 446 | |
borlanic | 0:02dd72d1d465 | 447 | config = json_data["config"] |
borlanic | 0:02dd72d1d465 | 448 | test_list = json_data["test_list"] |
borlanic | 0:02dd72d1d465 | 449 | repo_path = config["mbed_repo_path"] |
borlanic | 0:02dd72d1d465 | 450 | tests = [] |
borlanic | 0:02dd72d1d465 | 451 | |
borlanic | 0:02dd72d1d465 | 452 | # get username |
borlanic | 0:02dd72d1d465 | 453 | cmd = ['hg', 'config', 'auth.x.username'] |
borlanic | 0:02dd72d1d465 | 454 | ret, output = run_cmd_with_output(cmd, exit_on_failure=True) |
borlanic | 0:02dd72d1d465 | 455 | output = output.split('\n') |
borlanic | 0:02dd72d1d465 | 456 | user = output[0] |
borlanic | 0:02dd72d1d465 | 457 | |
borlanic | 0:02dd72d1d465 | 458 | # get password |
borlanic | 0:02dd72d1d465 | 459 | cmd = ['hg', 'config', 'auth.x.password'] |
borlanic | 0:02dd72d1d465 | 460 | ret, output = run_cmd_with_output(cmd, exit_on_failure=True) |
borlanic | 0:02dd72d1d465 | 461 | output = output.split('\n') |
borlanic | 0:02dd72d1d465 | 462 | password = output[0] |
borlanic | 0:02dd72d1d465 | 463 | |
borlanic | 0:02dd72d1d465 | 464 | mbed, mbed_dev = get_latest_library_versions(repo_path) |
borlanic | 0:02dd72d1d465 | 465 | |
borlanic | 0:02dd72d1d465 | 466 | if not mbed or not mbed_dev: |
borlanic | 0:02dd72d1d465 | 467 | rel_log.error("Could not obtain latest versions of library files!!") |
borlanic | 0:02dd72d1d465 | 468 | exit(1) |
borlanic | 0:02dd72d1d465 | 469 | |
borlanic | 0:02dd72d1d465 | 470 | rel_log.info("Latest mbed lib version = %s", mbed) |
borlanic | 0:02dd72d1d465 | 471 | rel_log.info("Latest mbed-dev lib version = %s", mbed_dev) |
borlanic | 0:02dd72d1d465 | 472 | |
borlanic | 0:02dd72d1d465 | 473 | # First update test repos to latest versions of their embedded libraries |
borlanic | 0:02dd72d1d465 | 474 | for test in test_list: |
borlanic | 0:02dd72d1d465 | 475 | tests.append(test['name']) |
borlanic | 0:02dd72d1d465 | 476 | upgrade_test_repo(test['name'], user, test['lib'], |
borlanic | 0:02dd72d1d465 | 477 | mbed if test['lib'] == "mbed" else mbed_dev, |
borlanic | 0:02dd72d1d465 | 478 | repo_path) |
borlanic | 0:02dd72d1d465 | 479 | |
borlanic | 0:02dd72d1d465 | 480 | total = len(supported_targets) * len(tests) |
borlanic | 0:02dd72d1d465 | 481 | current = 0 |
borlanic | 0:02dd72d1d465 | 482 | retries = 10 |
borlanic | 0:02dd72d1d465 | 483 | passes = 0 |
borlanic | 0:02dd72d1d465 | 484 | failures = [] |
borlanic | 0:02dd72d1d465 | 485 | skipped = [] |
borlanic | 0:02dd72d1d465 | 486 | |
borlanic | 0:02dd72d1d465 | 487 | # Compile each test for each supported target |
borlanic | 0:02dd72d1d465 | 488 | for test in tests: |
borlanic | 0:02dd72d1d465 | 489 | for target in supported_targets: |
borlanic | 0:02dd72d1d465 | 490 | |
borlanic | 0:02dd72d1d465 | 491 | combo = [test, target] |
borlanic | 0:02dd72d1d465 | 492 | |
borlanic | 0:02dd72d1d465 | 493 | if combo in ignore_list: |
borlanic | 0:02dd72d1d465 | 494 | rel_log.info("SKIPPING TEST: %s, TARGET: %s", test, target) |
borlanic | 0:02dd72d1d465 | 495 | total -= 1 |
borlanic | 0:02dd72d1d465 | 496 | skipped.append(combo) |
borlanic | 0:02dd72d1d465 | 497 | continue |
borlanic | 0:02dd72d1d465 | 498 | |
borlanic | 0:02dd72d1d465 | 499 | current += 1 |
borlanic | 0:02dd72d1d465 | 500 | for retry in range(0, retries): |
borlanic | 0:02dd72d1d465 | 501 | rel_log.info("COMPILING (%d/%d): TEST %s, TARGET: %s , attempt %u\n", current, total, test, target, retry) |
borlanic | 0:02dd72d1d465 | 502 | result, mesg = build_repo(target, test, user, password) |
borlanic | 0:02dd72d1d465 | 503 | if not result: |
borlanic | 0:02dd72d1d465 | 504 | if mesg == 'Internal': |
borlanic | 0:02dd72d1d465 | 505 | # Internal compiler error thus retry |
borlanic | 0:02dd72d1d465 | 506 | continue |
borlanic | 0:02dd72d1d465 | 507 | else: |
borlanic | 0:02dd72d1d465 | 508 | # Actual error thus move on to next compilation |
borlanic | 0:02dd72d1d465 | 509 | failures.append(combo) |
borlanic | 0:02dd72d1d465 | 510 | break |
borlanic | 0:02dd72d1d465 | 511 | |
borlanic | 0:02dd72d1d465 | 512 | passes += (int)(result) |
borlanic | 0:02dd72d1d465 | 513 | break |
borlanic | 0:02dd72d1d465 | 514 | else: |
borlanic | 0:02dd72d1d465 | 515 | rel_log.error("Compilation failed due to internal errors.") |
borlanic | 0:02dd72d1d465 | 516 | rel_log.error("Skipping test/target combination.") |
borlanic | 0:02dd72d1d465 | 517 | total -= 1 |
borlanic | 0:02dd72d1d465 | 518 | skipped.append(combo) |
borlanic | 0:02dd72d1d465 | 519 | |
borlanic | 0:02dd72d1d465 | 520 | rel_log.info(" SUMMARY OF COMPILATION RESULTS") |
borlanic | 0:02dd72d1d465 | 521 | rel_log.info(" ------------------------------") |
borlanic | 0:02dd72d1d465 | 522 | rel_log.info(" NUMBER OF TEST APPS: %d, NUMBER OF TARGETS: %d", |
borlanic | 0:02dd72d1d465 | 523 | len(tests), len(supported_targets)) |
borlanic | 0:02dd72d1d465 | 524 | log_results(failures, " FAILED") |
borlanic | 0:02dd72d1d465 | 525 | log_results(skipped, " SKIPPED") |
borlanic | 0:02dd72d1d465 | 526 | |
borlanic | 0:02dd72d1d465 | 527 | # Output a % pass rate, indicate a failure if not 100% successful |
borlanic | 0:02dd72d1d465 | 528 | pass_rate = (float(passes) / float(total)) * 100.0 |
borlanic | 0:02dd72d1d465 | 529 | rel_log.info(" PASS RATE %.1f %%\n", pass_rate) |
borlanic | 0:02dd72d1d465 | 530 | sys.exit(not (pass_rate == 100)) |