def load_project(path='army.toml'): # TODO find a way to add line to error message file = os.path.expanduser(path) if os.path.exists(file)==False: raise ProjectException(f"{file}: file not found") content = {} try: log.info(f"Load project '{file}'") content = toml.load(file) log.debug(f"content: {content}") except toml.decoder.TomlDecodeError as e: print_stack() log.debug(e) raise ProjectException(f"{format(e)}") except Exception as e: print_stack() log.debug(e) raise ProjectException(f"{format(e)}") project = Project(data=content) project.check() return project
def rtt_console(ctx, speed, detach, viewer, **kwargs): # def rtt_console(ctx, tty, baud, echo, detach, **kwargs): log.info(f"rtt-console") opts = [] if echo == True: opts.append("-c") command = [] jlinkexe = locate_jlink() log.debug(f"jlink path: {jlinkexe}") try: command += [ "picocom", f"/dev/{tty}", "-b", f"{baud}", "-l", "--imap=lfcrlf", "--omap=crlf", "--escape=a" ] command += opts if detach == True: command += ["&"] # TODO add check picocom is installed subprocess.check_call(command) except Exception as e: print_stack() log.error(f"{e}")
def load_plugin(name, config, plugin_config): log.info(f"load plugin '{name}'") search_name = name search_version = None if ':' in name: search_name, search_version = name.split(':') if search_name.endswith("-plugin") == False: search_name = f"{search_name}-plugin" package = load_installed_package(search_name) if package is None: raise PluginException(f"{name}: plugin not installed") return if search_version and package.version != Version(search_version): raise PluginException( f"{name}: require version '{search_version}' but '{package.version}' found" ) try: spec = importlib.util.spec_from_file_location( "plugin", os.path.join(package.path, '__init__.py')) plugin = importlib.util.module_from_spec(spec) plugin.args = plugin_config spec.loader.exec_module(plugin) except Exception as e: print_stack() log.debug(e) raise PluginException(f"{name}: failed to load plugin")
def logout(ctx, name, **kwargs): log.info(f"logout {name}") config = ctx.parent.config # build repositories list repositories = load_repositories(config, prefix) repo = None for repository in repositories: if repository.name == name: repo = repository if repo is None: print(f"{name}: repository not found", file=sys.stderr) exit(1) service_id = f"army.{name}" try: repo.logout() except Exception as e: print_stack() log.debug(e) print(f"{name}: {e}", file=sys.stderr) exit(1) print("logged out")
def package(ctx, **kwargs): log.info(f"package") config = ctx.parent.config project = None if os.path.exists('army.toml'): try: # load project configuration project = load_project() except Exception as e: print_stack() log.debug(e) if project is None: log.info(f"no project loaded") exit(1) try: file = project.package(os.getcwd(), 'output') except Exception as e: print_stack() log.debug(e) print(f"packaging failed: {e}") exit(1) print(f"{os.path.relpath(file, os.getcwd())} generated")
def update(self): logged = self.load_credentials() if logged==False: print(f"{self.name}: warning: load credentials failed, update may be incomplete when using private repository", file=sys.stderr) try: uri, groupuri = self._decompose_uri() if logged: # no need to login for public repo but needed for private repo g = gitlab.Gitlab(uri, private_token=self._token) g.auth() else: g = gitlab.Gitlab(uri) except Exception as e: print_stack() log.debug(f"{type(e)} {e}") raise GitlabRepositoryException(f"{e}") try: group = None for ig in g.groups.list(): if ig.name==groupuri: group = ig except gitlab.exceptions.GitlabGetError as e: print_stack() log.debug(f"{type(e)} {e}") raise GitlabRepositoryException(f"{groupuri}: group not found") except Exception as e: print_stack() log.debug(f"{type(e)} {e}") raise GitlabRepositoryException(f"{e}") if group is None: raise GitlabRepositoryException(f"{groupuri}: group not found") try: # get versions projects = {} for p in g.projects.list(): projects[p.name] = p except Exception as e: print_stack() log.debug(f"{type(e)} {e}") raise GitlabRepositoryException(f"{e}") try: for p in group.projects.list(all=True): log.debug(f"update repo {p.name}") if p.name not in projects: log.error(f"{p.name}: update failed") continue # TODO raise error? project = projects[p.name] for release in project.releases.list(): if release.tag_name.startswith("v"): self._index_package(project.name, release.tag_name[1:], project.description) except Exception as e: print_stack() log.debug(f"{type(e)} {e}") raise GitlabRepositoryException(f"{e}") super(GitlabRepository, self).update()
def check(self, value): try: # provide at least one version to resolve 'latest' version = VersionRange(value, versions=["0.0.0"]) except Exception as e: print_stack() log.debug(f"{type(e)} {e}") raise ValidatorException(f"'{value}' is not a valid version range")
def package(self, path, output_path): # execute prebuild step if os.path.exists(os.path.expanduser(os.path.join(path, 'pkg', 'prebuild'))): log.info("execute prebuild script") subprocess.check_call([os.path.join(os.path.expanduser(path), 'pkg', 'prebuild')]) # create temporary folder d = tempfile.TemporaryDirectory() files = [] for include in self.packaging.include: files.append(include) files.append('army.toml') # copy files for include in files: source = os.path.join(os.path.expanduser(path), include) dest = d.name if os.path.exists(source)==False: raise ProjectException(f"{include}: package item does not exists") try: if os.path.isfile(source): shutil.copy(source, dest) else: shutil.copytree(source, os.path.join(dest, os.path.basename(source)), dirs_exist_ok=True) except Exception as e: print_stack() log.debug(e) raise ProjectException(f"{e}") # TODO add exclude # create zip file pkg_name = f"{self.name}-{self.version}.zip" pkg_path = os.path.join(os.path.expanduser(path), output_path, pkg_name) if os.path.exists(pkg_path): log.info(f"remove existing file {pkg_path}") os.remove(pkg_path) if os.path.exists(output_path)==False: os.mkdir(output_path) log.info(f"create file {pkg_path}") with zipfile.ZipFile(pkg_path, 'w', zipfile.ZIP_DEFLATED) as zf: for root, dirs, files in os.walk(d.name): for file in files: zf.write(os.path.join(root, file), os.path.relpath(os.path.join(root, file), d.name)) # execute postbuild step if os.path.exists(os.path.expanduser(os.path.join(path, 'pkg', 'postbuild'))): log.info("execute postbuild script") subprocess.check_call([os.path.join(os.path.expanduser(path), 'pkg', 'postbuild')]) return pkg_path
def logout(self): service_id = f"army.{self.name}" try: keyring.delete_password(service_id, 'token') except keyring.errors.PasswordDeleteError as e: print_stack() log.debug(f"{type(e)}: {e}") raise GitlabRepositoryException("not logged to repository") except Exception as e: print_stack() log.debug(type(e), e) raise GitlabRepositoryException(f"{e}")
def load_credentials(self): try: service_id = f"army.{self.name}" token = keyring.get_password(service_id, 'token') if token is None: return False except Exception as e: print_stack() log.debug(f"{type(e)} {e}") return False self._token = token return True
def load_credentials(self): try: service_id = f"army.{self.name}" user = keyring.get_password(service_id, 'user') if user is None: return False password = keyring.get_password(service_id, user) except Exception as e: print_stack() log.debug(f"{type(e)} {e}") return False self._user = user self._password = password return True
def logout(self): service_id = f"army.{self.name}" try: user = keyring.get_password(service_id, 'user') keyring.delete_password(service_id, 'user') keyring.get_password(service_id, user) except keyring.errors.PasswordDeleteError as e: print_stack() log.debug(e) raise GithubRepositoryException("not logged to repository") except Exception as e: print_stack() log.debug(e) raise GithubRepositoryException(f"{e}")
def init(ctx, **kwargs): log.info(f"init") # load configuration config = ctx.parent.config project_config = None try: # load project configuration project_config = load_project(config) config = project_config except Exception as e: print_stack() log.debug(e) log.info(f"no project loaded") print("Not yet implemented")
def _search_dir(path): if os.path.exists(os.path.expanduser(path)) == False: return [] res = [] for package in os.listdir(os.path.expanduser(path)): try: pkg = _load_installed_package(os.path.join(path, package)) res.append(pkg) except Exception as e: print_stack() log.debug(e) log.error( f"{os.path.join(path, package)}: not a valid package") return res
def login(self, token=None, **kwargs): if token is None: raise GitlabRepositoryException("only token auth is supported") try: uri, org = self._decompose_uri() g = gitlab.Gitlab(uri, private_token=token) g.auth() self._token = token except Exception as e: print_stack() log.debug(f"{type(e)}: {e}") raise GitlabRepositoryException("invalid token") service_id = f"army.{self.name}" # store token on keyring keyring.set_password(service_id, 'token', token)
def clean(ctx, **kwargs): log.info(f"clean") # load configuration config = ctx.config # load profile profile = ctx.profile # set code build path output_path = 'output' # load project project = ctx.project if project is None: print(f"no project found", sys.stderr) exit(1) # load dependencies try: dependencies = load_project_packages(project) log.debug(f"dependencies: {dependencies}") except Exception as e: print_stack() print(f"{e}", file=sys.stderr) clean_exit() # get arch from profile arch, arch_package = get_arch(profile, project, dependencies) # set build path if arch.mpu is None: build_path = os.path.join(output_path, arch.cpu) else: build_path = os.path.join(output_path, arch.mpu) log.info(f"clean path: {build_path}") if os.path.exists(build_path) == True: shutil.rmtree(build_path) print(f"cleaned")
def update(ctx, **kwargs): log.info(f"update") config = ctx.parent.config # build repositories list repositories = load_repositories(config, prefix) if len(repositories)==0: print("no repository configured") return for r in repositories: print(f"update {r.name}") try: r.update() except Exception as e: print_stack() log.debug(f"{type(e)} {e}") print(f"{r.name}: {e}", file=sys.stderr) print("updated")
def _load_installed_package(path): # TODO find a way to add line to error message file = os.path.expanduser(os.path.join(path, 'army.toml')) if os.path.exists(file) == False: raise PackageException(f"{file}: file not found") content = {} try: log.info(f"load installed package '{file}'") content = toml.load(file) log.debug(f"content: {content}") except Exception as e: print_stack() log.debug(e) raise PackageException(f"{format(e)}") project = InstalledPackage(data=content, path=path) project.check() return project
def load(self): # load project file # TODO find a way to add line to error message file = os.path.expanduser( os.path.join(prefix or "", self.uri, 'army.toml')) if os.path.exists(file) == False: raise LocalGitRepositoryException(f"{file}: file not found") content = {} try: log.info(f"Load git repository '{file}'") content = toml.load(file) log.debug(f"content: {content}") except Exception as e: print_stack() log.debug(e) raise LocalGitRepositoryException(f"{file}: {format(e)}") self._project = RepositoryPackage(data=content, repository=self) self._project.check()
def login(ctx, name, token, **kwargs): log.info(f"login {name}") config = ctx.parent.config # build repositories list repositories = load_repositories(config, prefix) repo = None for repository in repositories: if repository.name == name: repo = repository if repo is None: print(f"{name}: repository not found", file=sys.stderr) exit(1) if token == True: token = getpass.getpass(prompt='token: ', stream=None) try: repo.login(token=token) except Exception as e: print_stack() log.debug(e) print(f"{name}: {e}", file=sys.stderr) exit(1) else: user = input("login: "******"{name}: {e}", file=sys.stderr) exit(1) print("logged in")
def _search_dir(path, version_range): if os.path.exists(os.path.expanduser(path)) == False: return None for package in os.listdir(os.path.expanduser(path)): if name == package: try: pkg = _load_installed_package(os.path.join(path, package)) if version_range is None: return pkg else: version_range = VersionRange(version_range, [pkg.version]) if version_range.match(Version(pkg.version)): return pkg except Exception as e: print_stack() log.debug(e) log.error( f"{os.path.join(path, package)}: not a valid package") return None
def get_arch(profile, project, dependencies): # add arch try: arch = profile.data["/arch"] arch_name = profile.data["/arch/name"] except Exception as e: print_stack() log.error(e) print("No arch definition provided by profile", file=sys.stderr) exit(1) if 'name' not in arch: print("Arch name missing", file=sys.stderr) exit(1) package = None res_package = None if 'package' in arch: if 'version' in arch: package_version = arch['version'] else: package_version = 'latest' package_name = arch['package'] package = load_installed_package(package_name, package_version) res_package = package if package is None: package = project # search arch in found package archs = package.archs arch = next(arch for arch in archs if arch.name == arch_name) if arch is None: print(f"Arch {arch_name} not found in {package}", file=sys.stderr) exit(1) return arch, res_package
def login(self, user, password): try: g = github.Github(user, password) for repo in g.get_user().get_repos(): # if it suceeds one time then it means we are logged ok name = repo.name break self._user = user self._password = password except BadCredentialsException as e: print_stack() log.debug(e) raise GithubRepositoryException("invalid username/password") except Exception as e: print_stack() log.debug(f"{type(e)} {e}") raise GithubRepositoryException("login failed") service_id = f"army.{self.name}" # store password on keyring keyring.set_password(service_id, user, password) # store user on keyring keyring.set_password(service_id, 'user', user)
def load_configuration_repository_file(path, parent=None): # TODO find a way to add line to error message file = os.path.expanduser(path) if os.path.exists(file) == False: raise ConfigException(f"{file}: file not found") config = {} try: log.info(f"Load config '{path}'") config = toml.load(file) log.debug(f"content: {config}") except Exception as e: print_stack() log.debug(e) raise ConfigException(f"{format(e)}") try: res = ArmyConfigRepository(value=config, parent=parent) except Exception as e: print_stack() log.debug(e) raise ConfigException(f"{format(e)}") return res
def locate_tools(profile): tools = { "c": "c compiler", "c++": "c++ compiler", "asm": "assembler compiler", "ld": "linker", "ar": "archive tool", "objcopy": "object file copy tool", "objdump": "object file content dump tool", "size": "object file size tool", "nm": "symbols export tool", } for tool, name in tools.items(): try: tool_path = profile.data[f"/tools/{tool}/path"] if os.path.exists(tool_path) == False: print(f"{tool_path}: path not found for {name}", file=sys.stderr) exit(1) except Exception as e: print_stack() log.error(e) print(f"No {name} defined", file=sys.stderr) exit(1)
def publish(ctx, name, force, **kwargs): log.info(f"publish") config = ctx.parent.config project = None if os.path.exists('army.toml'): try: # load project configuration project = load_project() except Exception as e: print_stack() log.debug(e) if project is None: log.info(f"no project loaded") exit(1) # build repositories list repositories = load_repositories(config, prefix) repo = None for repository in repositories: if repository.name == name: repo = repository if repo is None: print(f"{name}: repository not found", file=sys.stderr) exit(1) if repo.load_credentials() == False: print(f"{name}: no credentials found", file=sys.stderr) exit(1) # package try: file = project.package(os.getcwd(), 'output') print(f"{os.path.relpath(file, os.getcwd())} generated") except Exception as e: print_stack() log.debug(e) print(f"packaging failed: {e}") exit(1) # TODO check version is tagged and files are commited and pushed # publish try: repo.publish(project, file, overwrite=force) except Exception as e: print_stack() log.debug(e) print(f"publishing failed: {e}") exit(1) print(f"{os.path.relpath(file, os.getcwd())} published")
def update(self): if self.load_credentials() == False: print( f"{self.name}: warning: load credentials failed, update may fail due to rate limitation", file=sys.stderr) try: uri, org = self._decompose_uri() # no need to login for public repo but needed for private repo g = github.Github(self._user, self._password) except Exception as e: print_stack() log.debug(f"{type(e)} {e}") raise GithubRepositoryException(f"{e}") try: organization = g.get_organization(org) except UnknownObjectException as e: print_stack() log.debug(f"{type(e)} {e}") raise GithubRepositoryException(f"{org}: not found") except Exception as e: print_stack() log.debug(f"{type(e)} {e}") raise GithubRepositoryException(f"{e}") try: # get versions for repo in organization.get_repos(): # remove previous index state self._index_remove_package(repo.name) log.debug(f"update repo {repo}") for release in repo.get_releases(): if release.tag_name.startswith("v"): self._index_package(repo.name, release.tag_name[1:], repo.description) except Exception as e: print_stack() log.debug(f"{type(e)} {e}") raise GithubRepositoryException(f"{e}") super(GithubRepository, self).update()
def flash(ctx, timeout, **kwargs): log.info(f"flash") # load configuration config = ctx.config # load profile profile = ctx.profile # load project project = ctx.project if project is None: print(f"no project found", sys.stderr) exit(1) # load dependencies try: dependencies = load_project_packages(project) log.debug(f"dependencies: {dependencies}") except Exception as e: print_stack() print(f"{e}", file=sys.stderr) clean_exit() # get arch from profile arch, arch_package = get_arch(profile, project, dependencies) # get target from profile target = get_target(profile) if arch.mpu is None: print("Missing mpu informations from arch", file=sys.stderr) exit(1) # set code build path output_path = 'output' build_path = os.path.join(output_path, arch.mpu) log.info(f"build_path: {build_path}") device = arch.mpu if device.startswith("ATSAMD"): device = device.replace("ATSAMD", "SAMD") log.info(f"Flash {device} with JLink") # hex_file = os.path.join(build_path, "bin/firmware.hex") binfile = os.path.join(build_path, "bin/firmware.bin") # jlinkexe = locate_jlink(profile) log.debug(f"jlink path: {jlinkexe}") # TODO: en cas d'immpossibilité de programmation il y a probablement une mauvaise configuration du proc # voir http://forum.segger.com/index.php?page=Thread&postID=11854, avec Ozone changer la zone mémoire 00804000 # 0x00804000 contains the calibration data AUX0–NVM User # 0x00804000 = FF C7 E0 D8 5D FC FF FF FF FF FF FF FF FF FF FF # [[ $opt_erase -ne 0 ]] && erase=erase # [[ $opt_erase -ne 0 ]] || erase=r if os.path.exists('/etc/udev/rules.d/99-jlink.rules') == False: print( f"Can not execute jlink with current user, add '{os.path.join(tools_path, 'jlink/99-jlink.rules')}' inside '/etc/udev/rules.d/'", file=sys.stderr) exit(1) try: # commandline = [ # f"{os.path.join(tools_path, jlinkexe)}", # "-device", f"at{device}", # "-if", "swd", # "-speed", "12000" # ] # log.info(" ".join(commandline)) # p = Popen(commandline, stdout=PIPE, stdin=PIPE, stderr=PIPE) # # stdout_data = p.communicate(input=b'data_to_write')[0] # # print(stdout_data.decode('utf-8')) # commands = [ # "connect", # "r", # # {erase} # f"loadfile {hex_file}", # "exit" # ] # for command in commands: # p.stdin.write(f"{command}\n".encode('utf-8')) # p.stdin.flush() # line = p.stdout.readline() # while line: # print(line.decode('utf-8'), end='') # line = p.stdout.readline() # # p.stdin.close() # p.terminate() # p.wait(timeout=0.2) jlink = JLink(f"AT{device}") jlink.open(timeout) jlink.connect(timeout) jlink.erase(timeout) jlink.flash_file( '/home/seb/git/bootloader/output/SAMD21G18A/bin/firmware.bin', power_on=True, timeout=timeout) jlink.reset(timeout, halt=False) except Exception as e: print_stack() print(f"{e}", file=sys.stderr) exit(1)
def main(): global prefix global config global project global default_target global target_name try: # cli_init will initialize the logger only, everything else is ignored at this point # we need to load the plugins before showing any help cli_init() except: pass global premature_exit if premature_exit: exit(1) # load army configuration files prefix = os.getenv('ARMY_PREFIX', None) if prefix is not None: log.debug(f"using {prefix} as path prefix") try: config = load_configuration(parent=root_config, prefix=prefix) except Exception as e: print_stack() print(f"{e}", file=sys.stderr) exit(1) # load internal plugins import army.plugin.repository import army.plugin.dependency import army.plugin.package # import army.plugin.build # load plugins # TODO load plugins from installed packages if os.path.exists('army.toml'): try: project = load_project() except Exception as e: print_stack() print(f"army.toml: {e}", file=sys.stderr) exit(1) # load default target if exists if project is not None: # get target config default_target = None if target_name is None and project.default_target: target_name = project.default_target if target_name is not None: if target_name in project.target: default_target = project.target[target_name] else: print(f"{target_name}: target not defined in project", file=sys.stderr) exit(1) log.info(f"current target: {target_name}") if project is not None: # load plugins at project level for plugin in project.plugins: plugin_config = None # search for plugin configuration in project if plugin in project.plugin: plugin_config = project.plugin[plugin] # search plugin configuration in target if plugin in default_target.plugin: plugin_config = default_target.plugin[plugin] try: load_plugin(plugin, config, plugin_config) except Exception as e: print_stack() print(f"{e}") if default_target is not None: # load plugins at target level for plugin in default_target.plugins: plugin_config = None # search plugin configuration in target if plugin in default_target.plugin: plugin_config = default_target.plugin[plugin] try: load_plugin(plugin, config, plugin_config) except Exception as e: print_stack() print(f"{e}") # parse command line cli()
def console(ctx, tty, baud, echo, detach, **kwargs): log.info(f"console") global tools_path print("Use ctrl-a to send content to serial") opts = [] if echo == True: opts.append("-c") picocom_command = [] picocom = shutil.which("picocom") if picocom is None: print( f"picocom: not found, you can install it with 'sudo apt-get install picocom'" ) return try: picocom_command += [ "picocom", f"/dev/{tty}", "-b", f"{baud}", "-l", "--imap=lfcrlf", "--omap=crlf", "--escape=a" ] picocom_command += opts if detach == True: # xterm = shutil.which("xterm") # if xterm is None: # print(f"xterm: not found, you can install it with 'sudo apt-get install xterm'") # return # command += [ # "nohup", # "xterm", # "-j", # "-rightbar", # "-sb", # "-si", # "-sk", # "-sl", "99999", # "-e" # ] terminator = shutil.which("terminator") if terminator is None: print( f"terminator: not found, you can install it with 'sudo apt-get install terminator'" ) return command = [ "terminator", "--no-dbus", "--command", ' '.join([f'{tools_path}/plugin/daemon.sh'] + picocom_command), ] log.debug(" ".join(command)) subprocess.Popen(command, start_new_session=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) sleep(1) else: log.debug(" ".join(picocom_command)) subprocess.check_call(picocom_command) except Exception as e: print_stack() log.error(f"{e}") exit(1)