mbed-os for GR-LYCHEE
Dependents: mbed-os-example-blinky-gr-lychee GR-Boads_Camera_sample GR-Boards_Audio_Recoder GR-Boads_Camera_DisplayApp ... more
Diff: tools/test/examples/update.py
- Revision:
- 0:f782d9c66c49
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/test/examples/update.py Fri Feb 02 05:42:23 2018 +0000 @@ -0,0 +1,367 @@ +#!/usr/bin/env python + +import os +from os.path import dirname, abspath, basename +import sys +import argparse +import json +import subprocess +import shutil +import stat +import re +from github import Github, GithubException + +ROOT = abspath(dirname(dirname(dirname(dirname(__file__))))) +sys.path.insert(0, ROOT) + +import examples_lib as lib +from examples_lib import SUPPORTED_TOOLCHAINS + +def run_cmd(command, print_warning_on_fail=True): + """ Takes the command specified and runs it in a sub-process, obtaining the return code. + + Args: + command - command to run, provided as a list of individual fields which are combined into a + single command before passing to the sub-process call. + return_code - result of the command. + + """ + print('[Exec] %s' % ' '.join(command)) + return_code = subprocess.call(command) + + if return_code: + print("The command '%s' failed with return code: %s" % (' '.join(command), return_code)) + print("Ignoring and moving on to the next example") + + return return_code + +def run_cmd_with_output(command, print_warning_on_fail=True): + """ Takes the command specified and runs it in a sub-process, obtaining the return code + and the returned output. + + Args: + command - command to run, provided as a list of individual fields which are combined into a + single command before passing to the sub-process call. + return_code - result of the command. + output - the output of the command + + """ + print('[Exec] %s' % ' '.join(command)) + returncode = 0 + output = None + try: + output = subprocess.check_output(command) + except subprocess.CalledProcessError as e: + print("The command '%s' failed with return code: %s" % (' '.join(command), e.returncode)) + returncode = e.returncode + return returncode, output + +def rmtree_readonly(directory): + """ Deletes a readonly directory tree. + + Args: + directory - tree to delete + """ + def remove_readonly(func, path, _): + os.chmod(path, stat.S_IWRITE) + func(path) + + shutil.rmtree(directory, onerror=remove_readonly) + +def find_all_examples(path): + """ Searches the path specified for sub-example folders, ie those containing an + mbed-os.lib file. If found adds the path to the sub-example to a list which is + then returned. + + Args: + path - path to search. + examples - (returned) list of paths to example directories. + + """ + examples = [] + for root, dirs, files in os.walk(path): + if 'mbed-os.lib' in files: + examples += [root] + + return examples + +def upgrade_single_example(example, tag, directory, ref): + """ Updates the mbed-os.lib file in the example specified to correspond to the + version specified by the GitHub tag supplied. Also deals with + multiple sub-examples in the GitHub repo, updating them in the same way. + + Args: + example - json example object containing the GitHub repo to update. + tag - GitHub tag corresponding to a version of mbed-os to upgrade to. + directory - directory path for the example. + ref - SHA corresponding to the supplied tag + returns - True if the upgrade was successful, False otherwise. + + """ + cwd = os.getcwd() + os.chdir(directory) + + return_code = False + + if os.path.isfile("mbed-os.lib"): + # Rename command will fail on some OS's if the target file already exist, + # so ensure if it does, it is deleted first. + if os.path.isfile("mbed-os.lib_bak"): + os.remove("mbed-os.lib_bak") + + os.rename("mbed-os.lib", "mbed-os.lib_bak") + else: + print("!! Error trying to backup mbed-os.lib prior to updating.") + return False + + # mbed-os.lib file contains one line with the following format + # e.g. https://github.com/ARMmbed/mbed-os/#0789928ee7f2db08a419fa4a032fffd9bd477aa7 + lib_re = re.compile('https://github.com/ARMmbed/mbed-os/#[A-Za-z0-9]+') + updated = False + + # Scan through mbed-os.lib line by line + with open('mbed-os.lib_bak', 'r') as ip, open('mbed-os.lib', 'w') as op: + for line in ip: + + opline = line + + regexp = lib_re.match(line) + if regexp: + opline = 'https://github.com/ARMmbed/mbed-os/#' + ref + updated = True + + op.write(opline) + + if updated: + # Setup and run the git add command + cmd = ['git', 'add', 'mbed-os.lib'] + return_code = run_cmd(cmd) + + os.chdir(cwd) + return not return_code + +def prepare_fork(arm_example): + """ Synchronises a cloned fork to ensure it is up to date with the original. + + Args: + arm_example - Full GitHub repo path for original example + ret - True if the fork was synchronised successfully, False otherwise + + """ + + print "In " + os.getcwd() + + for cmd in [['git', 'remote', 'add', 'armmbed', arm_example], + ['git', 'fetch', 'armmbed'], + ['git', 'reset', '--hard', 'armmbed/master'], + ['git', 'push', '-f', 'origin']]: + if run_cmd(cmd): + print("preparation of the fork failed!") + return False + return True + + +def upgrade_example(github, example, tag, user, ref): + """ Clone a fork of the example specified. + Ensures the fork is up to date with the original and then and updates the associated + mbed-os.lib file on that fork to correspond to the version specified by the GitHub tag supplied. + Also deals with multiple sub-examples in the GitHub repo, updating them in the same way. + The updates are pushed to the forked repo. + Finally a PR is raised against the original example repo for the changes. + + Args: + github - GitHub instance to allow internal git commands to be run + example - json example object containing the GitHub repo to update. + tag - GitHub tag corresponding to a version of mbed-os to upgrade to. + user - GitHub user name + ref - SHA corresponding to the tag + + """ + ret = False + print("\nUpdating example '%s'" % example['name']) + cwd = os.getcwd() + + full_repo_name = 'ARMmbed/'+ example['name'] + fork = "https://github.com/" + user + '/' + example['name'] + + # Check access to mbed-os repo + try: + repo = github.get_repo(full_repo_name, False) + + except: + print("\t\t!! Repo does not exist - skipping\n") + return False + + + # Clone the forked example repo + clone_cmd = ['git', 'clone', fork] + return_code = run_cmd(clone_cmd) + + if not return_code: + + # Find all examples + example_directories = find_all_examples(example['name']) + + os.chdir(example['name']) + + # checkout and synchronise the release-candidate branch + prepare_fork(example['github']) + + for example_directory in example_directories: + if not upgrade_single_example(example, tag, os.path.relpath(example_directory, example['name']), ref): + os.chdir(cwd) + return False + + # Setup the default commit message + commit_message = 'Updating mbed-os to ' + tag + + # Setup and run the commit command + commit_cmd = ['git', 'commit', '-m', commit_message] + return_code = run_cmd(commit_cmd) + if not return_code: + + # Setup and run the push command + push_cmd = ['git', 'push', 'origin'] + return_code = run_cmd(push_cmd) + + if not return_code: + body = "Please test/merge this PR and then tag Master with " + tag + # Raise a PR from release-candidate to master + user_fork = user + ':master' + try: + pr = repo.create_pull(title='Updating mbed-os to ' + tag, head=user_fork, base='master', body=body) + ret = True + except GithubException as e: + # Default to False + print("Creation of Pull Request from release-candidate to master failed with the following error!") + print e + else: + print("!!! Git push command failed.") + else: + print("!!! Git commit command failed.") + else: + print("!!! Could not clone user fork %s\n" % fork) + + + os.chdir(cwd) + return ret + +def create_work_directory(path): + """ Create a new directory specified in 'path', overwrite if the directory already + exists. + + Args: + path - directory path to be created. + + """ + if os.path.exists(path): + print("'%s' directory already exists. Deleting..." % path) + rmtree_readonly(path) + + os.makedirs(path) + +def test_compile(config, tag): + """ For each example repo identified in the config json object, clone, update mbed-os to + the specified tag and then compile for all supported toolchains. + + Args: + config - the json object imported from the file. + tag - GitHub tag corresponding to a version of mbed-os to upgrade to. + results - summary of compilation results. + + """ + # Create work directories + create_work_directory('test_compile') + + # Loop through the examples + results = {} + os.chdir('test_compile') + + lib.source_repos(config) + lib.update_mbedos_version(config, tag) + results = lib.compile_repos(config, SUPPORTED_TOOLCHAINS) + os.chdir("..") + + return results + + +def main(arguments): + """ Will update any mbed-os.lib files found in the example list specified by the config file. + If no config file is specified the default 'examples.json' is used. + The update is done by cloning a fork of each example (the fork must be present in the + github account specified by the github user parameter). The fork is searched for any + mbed-os.lib files and each one found is updated with the SHA corresponding to the supplied + github tag. A pull request is then made from the fork to the original example repo. + + Args: + tag - tag to update the mbed-os.lib to. E.g. mbed-os-5.3.1 + github_token - Pre-authorised token to allow github access + github_user - github username whose account contains the example forks + config_file - optional parameter to specify a list of examples + + """ + + parser = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument('tag', help="mbed-os tag to which all examples will be updated") + parser.add_argument('-c', '--config_file', help="Path to the configuration file (default is 'examples.json')", default='examples.json') + parser.add_argument('-T', '--github_token', help="GitHub token for secure access") + parser.add_argument('-U', '--github_user', help="GitHub user for forked repos") + + args = parser.parse_args(arguments) + + cfg = os.path.join(os.path.dirname(__file__), args.config_file) + + # Load the config file + config = json.load(open(os.path.join(os.path.dirname(__file__), + args.config_file))) + + if not config: + print("Failed to load config file '%s'" % args.config_file) + sys.exit(1) + + # Create working directory + create_work_directory('examples') + + github = Github(args.github_token) + + # Get the github sha corresponding to the specified mbed-os tag + cmd = ['git', 'rev-list', '-1', args.tag] + return_code, ref = run_cmd_with_output(cmd) + + if return_code: + print("Could not obtain SHA for tag: %s\n" % args.tag) + sys.exit(1) + + # Loop through the examples + failures = [] + successes = [] + results = {} + os.chdir('examples') + + for example in config['examples']: + # Determine if this example should be updated and if so update any found + # mbed-os.lib files. + + if upgrade_example(github, example, args.tag, args.github_user, ref): + successes += [example['name']] + else: + failures += [example['name']] + + os.chdir('../') + + # Finish the script and report the results + print(os.linesep + os.linesep +'Finished updating examples!' + os.linesep) + + if successes: + print('\nThe following examples updated successfully:') + for success in successes: + print(' - %s' % success) + + if failures: + print('\nThe following examples were not updated:') + for fail in failures: + print(' - %s' % fail) + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) \ No newline at end of file