def package_install(path, package, batch=False, pkgrepo=None, pkglist=None): """ Install a package or roll back to last coherent state upon failure. If batch is True, do not update package list (caller will update). Returns True on success, else (error or already installed) False. """ pkgpath = package_dir_path(path) if pkgrepo is None: pkgrepo = package_repo_open(pkgpath) if pkglist is None: pkglist = package_list_read(pkgpath) pkgnames = list(map(lambda x: x['name'], pkglist)) try: _package_install_unsafe(path, package, pkgrepo, pkglist, pkgnames) pkgrepo.index.add(['.gitmodules', package.name]) pkgrepo.index.commit('Installed ' + package.name) if not batch: package_list_add_many(pkgpath, [package]) except AlreadyInstalledException as e: return e.link_updated except Exception as e: # Installation failed, roll back try: sm = pkgrepo.submodule(package.name) sm.remove() except Exception: pass pkgrepo.git.clean('-fdX') # Remove all untracked files writeln('Install aborted.') writeln(str(e)) return False return True
def verify_path(path): """check if the project path is correct""" if not os.path.exists(path) or not os.path.isdir(path): writeln( '\nPath specified for project creation does not exist or is not a directory', color=Fore.RED) quit(2)
def package_update_all(path): """Update all installed packages""" repo = package_repo_open(package_dir_path(path)) for sm in repo.submodules: write('Updating %s... ' % sm.name) remote = sm.module().remotes[0] remote.pull('-f') # sm.update is defective; use this instead. writeln('Done.') if repo.is_dirty(): # Something has changed repo.index.commit('Updated ' + sm.name)
def update_wcosa(path, board, ide): """Updates existing WCosa project""" path = str(path) write('Updating work environment - ', color=Fore.CYAN) verify_path(path) templates_path = helper.linux_path(helper.get_wcosa_path() + '/templates') user_config_path = path + '/config.json' internal_config_path = path + '/wcosa/internal-config.json' general_cmake_path = path + '/wcosa/CMakeLists.txt' # create src, lib, and wcosa folders create_folders(path) # copy all then CMakeLists templates and configuration templates copyfile(templates_path + '/cmake/CMakeLists.txt.tpl', general_cmake_path) copyfile(templates_path + '/config/internal-config.json.tpl', internal_config_path) # recopy any missing files if not os.path.exists(path + '/config.json'): copyfile(templates_path + '/config/config.json.tpl', user_config_path) writeln('done') write('Updating configurations with new changes - ', color=Fore.CYAN) user_data = config.fill_user_config(user_config_path, board, Port(None), ide) project_data = config.fill_internal_config(internal_config_path, path, user_data) cmake.parse_update(general_cmake_path, project_data) if user_data['ide'] == 'clion': copyfile(templates_path + '/ide/clion/CMakeListsPrivate.txt.tpl', path + '/CMakeListsPrivate.txt') # recopy any missing files if not os.path.exists(path + '/CMakeLists.txt'): copyfile(templates_path + '/ide/clion/CMakeLists.txt.tpl', path + '/CMakeLists.txt') if not os.path.exists(path + '/.gitignore-files'): copyfile(templates_path + '/gitignore-files/.gitignore-clion', path + '/.gitignore') cmake.parse_update(path + '/CMakeLists.txt', project_data) cmake.parse_update(path + '/CMakeListsPrivate.txt', project_data) elif not os.path.exists(path + '/.gitignore'): copyfile(templates_path + '/gitignore-files/.gitignore-general', path + '/.gitignore') writeln('done')
def verify(self): # verify the port ports = list(serial.tools.list_ports.comports()) devices = [] for p in ports: devices.append(p.device) if self.name not in devices: output.writeln('There is no device connected to this port', Fore.RED) quit(2)
def print_boards(): """Print all the available boards and their name""" boards = board_parser.get_all_board(helper.get_wcosa_path() + '/wcosa/boards.json') output.writeln('Boards compatible with this project are: ', Fore.CYAN) for curr_board in boards: name = board_parser.get_board_properties( curr_board, helper.get_wcosa_path() + '/wcosa/boards.json')['name'] output.writeln('{:15s} --->\t{}'.format(curr_board, name))
def serial_monitor(port, baud): """ Open serial monitor to the specified port with the given baud rate. """ write('Serial Monitor ', Fore.CYAN) write(port, Fore.YELLOW) write(' @ ', Fore.CYAN) writeln(baud, Fore.YELLOW) sys.argv = ['monitor', '--exit-char', '3'] try: miniterm.main(default_port=port or serial_ports(), default_baudrate=baud or 9600) except KeyboardInterrupt: writeln('\nExiting serial monitor', Fore.CYAN)
def package_repo_init(pkgpath): """Initialize package repo""" write('Initializing package repository... ') sys.stdout.flush() pkgrepo = git.Repo.init(pkgpath) pkglist_exists = os.path.exists(PACKAGE_LIST_FILE) if not pkglist_exists: with open(PACKAGE_LIST_FILE, 'w+') as pkglist: pkglist.write('[]') # Start with empty package list pkgrepo.index.commit('Initialized repository') writeln('Done') return pkgrepo
def __init__(self, name): if name is None: self.same = True self.name = None else: self.name = name # verify the board boards = board_parser.get_all_board(helper.get_wcosa_path() + '/wcosa/boards.json') if name not in boards: output.writeln( 'Board Invalid. Run wcosa script with boards option to see all the valid boards', Fore.RED) quit(2) self.same = False
def package_uninstall(path, package, batch=False, pkgrepo=None, pkglist=None): """ Uninstall a package or unlink from given location if linked to multiple. If batch is True, do not update package list (caller will update). Returns True on success, else (on error) False. """ pkgpath = package_dir_path(path) if pkgrepo is None: pkgrepo = package_repo_open(pkgpath) if pkglist is None: pkglist = package_list_read(pkgpath) pkgnames = list(map(lambda x: x['name'], pkglist)) write('Uninstalling %s... ' % package.name) sys.stdout.flush() if package.name not in pkgnames: writeln('Not installed.') return False try: paths = pkglist[pkgnames.index(package.name)]['paths'] # Get paths try: os.unlink(path + '/' + package.path) except Exception: writeln('%s not linked at %s.' % (package.name, package.path)) return False if len(paths) > 1: # Installed in multiple locations writeln('Unlinked from %s.' % package.path) if not batch: package_list_remove_many(pkgpath, [package]) return True else: sm = pkgrepo.submodule(package.name) sm.remove(force=True) # Remove even if there are local changes pkgrepo.index.add(['.gitmodules']) pkgrepo.index.remove([package.name], r=True) # Remove recursively pkgrepo.index.commit('Uninstalled ' + package.name) if not batch: package_list_remove_many(pkgpath, [package]) except Exception as e: writeln('Failed to uninstall %s: %s' % (package.name, e)) return False else: writeln('Uninstalled.') return True
def serial_ports(): """Returns the serial port by scanning all of them""" ports = list(serial.tools.list_ports.comports()) if not ports: output.writeln('No device is connected at the moment', Fore.RED) quit(2) for p in ports: test_str = str(p.description).lower() if 'arduino' in test_str or 'Arduino' in test_str: return p.device # if no arduino port is found choose the first port output.writeln( 'No Arduino port found, choosing the first available one. Specify the port if you want another port', Fore.YELLOW) return ports[0].device
def clean_wcosa(path): """cleans the bin folder, deleting all the build files""" path = str(path) # confirm if bin folder/path exists if os.path.exists(helper.linux_path(path + '/wcosa/bin')): os.chdir(helper.linux_path(path + '/wcosa/bin')) else: output.writeln('\nNot a valid WCosa project', Fore.RED) quit(2) # check if the current build files are for the current board # clean and build if boards are different try: output.write('Cleaning build files - ', Fore.GREEN) for folder in helper.get_dirs(helper.get_working_directory()): shutil.rmtree(folder) for file in helper.get_files(helper.get_working_directory()): os.unlink(file) except IOError: output.writeln('Error while cleaning build files', Fore.RED) output.writeln('done')
def main(): options = parse() path = Path(options.path) # based on the action call scripts if options.action == 'version': output.writeln(__version__) if options.action == 'boards': print_boards() elif options.action == 'create': handle.create_wcosa(path, Board(options.board), IDE(options.ide)) elif options.action == 'update': handle.update_wcosa(path, Board(options.board), IDE(options.ide)) elif options.action == 'build': use.build_wcosa(path, Generator(options.generator), options.make, options.cmake) elif options.action == 'make': use.build_wcosa(path, make=options.make, needs_cmake=False) elif options.action == 'upload': use.upload_wcosa(path, Port(options.port)) elif options.action == 'clean': use.clean_wcosa(path) elif options.action == 'monitor': monitor.serial_monitor(options.port, options.baud) elif options.action == 'package': if options.package_command == 'install': if not options.package: package_manager.package_install_pkglist(options.path) else: package_manager.package_install_many( options.path, ' '.join(options.package).split(', ')) elif options.package_command == 'update': package_manager.package_update_all(options.path) elif options.package_command == 'remove': package_manager.package_uninstall_many( options.path, ' '.join(options.package).split(', '))
def _package_install_unsafe(path, package, pkgrepo, pkglist, pkgnames): """ NOT A PUBLIC INTERFACE: use package_install[_many] instead. Try to install a package and forward exceptions to the caller. Will leave package repository in dirty state. Returns """ write('Installing %s... ' % package.name) sys.stdout.flush() repo_path = package_repo_path(path, package) if package.name in pkgnames and os.path.exists(repo_path): index = pkgnames.index(package.name) link_path = package_link_path(path, package) if package.path in pkglist[index]['paths'] and os.path.lexists( link_path): writeln('Already installed.') raise AlreadyInstalledException(link_updated=False) else: write('Already installed, linking to %s... ' % package.path) sys.stdout.flush() package_link(path, package) writeln('Linked.') raise AlreadyInstalledException(link_updated=True) # If the above did not return, we need to actually install the package try: if package.branch: pkgrepo.create_submodule(package.name, package.name, url=package.url, branch=package.branch) else: pkgrepo.create_submodule(package.name, package.name, url=package.url) except Exception: # Default message is cryptic raise GitFetchException(package) package_link(path, package) writeln('Installed.')
def build_wcosa(path, generator=None, make=None, cmake=None, needs_cmake=True): """build wcosa project, cmake and make""" path = str(path) output.writeln('Wcosa project build started', Fore.GREEN) output.write('Verifying the project structure - ', Fore.GREEN) # check if path is valid if not os.path.exists(path): output.write('Project path is invalid: ' + path, Fore.RED) quit(2) # confirm if bin folder/path exists if os.path.exists(helper.linux_path(path + '/wcosa/bin')): os.chdir(helper.linux_path(path + '/wcosa/bin')) else: output.writeln('\nNot a valid WCosa project', Fore.RED) quit(2) output.writeln('done') cmake_program = cmake or get_cmake_program() make_program = make or get_make_program() output.write('Verifying cmake and make installs - ', Fore.GREEN) # check if cmake is in environment paths (unix/linux based systems) if not cmake_program: output.writeln( '\ncmake does not exist, please install it or make sure it is in your environment PATH', Fore.RED) quit(2) # check if make is in environment paths (unix/linux based systems) if not make_program: output.writeln( '\nmake does not exist, please install it or make sure it is in your environment PATH', Fore.RED) quit(2) output.writeln('done') if not generator or not str(generator): generator = Generator(get_generator_for(make_program)) # check if path is valid and get build information from the user config if not os.path.exists(path + '/config.json'): output.write( 'Project user configuration file does not exist, recreate or update the project', Fore.RED) quit(2) else: with open(path + '/config.json') as f: data = json.load(f, object_pairs_hook=OrderedDict) board = data['board'] # check if the current build files are for the current board # clean and build if boards are different if os.path.exists(helper.get_working_directory() + '/Makefile'): with open(helper.get_working_directory() + '/Makefile') as f: makefile_str = ''.join(f.readlines()) if '\n' + board not in makefile_str: output.writeln( 'Since a new board is detected, full build will be triggered', Fore.GREEN) clean_wcosa(path) if needs_cmake: output.writeln('Running the build using cmake and ' + str(generator), Fore.GREEN) cmake_code = subprocess.call(['cmake', '-G', str(generator), '..']) if cmake_code != 0: output.writeln( 'Project build unsuccessful, cmake exited with error code ' + str(cmake_code), Fore.RED) quit(2) make_code = subprocess.call([make_program]) # make returns 2 if the project has not been configured if make_code == 2 or make_code == '2': output.writeln('No Makefiles present, run wcosa build', Fore.RED) quit(2) if make_code != 0: output.writeln( 'Project build unsuccessful, make exited with error code ' + str(make_code), Fore.RED) quit(2) output.writeln('Project successfully built', Fore.GREEN)
def upload_wcosa(path, port): """upload wcosa project to the port specified or an automatically selected one""" path = str(path) output.writeln('Wcosa project upload started', Fore.GREEN) output.write('Verifying build files - ', Fore.GREEN) # confirm if bin folder/path exists if os.path.exists(helper.linux_path(path + '/wcosa/bin')): os.chdir(helper.linux_path(path + '/wcosa/bin')) else: output.writeln('\nNot a valid WCosa project', Fore.RED) # if project has not been built right if not os.path.exists(helper.get_working_directory() + '/Makefile'): output.writeln('\nNo Makefile, build the project first ', Fore.RED) quit(2) output.writeln('done') # check if path is valid and get the user config if not os.path.exists(path + '/config.json'): output.write( 'Project user configuration file does not exist, recreate or update the project', Fore.RED) quit(2) user_port = None data = None else: with open(path + '/config.json') as f: data = json.load(f, object_pairs_hook=OrderedDict) user_port = data['port'].strip(' ') if not port.use_same(): output.write('Using the port provided - ' + str(port), Fore.GREEN) else: # if port is defined in the config file, then use that port if user_port.lower() != 'None'.lower() and user_port != '': port = user_port output.writeln('Using the port from the config file: ' + user_port, Fore.GREEN) # check if the port is valid if port not in get_serial_devices(): output.writeln( '\nPort provided does not have a valid device connected to it', Fore.RED) quit(2) else: output.writeln('Automatically selecting a port', Fore.GREEN) port = serial_ports() output.writeln('Port chosen: ' + port, Fore.GREEN) # save the port in the config file so that update can update the project based on that temp_data = copy.copy(data) temp_data['port'] = str(port) with open(path + '/config.json', 'w') as f: json.dump(temp_data, f, indent=settings.get_settings_value('json-indent')) # do not print the updating logs output.output_status(False) # reset the port in the user config to the port that was there with open(path + '/config.json', 'w') as f: json.dump(data, f, indent=settings.get_settings_value('json-indent')) output.writeln('Upload triggered on ' + str(port), Fore.GREEN) upload_code = subprocess.call([get_make_program(), 'upload']) if upload_code != 0: output.writeln( 'Project upload unsuccessful, make exited with error code ' + str(upload_code), Fore.RED) quit(2) output.writeln('Project upload successful', Fore.GREEN)
def create_wcosa(path, board, ide): """Creates WCosa project from scratch""" path = str(path) write('Creating work environment - ', color=Fore.CYAN) verify_path(path) templates_path = helper.linux_path(helper.get_wcosa_path() + '/templates') user_config_path = helper.linux_path(path + '/config.json') internal_config_path = helper.linux_path(path + '/wcosa/internal-config.json') general_cmake_path = helper.linux_path(path + '/wcosa/CMakeLists.txt') src_path = helper.linux_path(path + '/src/main.cpp') # check if there are already src and lib folders. We do not want to delete those folders if len(helper.get_dirs(path)) > 0 and (os.path.exists(path + '/src') or os.path.exists(path + '/lib')): writeln( '\nThere is already a src and/or lib folder in this directory. Use wcosa update instead', color=Fore.RED) quit(2) # create src, lib, and wcosa folders create_folders(path, True) # copy all then CMakeLists templates and configuration templates copyfile(templates_path + '/cmake/CMakeLists.txt.tpl', general_cmake_path) copyfile(templates_path + '/config/internal-config.json.tpl', internal_config_path) copyfile(templates_path + '/config/config.json.tpl', user_config_path) copyfile(templates_path + '/examples/main.cpp', src_path) writeln('done') write('Updating configurations based on the system - ', color=Fore.CYAN) user_data = config.fill_user_config(user_config_path, board, Port(None), ide) project_data = config.fill_internal_config(internal_config_path, path, user_data) cmake.parse_update(general_cmake_path, project_data) if user_data['ide'] == 'clion': copyfile(templates_path + '/ide/clion/CMakeLists.txt.tpl', path + '/CMakeLists.txt') copyfile(templates_path + '/ide/clion/CMakeListsPrivate.txt.tpl', path + '/CMakeListsPrivate.txt') copyfile(templates_path + '/gitignore-files/.gitignore-clion', path + '/.gitignore') cmake.parse_update(path + '/CMakeLists.txt', project_data) cmake.parse_update(path + '/CMakeListsPrivate.txt', project_data) else: copyfile(templates_path + '/gitignore-files/.gitignore-general', path + '/.gitignore') writeln('done') writeln('Project Created and structure:', color=Fore.YELLOW) writeln('src -> All source files go here:', color=Fore.YELLOW) writeln('lib -> All custom libraries go here', color=Fore.YELLOW) writeln('wcosa -> All the build files are here (do no modify)', color=Fore.YELLOW)