def disable(self): if 'linux' in platform: return self._disable_linux() elif 'darwin' in platform: self.profile = Profile() return self._disable_darwin() elif 'windows' in platform: return self._disable_windows()
def __init__(self, project_dir=''): self.profile = Profile() self.resources = Resources() if project_dir is not None: # Move to project dir project_dir = util.check_dir(project_dir) os.chdir(project_dir)
def _setup_windows(self): profile = Profile() resources = Resources() self.name = 'drivers' self.version = util.get_package_version(self.name, profile) self.spec_version = util.get_package_spec_version(self.name, resources)
def __init__(self): profile = Profile() resources = Resources() self.name = 'examples' self.examples_dir = util.get_package_dir(self.name) self.version = util.get_package_version(self.name, profile) self.spec_version = util.get_package_spec_version(self.name, resources)
def cli(ctx, list, verbose, exe): """Apio configuration.""" if list: # pragma: no cover profile = Profile() profile.list() elif verbose: # pragma: no cover profile = Profile() profile.add_config('verbose', verbose) elif exe: # pragma: no cover profile = Profile() profile.add_config('exe', exe) else: click.secho(ctx.get_help())
def __init__(self): profile = Profile() resources = Resources() self.name = 'system' self.version = util.get_package_version(self.name, profile) self.spec_version = util.get_package_spec_version(self.name, resources) self.ext = '' if 'Windows' == platform.system(): self.ext = '.exe'
def cli(ctx, packages, all, list, platform): """Uninstall packages.""" if packages: _uninstall(packages, platform) elif all: # pragma: no cover packages = Profile().packages _uninstall(packages, platform) elif list: Resources(platform).list_packages(installed=True, notinstalled=False) else: click.secho(ctx.get_help())
def list_packages(self, installed=True, notinstalled=True): """Return a list with all the installed/notinstalled packages""" self.profile = Profile() # Classify packages installed_packages, notinstalled_packages = self.get_packages() # Print tables terminal_width, _ = click.get_terminal_size() if installed and installed_packages: # - Print installed packages table click.echo('\nInstalled packages:\n') PACKAGELIST_TPL = ('{name:20} {description:30} {version:<8}') click.echo('-' * terminal_width) click.echo( PACKAGELIST_TPL.format(name=click.style('Name', fg='cyan'), version='Version', description='Description')) click.echo('-' * terminal_width) for package in installed_packages: click.echo( PACKAGELIST_TPL.format( name=click.style(package.get('name'), fg='cyan'), version=package.get('version'), description=package.get('description'))) if notinstalled and notinstalled_packages: # - Print not installed packages table click.echo('\nNot installed packages:\n') PACKAGELIST_TPL = ('{name:20} {description:30}') click.echo('-' * terminal_width) click.echo( PACKAGELIST_TPL.format(name=click.style('Name', fg='yellow'), description='Description')) click.echo('-' * terminal_width) for package in notinstalled_packages: click.echo( PACKAGELIST_TPL.format( name=click.style(package.get('name'), fg='yellow'), description=package.get('description')))
class Drivers(object): # pragma: no cover rules_local_path = util.safe_join(util.get_folder('resources'), '80-icestick.rules') rules_system_path = '/etc/udev/rules.d/80-icestick.rules' # Driver to restore: mac os driverC = '' def enable(self): if 'linux' in platform: return self._enable_linux() elif 'darwin' in platform: self.profile = Profile() return self._enable_darwin() elif 'windows' in platform: return self._enable_windows() def disable(self): if 'linux' in platform: return self._disable_linux() elif 'darwin' in platform: self.profile = Profile() return self._disable_darwin() elif 'windows' in platform: return self._disable_windows() def pre_upload(self): if 'darwin' in platform: self._pre_upload_darwin() def post_upload(self): if 'darwin' in platform: self._post_upload_darwin() def _enable_linux(self): click.secho('Configure FTDI drivers for FPGA') if not isfile(self.rules_system_path): subprocess.call( ['sudo', 'cp', self.rules_local_path, self.rules_system_path]) subprocess.call(['sudo', 'service', 'udev', 'restart']) # subprocess.call(['sudo', 'udevadm', 'control', '--reload-rules']) # subprocess.call(['sudo', 'udevadm', 'trigger']) click.secho('FPGA drivers enabled', fg='green') click.secho('Unplug and reconnect your board', fg='yellow') else: click.secho('Already enabled', fg='yellow') def _disable_linux(self): if isfile(self.rules_system_path): click.secho('Revert FTDI drivers\' configuration') subprocess.call(['sudo', 'rm', self.rules_system_path]) subprocess.call(['sudo', 'service', 'udev', 'restart']) # subprocess.call(['sudo', 'udevadm', 'control', '--reload-rules']) # subprocess.call(['sudo', 'udevadm', 'trigger']) click.secho('FPGA drivers disabled', fg='green') click.secho('Unplug and reconnect your board', fg='yellow') else: click.secho('Already disabled', fg='yellow') def _enable_darwin(self): # Check homebrew brew = subprocess.call('which brew > /dev/null', shell=True) if brew != 0: click.secho('Error: homebrew is required', fg='red') else: click.secho('Enable FTDI drivers for FPGA') subprocess.call(['brew', 'update']) subprocess.call(['brew', 'install', '--force', 'libftdi']) subprocess.call(['brew', 'unlink', 'libftdi']) subprocess.call(['brew', 'link', '--force', 'libftdi']) subprocess.call(['brew', 'install', '--force', 'libffi']) subprocess.call(['brew', 'unlink', 'libffi']) subprocess.call(['brew', 'link', '--force', 'libffi']) self.profile.add_setting('macos_drivers', True) self.profile.save() click.secho('FPGA drivers enabled', fg='green') def _disable_darwin(self): click.secho('Disable FTDI drivers\' configuration') self.profile.add_setting('macos_drivers', False) self.profile.save() click.secho('FPGA drivers disabled', fg='green') def _pre_upload_darwin(self): if self.profile.settings.get('macos_drivers', False): # Check and unload the drivers driverA = 'com.FTDI.driver.FTDIUSBSerialDriver' driverB = 'com.apple.driver.AppleUSBFTDI' if self._check_driver_darwin(driverA): subprocess.call(['sudo', 'kextunload', '-b', driverA]) self.driverC = driverA elif self._check_driver_darwin(driverB): subprocess.call(['sudo', 'kextunload', '-b', driverB]) self.driverC = driverB def _post_upload_darwin(self): if self.profile.settings.get('macos_drivers', False): # Restore previous driver configuration if self.driverC: subprocess.call(['sudo', 'kextload', '-b', self.driverC]) def _check_driver_darwin(self, driver): return driver in subprocess.check_output(['kextstat']) def _enable_windows(self): drivers_base_dir = util.get_package_dir('tools-drivers') drivers_bin_dir = util.safe_join(drivers_base_dir, 'bin') drivers_share_dir = util.safe_join(drivers_base_dir, 'share') zadig_ini_path = util.safe_join(drivers_share_dir, 'zadig.ini') zadig_ini = 'zadig.ini' try: if isdir(drivers_bin_dir): click.secho('Launch drivers configuration tool') click.secho(FTDI_INSTALL_DRIVER_INSTRUCTIONS, fg='yellow') # Copy zadig.ini with open(zadig_ini, 'w') as ini_file: with open(zadig_ini_path, 'r') as local_ini_file: ini_file.write(local_ini_file.read()) result = util.exec_command( util.safe_join(drivers_bin_dir, 'zadig.exe')) click.secho('FPGA drivers configuration finished', fg='green') else: util._check_package('drivers') result = 1 except Exception as e: click.secho('Error: ' + str(e), fg='red') result = 1 finally: # Remove zadig.ini if isfile(zadig_ini): os.remove(zadig_ini) if not isinstance(result, int): result = result['returncode'] return result def _disable_windows(self): click.secho('Launch device manager') click.secho(FTDI_UNINSTALL_DRIVER_INSTRUCTIONS, fg='yellow') result = util.exec_command('mmc devmgmt.msc') return result['returncode']
class SCons(object): def __init__(self): self.resources = Resources() self.profile = Profile() def clean(self): return self.run('-c', deps=['scons']) def verify(self): return self.run('verify', deps=['scons', 'iverilog']) def sim(self): return self.run('sim', deps=['scons', 'iverilog', 'gtkwave']) def build(self, args): ret = self.process_arguments(args) if isinstance(ret, int): return ret if isinstance(ret, tuple): variables, board = ret return self.run('build', variables, board, deps=['scons', 'icestorm']) def upload(self, args, device=-1): ret = self.process_arguments(args) if isinstance(ret, int): return ret if isinstance(ret, tuple): variables, board = ret # Get programmer value programmer = '' if board: p = self.resources.boards[board]['programmer'] type = p['type'] content = self.resources.programmers[type] extra_args = p['extra_args'] if 'extra_args' in p else '' command = content['command'] if 'command' in content else '' args = content['args'] if 'args' in content else '' programmer = '{0} {1} {2}'.format(command, args, extra_args) # -- Check check = self.resources.boards[board]['check'] # Check FTDI description if 'ftdi-desc' in check: detected_boards = System().detect_boards() if isinstance(detected_boards, int): return detected_boards if device: # Check device argument if board: desc = check['ftdi-desc'] found = False for b in detected_boards: # Selected board if device == b['index']: # Check the device ftdi description if desc in b['description']: found = True break if not found: device = -1 else: # Check device id if int(device) >= len(detected_boards): device = -1 else: # Detect device device = -1 if board: desc = check['ftdi-desc'] for b in detected_boards: if desc in b['description']: # Select the first board that validates # the ftdi description device = b['index'] break else: # Insufficient arguments click.secho( 'Error: insufficient arguments: device or board', fg='red') click.secho( 'You have two options:\n' + ' 1) Execute your command with\n' + ' `--device <deviceid>`\n' + ' 2) Execute your command with\n' + ' `--board <boardname>`', fg='yellow') return 1 if device == -1: # Board not detected click.secho('Error: board not detected', fg='red') return 1 # Check platforms if 'platform' in check: # Device argument is ignored if device and device != -1: click.secho( 'Info: ignore device argument {0}'.format(device), fg='yellow') platform = check['platform'] current_platform = util.get_systype() if platform != current_platform: # Incorrect platform if platform == 'linux_armv7l': click.secho( 'Error: incorrect platform: RPI2 or RPI3 required', fg='red') else: click.secho( 'Error: incorrect platform {0}'.format(platform), fg='red') return 1 return self.run('upload', variables + ['device={0}'.format(device), 'prog={0}'.format(programmer)], board, deps=['scons', 'icestorm']) def time(self, args): ret = self.process_arguments(args) if isinstance(ret, int): return ret if isinstance(ret, tuple): variables, board = ret return self.run('time', variables, board, deps=['scons', 'icestorm']) def run(self, command, variables=[], board=None, deps=[]): """Executes scons for building""" # -- Check for the SConstruct file if not isfile(join(util.get_project_dir(), 'SConstruct')): click.secho('Using default SConstruct file') variables += ['-f', join( dirname(__file__), '..', 'resources', 'SConstruct')] # -- Resolve packages if self.profile.check_exe_default(): # Run on `default` config mode if not util.resolve_packages(self.resources.packages, deps): # Exit if a package is not installed return 1 # -- Execute scons terminal_width, _ = click.get_terminal_size() start_time = time.time() if command == 'build' or \ command == 'upload' or \ command == 'time': if board: processing_board = board else: processing_board = 'custom board' click.echo('[%s] Processing %s' % ( datetime.datetime.now().strftime('%c'), click.style(processing_board, fg='cyan', bold=True))) click.secho('-' * terminal_width, bold=True) if self.profile.get_verbose_mode() > 0: click.secho('Executing: scons -Q {0} {1}'.format( command, ' '.join(variables))) result = util.exec_command( util.scons_command + ['-Q', command] + variables, stdout=util.AsyncPipe(self._on_run_out), stderr=util.AsyncPipe(self._on_run_err) ) # -- Print result exit_code = result['returncode'] is_error = exit_code != 0 summary_text = ' Took %.2f seconds ' % (time.time() - start_time) half_line = '=' * int( ((terminal_width - len(summary_text) - 10) / 2)) click.echo('%s [%s]%s%s' % ( half_line, (click.style(' ERROR ', fg='red', bold=True) if is_error else click.style('SUCCESS', fg='green', bold=True)), summary_text, half_line ), err=is_error) if False: if is_error: print(""" ______ _ | ____| | | | |__ _ __ _ __ ___ _ __| | | __| | '__| '__/ _ \| '__| | | |____| | | | | (_) | | |_| |______|_| |_| \___/|_| (_) """) else: print(""" _____ _ / ____| | | | (___ _ _ ___ ___ ___ ___ ___| | \___ \| | | |/ __/ __/ _ \/ __/ __| | ____) | |_| | (_| (_| __/\__ \__ \_| |_____/ \__,_|\___\___\___||___/___(_) """) return exit_code def process_arguments(self, args): # -- Check arguments var_board = args['board'] var_fpga = args['fpga'] var_size = args['size'] var_type = args['type'] var_pack = args['pack'] # TODO: reduce code size if var_board: if isfile('apio.ini'): click.secho('Info: ignore apio.ini board', fg='yellow') if var_board in self.resources.boards: fpga = self.resources.boards[var_board]['fpga'] if fpga in self.resources.fpgas: fpga_size = self.resources.fpgas[fpga]['size'] fpga_type = self.resources.fpgas[fpga]['type'] fpga_pack = self.resources.fpgas[fpga]['pack'] redundant_arguments = [] contradictory_arguments = [] if var_fpga: if var_fpga in self.resources.fpgas: if var_fpga == fpga: # Redundant argument redundant_arguments += ['fpga'] else: # Contradictory argument contradictory_arguments += ['fpga'] else: # Unknown fpga click.secho( 'Error: unknown fpga: {0}'.format( var_fpga), fg='red') return 1 if var_size: if var_size == fpga_size: # Redundant argument redundant_arguments += ['size'] else: # Contradictory argument contradictory_arguments += ['size'] if var_type: if var_type == fpga_type: # Redundant argument redundant_arguments += ['type'] else: # Contradictory argument contradictory_arguments += ['type'] if var_pack: if var_pack == fpga_pack: # Redundant argument redundant_arguments += ['pack'] else: # Contradictory argument contradictory_arguments += ['pack'] if redundant_arguments: # Redundant argument click.secho( 'Warning: redundant arguments: {}'.format( ', '.join(redundant_arguments)), fg='yellow') if contradictory_arguments: # Contradictory argument click.secho( 'Error: contradictory arguments: {}'.format( ', '.join(contradictory_arguments)), fg='red') return 1 else: # Unknown fpga pass else: # Unknown board click.secho( 'Error: unknown board: {0}'.format(var_board), fg='red') return 1 else: if var_fpga: if isfile('apio.ini'): click.secho('Info: ignore apio.ini board', fg='yellow') if var_fpga in self.resources.fpgas: fpga_size = self.resources.fpgas[var_fpga]['size'] fpga_type = self.resources.fpgas[var_fpga]['type'] fpga_pack = self.resources.fpgas[var_fpga]['pack'] redundant_arguments = [] contradictory_arguments = [] if var_size: if var_size == fpga_size: # Redundant argument redundant_arguments += ['size'] else: # Contradictory argument contradictory_arguments += ['size'] if var_type: if var_type == fpga_type: # Redundant argument redundant_arguments += ['type'] else: # Contradictory argument contradictory_arguments += ['type'] if var_pack: if var_pack == fpga_pack: # Redundant argument redundant_arguments += ['pack'] else: # Contradictory argument contradictory_arguments += ['pack'] if redundant_arguments: # Redundant argument click.secho( 'Warning: redundant arguments: {}'.format( ', '.join(redundant_arguments)), fg='yellow') if contradictory_arguments: # Contradictory argument click.secho( 'Error: contradictory arguments: {}'.format( ', '.join(contradictory_arguments)), fg='red') return 1 else: # Unknown fpga click.secho( 'Error: unknown fpga: {0}'.format(var_fpga), fg='red') return 1 else: if var_size and var_type and var_pack: if isfile('apio.ini'): click.secho('Info: ignore apio.ini board', fg='yellow') fpga_size = var_size fpga_type = var_type fpga_pack = var_pack else: if not var_size and not var_type and not var_pack: # No arguments: use apio.ini board p = Project() p.read() if p.board: var_board = p.board click.secho( 'Info: use apio.ini board: {}'.format( var_board)) fpga = self.resources.boards[var_board]['fpga'] fpga_size = self.resources.fpgas[fpga]['size'] fpga_type = self.resources.fpgas[fpga]['type'] fpga_pack = self.resources.fpgas[fpga]['pack'] else: click.secho( 'Error: insufficient arguments: missing board', fg='red') click.secho( 'You have two options:\n' + ' 1) Execute your command with\n' + ' `--board <boardname>`\n' + ' 2) Create an ini file using\n' + ' `apio init --board <boardname>`', fg='yellow') return 1 else: if isfile('apio.ini'): click.secho('Info: ignore apio.ini board', fg='yellow') # Insufficient arguments missing = [] if not var_size: missing += ['size'] if not var_type: missing += ['type'] if not var_pack: missing += ['pack'] pass click.secho( 'Error: insufficient arguments: missing {}'.format( ', '.join(missing)), fg='red') return 1 # -- Build Scons variables list variables = self.format_vars({ 'fpga_size': fpga_size, 'fpga_type': fpga_type, 'fpga_pack': fpga_pack }) return variables, var_board def format_vars(self, args): """Format the given vars in the form: 'flag=value'""" variables = [] for key, value in args.items(): if value: variables += ['{0}={1}'.format(key, value)] return variables def _on_run_out(self, line): fg = 'green' if 'is up to date' in line else None click.secho(line, fg=fg) def _on_run_err(self, line): time.sleep(0.01) # Delay fg = 'red' if 'error' in line.lower() else 'yellow' click.secho(line, fg=fg)
def __init__(self): self.resources = Resources() self.profile = Profile()
class Installer(object): def __init__(self, package, platform='', force=False): # Parse version if '@' in package: split = package.split('@') self.package = split[0] self.version = split[1] else: self.package = package self.version = None self.forced_install = force self.packages_dir = '' self.resources = Resources(platform) if self.package in self.resources.packages: self.profile = Profile() dirname = 'packages' self.packages_dir = join(get_home_dir(), dirname) # Check version data = self.resources.packages[self.package] distribution = self.resources.distribution self.specversion = distribution['packages'][self.package] version = self._get_valid_version( data['repository']['name'], data['repository']['organization'], data['release']['tag_name'], self.version, self.specversion ) # Valid version added with @ if version and self.version: self.forced_install = True self.version = version if version else '' # Valid version if version: self.platform = platform or self._get_platform() release = data['release'] self.compressed_name = release['compressed_name'].replace( '%V', self.version).replace('%P', self.platform) self.uncompressed_name = release['uncompressed_name'].replace( '%V', self.version).replace('%P', self.platform) self.package_name = data['release']['package_name'] if isinstance(data['release']['extension'], dict): for os in ['linux', 'darwin', 'windows']: if os in self.platform: self.extension = data['release']['extension'][os] else: self.extension = data['release']['extension'] self.tarball = self._get_tarball_name( self.compressed_name, self.extension ) self.download_url = self._get_download_url( data['repository']['name'], data['repository']['organization'], data['release']['tag_name'].replace('%V', self.version), self.tarball ) def install(self): if self.packages_dir == '': click.secho( 'Error: No such package \'{0}\''.format(self.package), fg='red') elif self.version == '': click.secho( 'Error: No valid version found. Semantic {0}'.format( self.specversion), fg='red') else: click.echo('Installing %s package:' % click.style( self.package, fg='cyan')) if not isdir(self.packages_dir): makedirs(self.packages_dir) assert isdir(self.packages_dir) try: dlpath = None dlpath = self._download(self.download_url) if dlpath: package_dir = join(self.packages_dir, self.package) if isdir(package_dir): shutil.rmtree(package_dir) if self.uncompressed_name: self._unpack(dlpath, self.packages_dir) else: self._unpack(dlpath, join( self.packages_dir, self.package_name)) except Exception as e: click.secho('Error: ' + str(e), fg='red') else: if dlpath: remove(dlpath) self.profile.add_package(self.package, self.version) self.profile.save() click.secho( """Package \'{}\' has been """ """successfully installed!""".format(self.package), fg='green') # Rename unpacked dir to package dir if self.uncompressed_name: unpack_dir = join(self.packages_dir, self.uncompressed_name) package_dir = join(self.packages_dir, self.package_name) if isdir(unpack_dir): rename(unpack_dir, package_dir) def uninstall(self): if self.packages_dir == '': click.secho( 'Error: No such package \'{0}\''.format(self.package), fg='red') else: if isdir(join(self.packages_dir, self.package_name)): click.echo('Uninstalling %s package' % click.style( self.package, fg='cyan')) shutil.rmtree(join(self.packages_dir, self.package_name)) click.secho( """Package \'{}\' has been """ """successfully uninstalled!""".format(self.package), fg='green') else: click.secho('Package \'{0}\' is not installed'.format( self.package), fg='red') self.profile.remove_package(self.package) self.profile.save() def _get_platform(self): return get_systype() def _get_download_url(self, name, organization, tag, tarball): url = 'https://github.com/{0}/{1}/releases/download/{2}/{3}'.format( organization, name, tag, tarball) return url def _get_tarball_name(self, name, extension): tarball = '{0}.{1}'.format( name, extension) return tarball def _get_valid_version(self, name, organization, tag_name, version='', specversion=''): # Check spec version try: spec = semantic_version.Spec(specversion) except ValueError: click.secho('Invalid distribution version {0}: {1}'.format( name, specversion), fg='red') exit(1) # Download latest releases list releases = api_request('{}/releases'.format(name), organization) if releases is not None: for release in releases: if 'tag_name' in release: if version: # Version number via @ tag = tag_name.replace('%V', version) if tag == release['tag_name']: return self._check_sem_version(version, spec) else: pattern = tag_name.replace('%V', '(?P<v>.*?)') + '$' match = re.search(pattern, release['tag_name']) if match: version = match.group('v') return self._check_sem_version(version, spec) def _check_sem_version(self, version, spec): try: if semantic_version.Version(version) in spec: return version except ValueError: if version in str(spec): return version def _download(self, url): # Note: here we check only for the version of locally installed # packages. For this reason we don't say what's the installation # path. if self.profile.check_package_version(self.package, self.version) \ or self.forced_install: fd = FileDownloader(url, self.packages_dir) filepath = fd.get_filepath() click.secho('Download ' + basename(filepath)) try: fd.start() except KeyboardInterrupt: if isfile(filepath): remove(filepath) click.secho('Abort download!', fg='red') exit(1) return filepath else: click.secho('Already installed. Version {0}'.format( self.profile.get_package_version(self.package)), fg='yellow') return None def _unpack(self, pkgpath, pkgdir): fu = FileUnpacker(pkgpath, pkgdir) return fu.start()
def __init__(self, package, platform='', force=False, checkversion=True): # Parse version if '@' in package: split = package.split('@') self.package = split[0] self.version = split[1] else: self.package = package self.version = None self.force_install = force self.packages_dir = '' self.resources = Resources(platform) if self.package in self.resources.packages: self.profile = Profile() dirname = 'packages' self.packages_dir = util.safe_join(util.get_home_dir(), dirname) # Get data data = self.resources.packages.get(self.package) distribution = self.resources.distribution self.spec_version = distribution.get('packages').get(self.package) self.package_name = data.get('release').get('package_name') self.extension = data.get('release').get('extension') platform = platform or self._get_platform() if checkversion: # Check version valid_version = self._get_valid_version( data.get('repository').get('name'), data.get('repository').get('organization'), data.get('release').get('tag_name') ) # Valid version if not valid_version: # Error click.secho('Error: no valid version found', fg='red') exit(1) self.version = valid_version # e.g., [linux_x86_64, linux] platform_os = platform.split('_')[0] self.download_urls = [ { 'url': self.get_download_url(data, platform), 'platform': platform }, { 'url': self.get_download_url(data, platform_os), 'platform': platform_os } ] if self.packages_dir == '': click.secho( 'Error: no such package \'{}\''.format(self.package), fg='red') exit(1)
def __init__(self, package, platform='', force=False): # Parse version if '@' in package: split = package.split('@') self.package = split[0] self.version = split[1] else: self.package = package self.version = None self.forced_install = force self.packages_dir = '' self.resources = Resources(platform) if self.package in self.resources.packages: self.profile = Profile() dirname = 'packages' self.packages_dir = join(get_home_dir(), dirname) # Check version data = self.resources.packages[self.package] distribution = self.resources.distribution self.specversion = distribution['packages'][self.package] version = self._get_valid_version( data['repository']['name'], data['repository']['organization'], data['release']['tag_name'], self.version, self.specversion ) # Valid version added with @ if version and self.version: self.forced_install = True self.version = version if version else '' # Valid version if version: self.platform = platform or self._get_platform() release = data['release'] self.compressed_name = release['compressed_name'].replace( '%V', self.version).replace('%P', self.platform) self.uncompressed_name = release['uncompressed_name'].replace( '%V', self.version).replace('%P', self.platform) self.package_name = data['release']['package_name'] if isinstance(data['release']['extension'], dict): for os in ['linux', 'darwin', 'windows']: if os in self.platform: self.extension = data['release']['extension'][os] else: self.extension = data['release']['extension'] self.tarball = self._get_tarball_name( self.compressed_name, self.extension ) self.download_url = self._get_download_url( data['repository']['name'], data['repository']['organization'], data['release']['tag_name'].replace('%V', self.version), self.tarball )
class SCons(object): def __init__(self, project_dir=''): self.profile = Profile() self.resources = Resources() if project_dir is not None: # Move to project dir project_dir = util.check_dir(project_dir) os.chdir(project_dir) @util.command def clean(self): return self.run('-c', packages=['scons']) @util.command def verify(self): return self.run('verify', packages=['scons', 'iverilog']) @util.command def lint(self, args): var = format_vars({ 'all': args.get('all'), 'top': args.get('top'), 'nowarn': args.get('nowarn'), 'warn': args.get('warn'), 'nostyle': args.get('nostyle') }) return self.run('lint', var, packages=['scons', 'verilator']) @util.command def sim(self): return self.run('sim', packages=['scons', 'iverilog', 'gtkwave']) @util.command def build(self, args): var, board = process_arguments(args, self.resources) return self.run('build', var, board, packages=['scons', 'icestorm']) @util.command def time(self, args): var, board = process_arguments(args, self.resources) return self.run('time', var, board, packages=['scons', 'icestorm']) @util.command def upload(self, args, serial_port, ftdi_id, sram): var, board = process_arguments(args, self.resources) programmer = self.get_programmer(board, serial_port, ftdi_id, sram) var += ['prog={0}'.format(programmer)] return self.run('upload', var, board, packages=['scons', 'icestorm']) def get_programmer(self, board, ext_serial, ext_ftdi_id, sram): programmer = '' if board: board_data = self.resources.boards.get(board) # Check platform self.check_platform(board_data) # Check pip packages self.check_pip_packages(board_data) # Serialize programmer command programmer = self.serialize_programmer(board_data, sram) # Replace USB vendor id if '${VID}' in programmer: vid = board_data.get('usb').get('vid') programmer = programmer.replace('${VID}', vid) # Replace USB product id if '${PID}' in programmer: pid = board_data.get('usb').get('pid') programmer = programmer.replace('${PID}', pid) # Replace FTDI index if '${FTDI_ID}' in programmer: self.check_usb(board, board_data) ftdi_id = self.get_ftdi_id(board, board_data, ext_ftdi_id) programmer = programmer.replace('${FTDI_ID}', ftdi_id) # TinyFPGA BX board is not detected in MacOS HighSierra if 'tinyprog' in board_data and 'darwin' in util.get_systype(): # In this case the serial check is ignored return 'tinyprog --libusb --program' # Replace Serial port if '${SERIAL_PORT}' in programmer: self.check_usb(board, board_data) device = self.get_serial_port(board, board_data, ext_serial) programmer = programmer.replace('${SERIAL_PORT}', device) return programmer def check_platform(self, board_data): if 'platform' not in board_data: return platform = board_data.get('platform') current_platform = util.get_systype() if platform != current_platform: # Incorrect platform if platform == 'linux_armv7l': raise Exception('incorrect platform: RPI2 or RPI3 required') else: raise Exception('incorrect platform {0}'.format(platform)) def check_pip_packages(self, board_data): prog_info = board_data.get('programmer') content = self.resources.programmers.get(prog_info.get('type')) all_pip_packages = self.resources.distribution.get('pip_packages') pip_packages = content.get('pip_packages') or [] for pip_pkg in pip_packages: try: # Check pip_package version spec = semantic_version.Spec(all_pip_packages.get(pip_pkg, '')) pkg_version = pkg_resources.get_distribution(pip_pkg).version version = semantic_version.Version(pkg_version) if not spec.match(version): click.secho('Error: \'{}\' '.format(pip_pkg) + 'version ({}) '.format(version) + 'does not match {}'.format(spec), fg='red') click.secho('Please run:\n' ' pip install -U apio[{}]'.format(pip_pkg), fg='yellow') raise Exception except pkg_resources.DistributionNotFound: click.secho('Error: \'{}\' is not installed'.format(pip_pkg), fg='red') click.secho('Please run:\n' ' pip install -U apio[{}]'.format(pip_pkg), fg='yellow') raise Exception try: # Check pip_package itself __import__(pip_pkg) except Exception as e: # Exit if a package is not working python_version = util.get_python_version() message = '\'{}\' not compatible with '.format(pip_pkg) message += 'Python {}'.format(python_version) message += '\n {}'.format(e) raise Exception(message) def serialize_programmer(self, board_data, sram): prog_info = board_data.get('programmer') content = self.resources.programmers.get(prog_info.get('type')) programmer = content.get('command') # Add args if content.get('args'): programmer += ' {}'.format(content.get('args')) # Add extra args if prog_info.get('extra_args'): programmer += ' {}'.format(prog_info.get('extra_args')) # Enable SRAM programming if sram: # Only for iceprog programmer if programmer.startswith('iceprog'): programmer += ' -S' return programmer def check_usb(self, board, board_data): if 'usb' not in board_data: raise Exception('Missing board configuration: usb') usb_data = board_data.get('usb') hwid = '{0}:{1}'.format(usb_data.get('vid'), usb_data.get('pid')) found = False for usb_device in System().get_usb_devices(): if usb_device.get('hwid') == hwid: found = True break if not found: # Board not connected if 'tinyprog' in board_data: click.secho('Activate bootloader by pressing the reset button', fg='yellow') raise Exception('board ' + board + ' not connected') def get_serial_port(self, board, board_data, ext_serial_port): # Search Serial port by USB id device = self._check_serial(board, board_data, ext_serial_port) if device is None: # Board not connected raise Exception('board ' + board + ' not connected') return device def _check_serial(self, board, board_data, ext_serial_port): if 'usb' not in board_data: raise Exception('Missing board configuration: usb') usb_data = board_data.get('usb') hwid = '{0}:{1}'.format(usb_data.get('vid'), usb_data.get('pid')) # Match the discovered serial ports serial_ports = util.get_serial_ports() if len(serial_ports) == 0: # Board not available raise Exception('board ' + board + ' not available') for serial_port_data in serial_ports: port = serial_port_data.get('port') if ext_serial_port and ext_serial_port != port: # If the --device options is set but it doesn't match # the detected port, skip the port. continue if hwid.lower() in serial_port_data.get('hwid').lower(): if 'tinyprog' in board_data and \ not self._check_tinyprog(board_data, port): # If the board uses tinyprog use its port detection # to double check the detected port. # If the port is not detected, skip the port. continue # If the hwid and the description pattern matches # with the detected port return the port. return port def _check_tinyprog(self, board_data, port): desc_pattern = '^' + board_data.get('tinyprog').get('desc') + '$' for tinyprog_meta in util.get_tinyprog_meta(): tinyprog_port = tinyprog_meta.get('port') tinyprog_name = tinyprog_meta.get('boardmeta').get('name') if port == tinyprog_port and re.match(desc_pattern, tinyprog_name): # If the port is detected and it matches the pattern return True def get_ftdi_id(self, board, board_data, ext_ftdi_id): # Search device by FTDI id ftdi_id = self._check_ftdi(board, board_data, ext_ftdi_id) if ftdi_id is None: # Board not connected raise Exception('board ' + board + ' not connected') return ftdi_id def _check_ftdi(self, board, board_data, ext_ftdi_id): if 'ftdi' not in board_data: raise Exception('Missing board configuration: ftdi') desc_pattern = '^' + board_data.get('ftdi').get('desc') + '$' # Match the discovered FTDI chips ftdi_devices = System().get_ftdi_devices() if len(ftdi_devices) == 0: # Board not available raise Exception('board ' + board + ' not available') for ftdi_device in ftdi_devices: index = ftdi_device.get('index') if ext_ftdi_id and ext_ftdi_id != index: # If the --device options is set but it doesn't match # with the detected index, skip the port. continue if re.match(desc_pattern, ftdi_device.get('description')): # If matches the description pattern # return the index for the FTDI device. return index def run(self, command, variables=[], board=None, packages=[]): """Executes scons for building""" # -- Check for the SConstruct file if not isfile(util.safe_join(util.get_project_dir(), 'SConstruct')): variables += ['-f'] variables += [ util.safe_join(util.get_folder('resources'), 'SConstruct') ] else: click.secho('Info: use custom SConstruct file') # -- Resolve packages if self.profile.check_exe_default(): # Run on `default` config mode if not util.resolve_packages( packages, self.profile.packages, self.resources.distribution.get('packages')): # Exit if a package is not installed raise Exception else: click.secho('Info: native config mode') # -- Execute scons return self._execute_scons(command, variables, board) def _execute_scons(self, command, variables, board): terminal_width, _ = click.get_terminal_size() start_time = time.time() if command == 'build' or \ command == 'upload' or \ command == 'time': if board: processing_board = board else: processing_board = 'custom board' click.echo('[%s] Processing %s' % (datetime.datetime.now().strftime('%c'), click.style(processing_board, fg='cyan', bold=True))) click.secho('-' * terminal_width, bold=True) if self.profile.get_verbose_mode() > 0: click.secho('Executing: {}'.format( ' '.join(util.scons_command + ['-Q', command] + variables))) result = util.exec_command(util.scons_command + ['-Q', command] + variables, stdout=util.AsyncPipe(self._on_stdout), stderr=util.AsyncPipe(self._on_stderr)) # -- Print result exit_code = result.get('returncode') is_error = exit_code != 0 summary_text = ' Took %.2f seconds ' % (time.time() - start_time) half_line = '=' * int(((terminal_width - len(summary_text) - 10) / 2)) click.echo('%s [%s]%s%s' % (half_line, (click.style(' ERROR ', fg='red', bold=True) if is_error else click.style('SUCCESS', fg='green', bold=True)), summary_text, half_line), err=is_error) return exit_code def _on_stdout(self, line): fg = 'green' if 'is up to date' in line else None click.secho(line, fg=fg) def _on_stderr(self, line): if '%|' in line and '100%|' not in line: # Remove previous line for tqdm progress bar CURSOR_UP = '\033[F' ERASE_LINE = '\033[K' sys.stdout.write(CURSOR_UP + ERASE_LINE) fg = 'red' if 'error' in line.lower() else 'yellow' click.secho(line, fg=fg)
class Installer(object): def __init__(self, package): # Parse version if '@' in package: split = package.split('@') self.package = split[0] self.version = split[1] else: self.package = package self.version = None self.forced_install = False self.valid_version = True self.resources = Resources() self.profile = Profile() if self.package in self.resources.packages: data = self.resources.packages[self.package] if self.version: # Validate version valid = self._validate_version( data['repository']['name'], data['repository']['organization'], data['release']['tag_name'], self.version ) if valid: self.forced_install = True else: self.valid_version = False else: # Get latest version self.version = self._get_latest_version( data['repository']['name'], data['repository']['organization'], data['release']['tag_name'] ) self.arch = self._get_architecture() release = data['release'] self.compressed_name = release['compressed_name'].replace( '%V', self.version).replace('%A', self.arch) self.uncompressed_name = release['uncompressed_name'].replace( '%V', self.version).replace('%A', self.arch) self.package_name = data['release']['package_name'] if isinstance(data['release']['extension'], dict): for os in ['linux', 'darwin', 'windows']: if os in self.arch: self.extension = data['release']['extension'][os] else: self.extension = data['release']['extension'] self.tarball = self._get_tarball_name( self.compressed_name, self.extension ) self.download_url = self._get_download_url( data['repository']['name'], data['repository']['organization'], data['release']['tag_name'].replace('%V', self.version), self.tarball ) if 'main_dir' in data.keys(): self.packages_dir = join(expanduser('~'), data['main_dir']) else: self.packages_dir = join(util.get_home_dir(), 'packages') def install(self): if self.version is None: click.secho( 'Error: No such package \'{0}\''.format(self.package), fg='red') elif not self.valid_version: click.secho( 'Error: package \'{0}\' has no version {1}'.format( self.package, self.version), fg='red') else: click.echo("Installing %s package:" % click.style( self.package, fg="cyan")) if not isdir(self.packages_dir): makedirs(self.packages_dir) assert isdir(self.packages_dir) try: dlpath = None dlpath = self._download(self.download_url) if dlpath: package_dir = join(self.packages_dir, self.package) if isdir(package_dir): shutil.rmtree(package_dir) self._unpack(dlpath, self.packages_dir) except Exception: click.secho('Package {0} not found'.format( self.tarball), fg='red') else: if dlpath: remove(dlpath) self.profile.add(self.package, self.version) self.profile.save() click.secho( """Package \'{}\' has been """ """successfully installed!""".format(self.package), fg='green') # Rename unpacked dir to package dir if self.uncompressed_name: unpack_dir = join(self.packages_dir, self.uncompressed_name) package_dir = join(self.packages_dir, self.package_name) if isdir(unpack_dir): rename(unpack_dir, package_dir) def uninstall(self): if self.version is None: click.secho( 'Error: No such package \'{0}\''.format(self.package), fg='red') else: if isdir(join(self.packages_dir, self.package_name)): click.echo("Uninstalling %s package" % click.style( self.package, fg="cyan")) shutil.rmtree(join(self.packages_dir, self.package_name)) click.secho( """Package \'{}\' has been """ """successfully uninstalled!""".format(self.package), fg='green') else: click.secho('Package \'{0}\' is not installed'.format( self.package), fg='red') self.profile.remove(self.package) self.profile.save() def _get_architecture(self): return util.get_systype() def _get_download_url(self, name, organization, tag, tarball): url = 'https://github.com/{0}/{1}/releases/download/{2}/{3}'.format( organization, name, tag, tarball) return url def _get_tarball_name(self, name, extension): tarball = '{0}.{1}'.format( name, extension) return tarball def _validate_version(self, name, organization, tag_name, version): releases = api_request('{}/releases'.format(name), organization) if releases is not None: for release in releases: if 'tag_name' in release and \ release['tag_name'] == tag_name.replace('%V', version): return True return False def _get_latest_version(self, name, organization, tag_name): version = '' latest_release = api_request( '{}/releases/latest'.format(name), organization) if latest_release is not None and 'tag_name' in latest_release: pattern = tag_name.replace('%V', '(?P<v>.*?)') + '$' match = re.search(pattern, latest_release['tag_name']) if match: version = match.group('v') return version def _download(self, url): if self.profile.check_version(self.package, self.version) or \ self.forced_install: fd = FileDownloader(url, self.packages_dir) click.secho('Download ' + basename(fd.get_filepath())) fd.start() return fd.get_filepath() else: click.secho('Already installed. Version {0}'.format( self.profile.get_version(self.package)), fg='yellow') return None def _unpack(self, pkgpath, pkgdir): fu = FileUnpacker(pkgpath, pkgdir) return fu.start()
class SCons(object): def __init__(self, project_dir=''): self.profile = Profile() self.resources = Resources() if project_dir is not None: # Move to project dir project_dir = util.check_dir(project_dir) os.chdir(project_dir) @util.command def clean(self): return self.run('-c', packages=['scons']) @util.command def verify(self): return self.run('verify', packages=['scons', 'iverilog']) @util.command def lint(self, args): var = format_vars({ 'all': args.get('all'), 'top': args.get('top'), 'nowarn': args.get('nowarn'), 'warn': args.get('warn'), 'nostyle': args.get('nostyle') }) return self.run('lint', var, packages=['scons', 'verilator']) @util.command def sim(self): return self.run('sim', packages=['scons', 'iverilog', 'gtkwave']) @util.command def build(self, args): var, board = process_arguments(args, self.resources) return self.run('build', var, board, packages=['scons', 'icestorm']) @util.command def time(self, args): var, board = process_arguments(args, self.resources) return self.run('time', var, board, packages=['scons', 'icestorm']) @util.command def upload(self, args, serial_port, ftdi_id, sram): var, board = process_arguments(args, self.resources) programmer = self.get_programmer(board, serial_port, ftdi_id, sram) var += ['prog={0}'.format(programmer)] return self.run('upload', var, board, packages=['scons', 'icestorm']) def get_programmer(self, board, ext_serial, ext_ftdi_id, sram): programmer = '' if board: board_data = self.resources.boards.get(board) # Check platform self.check_platform(board_data) # Check pip packages self.check_pip_packages(board_data) # Serialize programmer command programmer = self.serialize_programmer(board_data, sram) # Replace USB vendor id if '${VID}' in programmer: vid = board_data.get('usb').get('vid') programmer = programmer.replace('${VID}', vid) # Replace USB product id if '${PID}' in programmer: pid = board_data.get('usb').get('pid') programmer = programmer.replace('${PID}', pid) # Replace FTDI index if '${FTDI_ID}' in programmer: self.check_usb(board, board_data) ftdi_id = self.get_ftdi_id(board, board_data, ext_ftdi_id) programmer = programmer.replace('${FTDI_ID}', ftdi_id) # TinyFPGA BX board is not detected in MacOS HighSierra if 'tinyprog' in board_data and 'darwin' in util.get_systype(): # In this case the serial check is ignored return 'tinyprog --libusb --program' # Replace Serial port if '${SERIAL_PORT}' in programmer: self.check_usb(board, board_data) device = self.get_serial_port(board, board_data, ext_serial) programmer = programmer.replace('${SERIAL_PORT}', device) return programmer def check_platform(self, board_data): if 'platform' not in board_data: return platform = board_data.get('platform') current_platform = util.get_systype() if platform != current_platform: # Incorrect platform if platform == 'linux_armv7l': raise Exception( 'incorrect platform: RPI2 or RPI3 required') else: raise Exception( 'incorrect platform {0}'.format(platform)) def check_pip_packages(self, board_data): prog_info = board_data.get('programmer') content = self.resources.programmers.get(prog_info.get('type')) all_pip_packages = self.resources.distribution.get('pip_packages') pip_packages = content.get('pip_packages') or [] for pip_pkg in pip_packages: try: # Check pip_package version spec = semantic_version.Spec(all_pip_packages.get(pip_pkg, '')) pkg_version = pkg_resources.get_distribution(pip_pkg).version version = semantic_version.Version(pkg_version) if not spec.match(version): click.secho( 'Error: \'{}\' '.format(pip_pkg) + 'version ({}) '.format(version) + 'does not match {}'.format(spec), fg='red') click.secho('Please run:\n' ' pip install -U apio[{}]'.format(pip_pkg), fg='yellow') raise Exception except pkg_resources.DistributionNotFound: click.secho( 'Error: \'{}\' is not installed'.format(pip_pkg), fg='red') click.secho('Please run:\n' ' pip install -U apio[{}]'.format(pip_pkg), fg='yellow') raise Exception try: # Check pip_package itself __import__(pip_pkg) except Exception as e: # Exit if a package is not working python_version = util.get_python_version() message = '\'{}\' not compatible with '.format(pip_pkg) message += 'Python {}'.format(python_version) message += '\n {}'.format(e) raise Exception(message) def serialize_programmer(self, board_data, sram): prog_info = board_data.get('programmer') content = self.resources.programmers.get(prog_info.get('type')) programmer = content.get('command') # Add args if content.get('args'): programmer += ' {}'.format(content.get('args')) # Add extra args if prog_info.get('extra_args'): programmer += ' {}'.format(prog_info.get('extra_args')) # Enable SRAM programming if sram: # Only for iceprog programmer if programmer.startswith('iceprog'): programmer += ' -S' return programmer def check_usb(self, board, board_data): if 'usb' not in board_data: raise Exception('Missing board configuration: usb') usb_data = board_data.get('usb') hwid = '{0}:{1}'.format( usb_data.get('vid'), usb_data.get('pid') ) found = False for usb_device in System().get_usb_devices(): if usb_device.get('hwid') == hwid: found = True break if not found: # Board not connected if 'tinyprog' in board_data: click.secho( 'Activate bootloader by pressing the reset button', fg='yellow') raise Exception('board ' + board + ' not connected') def get_serial_port(self, board, board_data, ext_serial_port): # Search Serial port by USB id device = self._check_serial(board, board_data, ext_serial_port) if device is None: # Board not connected raise Exception('board ' + board + ' not connected') return device def _check_serial(self, board, board_data, ext_serial_port): if 'usb' not in board_data: raise Exception('Missing board configuration: usb') usb_data = board_data.get('usb') hwid = '{0}:{1}'.format( usb_data.get('vid'), usb_data.get('pid') ) # Match the discovered serial ports serial_ports = util.get_serial_ports() if len(serial_ports) == 0: # Board not available raise Exception('board ' + board + ' not available') for serial_port_data in serial_ports: port = serial_port_data.get('port') if ext_serial_port and ext_serial_port != port: # If the --device options is set but it doesn't match # the detected port, skip the port. continue if hwid.lower() in serial_port_data.get('hwid').lower(): if 'tinyprog' in board_data and \ not self._check_tinyprog(board_data, port): # If the board uses tinyprog use its port detection # to double check the detected port. # If the port is not detected, skip the port. continue # If the hwid and the description pattern matches # with the detected port return the port. return port def _check_tinyprog(self, board_data, port): desc_pattern = '^' + board_data.get('tinyprog').get('desc') + '$' for tinyprog_meta in util.get_tinyprog_meta(): tinyprog_port = tinyprog_meta.get('port') tinyprog_name = tinyprog_meta.get('boardmeta').get('name') if port == tinyprog_port and re.match(desc_pattern, tinyprog_name): # If the port is detected and it matches the pattern return True def get_ftdi_id(self, board, board_data, ext_ftdi_id): # Search device by FTDI id ftdi_id = self._check_ftdi(board, board_data, ext_ftdi_id) if ftdi_id is None: # Board not connected raise Exception('board ' + board + ' not connected') return ftdi_id def _check_ftdi(self, board, board_data, ext_ftdi_id): if 'ftdi' not in board_data: raise Exception('Missing board configuration: ftdi') desc_pattern = '^' + board_data.get('ftdi').get('desc') + '$' # Match the discovered FTDI chips ftdi_devices = System().get_ftdi_devices() if len(ftdi_devices) == 0: # Board not available raise Exception('board ' + board + ' not available') for ftdi_device in ftdi_devices: index = ftdi_device.get('index') if ext_ftdi_id and ext_ftdi_id != index: # If the --device options is set but it doesn't match # with the detected index, skip the port. continue if re.match(desc_pattern, ftdi_device.get('description')): # If matches the description pattern # return the index for the FTDI device. return index def run(self, command, variables=[], board=None, packages=[]): """Executes scons for building""" # -- Check for the SConstruct file if not isfile(util.safe_join(util.get_project_dir(), 'SConstruct')): variables += ['-f'] variables += [util.safe_join( util.get_folder('resources'), 'SConstruct')] else: click.secho('Info: use custom SConstruct file') # -- Resolve packages if self.profile.check_exe_default(): # Run on `default` config mode if not util.resolve_packages( packages, self.profile.packages, self.resources.distribution.get('packages') ): # Exit if a package is not installed raise Exception else: click.secho('Info: native config mode') # -- Execute scons return self._execute_scons(command, variables, board) def _execute_scons(self, command, variables, board): terminal_width, _ = click.get_terminal_size() start_time = time.time() if command == 'build' or \ command == 'upload' or \ command == 'time': if board: processing_board = board else: processing_board = 'custom board' click.echo('[%s] Processing %s' % ( datetime.datetime.now().strftime('%c'), click.style(processing_board, fg='cyan', bold=True))) click.secho('-' * terminal_width, bold=True) if self.profile.get_verbose_mode() > 0: click.secho('Executing: {}'.format( ' '.join(util.scons_command + ['-Q', command] + variables))) result = util.exec_command( util.scons_command + ['-Q', command] + variables, stdout=util.AsyncPipe(self._on_stdout), stderr=util.AsyncPipe(self._on_stderr) ) # -- Print result exit_code = result.get('returncode') is_error = exit_code != 0 summary_text = ' Took %.2f seconds ' % (time.time() - start_time) half_line = '=' * int( ((terminal_width - len(summary_text) - 10) / 2)) click.echo('%s [%s]%s%s' % ( half_line, (click.style(' ERROR ', fg='red', bold=True) if is_error else click.style('SUCCESS', fg='green', bold=True)), summary_text, half_line ), err=is_error) return exit_code def _on_stdout(self, line): fg = 'green' if 'is up to date' in line else None click.secho(line, fg=fg) def _on_stderr(self, line): if '%|' in line and '100%|' not in line: # Remove previous line for tqdm progress bar CURSOR_UP = '\033[F' ERASE_LINE = '\033[K' sys.stdout.write(CURSOR_UP + ERASE_LINE) fg = 'red' if 'error' in line.lower() else 'yellow' click.secho(line, fg=fg)
class SCons(object): def __init__(self, project_dir=''): self.resources = Resources() self.profile = Profile() if project_dir is not None: # Move to project dir project_dir = util.check_dir(project_dir) os.chdir(project_dir) def clean(self): return self.run('-c', deps=['scons']) def verify(self): return self.run('verify', deps=['scons', 'iverilog']) def sim(self): return self.run('sim', deps=['scons', 'iverilog', 'gtkwave']) def build(self, args): ret = self.process_arguments(args) if isinstance(ret, int): return ret if isinstance(ret, tuple): variables, board = ret return self.run('build', variables, board, deps=['scons', 'icestorm']) def upload(self, args, device=-1): ret = self.process_arguments(args) if isinstance(ret, int): return ret if isinstance(ret, tuple): variables, board = ret # Get programmer value programmer = '' if board: p = self.resources.boards[board]['programmer'] type = p['type'] content = self.resources.programmers[type] extra_args = p['extra_args'] if 'extra_args' in p else '' command = content['command'] if 'command' in content else '' args = content['args'] if 'args' in content else '' programmer = '{0} {1} {2}'.format(command, args, extra_args) # -- Check check = self.resources.boards[board]['check'] # Check FTDI description if 'ftdi-desc' in check: detected_boards = System().detect_boards() if isinstance(detected_boards, int): return detected_boards if device: # Check device argument if board: desc = check['ftdi-desc'] found = False for b in detected_boards: # Selected board if device == b['index']: # Check the device ftdi description if desc in b['description']: found = True break if not found: device = -1 else: # Check device id if int(device) >= len(detected_boards): device = -1 else: # Detect device device = -1 if board: desc = check['ftdi-desc'] for b in detected_boards: if desc in b['description']: # Select the first board that validates # the ftdi description device = b['index'] break else: # Insufficient arguments click.secho( 'Error: insufficient arguments: device or board', fg='red') click.secho('You have two options:\n' + ' 1) Execute your command with\n' + ' `--device <deviceid>`\n' + ' 2) Execute your command with\n' + ' `--board <boardname>`', fg='yellow') return 1 if device == -1: # Board not detected click.secho('Error: board not detected', fg='red') return 1 # Check platforms if 'platform' in check: # Device argument is ignored if device and device != -1: click.secho('Info: ignore device argument {0}'.format(device), fg='yellow') platform = check['platform'] current_platform = util.get_systype() if platform != current_platform: # Incorrect platform if platform == 'linux_armv7l': click.secho( 'Error: incorrect platform: RPI2 or RPI3 required', fg='red') else: click.secho( 'Error: incorrect platform {0}'.format(platform), fg='red') return 1 return self.run('upload', variables + ['prog={0}'.format(programmer.replace('%D%', device))], board, deps=['scons', 'icestorm']) def time(self, args): ret = self.process_arguments(args) if isinstance(ret, int): return ret if isinstance(ret, tuple): variables, board = ret return self.run('time', variables, board, deps=['scons', 'icestorm']) def run(self, command, variables=[], board=None, deps=[]): """Executes scons for building""" # -- Check for the SConstruct file if not isfile(util.safe_join(util.get_project_dir(), 'SConstruct')): click.secho('Info: default SConstruct file') variables += ['-f'] variables += [ util.safe_join(util.get_folder('resources'), 'SConstruct') ] # -- Resolve packages if self.profile.check_exe_default(): # Run on `default` config mode if not util.resolve_packages(self.resources.packages, deps): # Exit if a package is not installed return 1 else: click.secho('Info: native config mode') # -- Execute scons terminal_width, _ = click.get_terminal_size() start_time = time.time() if command == 'build' or \ command == 'upload' or \ command == 'time': if board: processing_board = board else: processing_board = 'custom board' click.echo('[%s] Processing %s' % (datetime.datetime.now().strftime('%c'), click.style(processing_board, fg='cyan', bold=True))) click.secho('-' * terminal_width, bold=True) if self.profile.get_verbose_mode() > 0: click.secho('Executing: scons -Q {0} {1}'.format( command, ' '.join(variables))) result = util.exec_command(util.scons_command + ['-Q', command] + variables, stdout=util.AsyncPipe(self._on_run_out), stderr=util.AsyncPipe(self._on_run_err)) # -- Print result exit_code = result['returncode'] is_error = exit_code != 0 summary_text = ' Took %.2f seconds ' % (time.time() - start_time) half_line = '=' * int(((terminal_width - len(summary_text) - 10) / 2)) click.echo('%s [%s]%s%s' % (half_line, (click.style(' ERROR ', fg='red', bold=True) if is_error else click.style('SUCCESS', fg='green', bold=True)), summary_text, half_line), err=is_error) if False: if is_error: print(""" ______ _ | ____| | | | |__ _ __ _ __ ___ _ __| | | __| | '__| '__/ _ \| '__| | | |____| | | | | (_) | | |_| |______|_| |_| \___/|_| (_) """) else: print(""" _____ _ / ____| | | | (___ _ _ ___ ___ ___ ___ ___| | \___ \| | | |/ __/ __/ _ \/ __/ __| | ____) | |_| | (_| (_| __/\__ \__ \_| |_____/ \__,_|\___\___\___||___/___(_) """) return exit_code def process_arguments(self, args): # -- Check arguments var_board = args['board'] var_fpga = args['fpga'] var_size = args['size'] var_type = args['type'] var_pack = args['pack'] # TODO: reduce code size if var_board: if isfile('apio.ini'): click.secho('Info: ignore apio.ini board', fg='yellow') if var_board in self.resources.boards: fpga = self.resources.boards[var_board]['fpga'] if fpga in self.resources.fpgas: fpga_size = self.resources.fpgas[fpga]['size'] fpga_type = self.resources.fpgas[fpga]['type'] fpga_pack = self.resources.fpgas[fpga]['pack'] redundant_arguments = [] contradictory_arguments = [] if var_fpga: if var_fpga in self.resources.fpgas: if var_fpga == fpga: # Redundant argument redundant_arguments += ['fpga'] else: # Contradictory argument contradictory_arguments += ['fpga'] else: # Unknown fpga click.secho( 'Error: unknown fpga: {0}'.format(var_fpga), fg='red') return 1 if var_size: if var_size == fpga_size: # Redundant argument redundant_arguments += ['size'] else: # Contradictory argument contradictory_arguments += ['size'] if var_type: if var_type == fpga_type: # Redundant argument redundant_arguments += ['type'] else: # Contradictory argument contradictory_arguments += ['type'] if var_pack: if var_pack == fpga_pack: # Redundant argument redundant_arguments += ['pack'] else: # Contradictory argument contradictory_arguments += ['pack'] if redundant_arguments: # Redundant argument click.secho('Warning: redundant arguments: {}'.format( ', '.join(redundant_arguments)), fg='yellow') if contradictory_arguments: # Contradictory argument click.secho( 'Error: contradictory arguments: {}'.format( ', '.join(contradictory_arguments)), fg='red') return 1 else: # Unknown fpga pass else: # Unknown board click.secho('Error: unknown board: {0}'.format(var_board), fg='red') return 1 else: if var_fpga: if isfile('apio.ini'): click.secho('Info: ignore apio.ini board', fg='yellow') if var_fpga in self.resources.fpgas: fpga_size = self.resources.fpgas[var_fpga]['size'] fpga_type = self.resources.fpgas[var_fpga]['type'] fpga_pack = self.resources.fpgas[var_fpga]['pack'] redundant_arguments = [] contradictory_arguments = [] if var_size: if var_size == fpga_size: # Redundant argument redundant_arguments += ['size'] else: # Contradictory argument contradictory_arguments += ['size'] if var_type: if var_type == fpga_type: # Redundant argument redundant_arguments += ['type'] else: # Contradictory argument contradictory_arguments += ['type'] if var_pack: if var_pack == fpga_pack: # Redundant argument redundant_arguments += ['pack'] else: # Contradictory argument contradictory_arguments += ['pack'] if redundant_arguments: # Redundant argument click.secho('Warning: redundant arguments: {}'.format( ', '.join(redundant_arguments)), fg='yellow') if contradictory_arguments: # Contradictory argument click.secho( 'Error: contradictory arguments: {}'.format( ', '.join(contradictory_arguments)), fg='red') return 1 else: # Unknown fpga click.secho('Error: unknown fpga: {0}'.format(var_fpga), fg='red') return 1 else: if var_size and var_type and var_pack: if isfile('apio.ini'): click.secho('Info: ignore apio.ini board', fg='yellow') fpga_size = var_size fpga_type = var_type fpga_pack = var_pack else: if not var_size and not var_type and not var_pack: # No arguments: use apio.ini board p = Project() p.read() if p.board: var_board = p.board click.secho( 'Info: apio.ini board {}'.format(var_board)) fpga = self.resources.boards[var_board]['fpga'] fpga_size = self.resources.fpgas[fpga]['size'] fpga_type = self.resources.fpgas[fpga]['type'] fpga_pack = self.resources.fpgas[fpga]['pack'] else: click.secho( 'Error: insufficient arguments: missing board', fg='red') click.secho( 'You have two options:\n' + ' 1) Execute your command with\n' + ' `--board <boardname>`\n' + ' 2) Create an ini file using\n' + ' `apio init --board <boardname>`', fg='yellow') return 1 else: if isfile('apio.ini'): click.secho('Info: ignore apio.ini board', fg='yellow') # Insufficient arguments missing = [] if not var_size: missing += ['size'] if not var_type: missing += ['type'] if not var_pack: missing += ['pack'] pass click.secho( 'Error: insufficient arguments: missing {}'.format( ', '.join(missing)), fg='red') return 1 # -- Build Scons variables list variables = self.format_vars({ 'fpga_size': fpga_size, 'fpga_type': fpga_type, 'fpga_pack': fpga_pack }) return variables, var_board def format_vars(self, args): """Format the given vars in the form: 'flag=value'""" variables = [] for key, value in args.items(): if value: variables += ['{0}={1}'.format(key, value)] return variables def _on_run_out(self, line): fg = 'green' if 'is up to date' in line else None click.secho(line, fg=fg) def _on_run_err(self, line): time.sleep(0.01) # Delay fg = 'red' if 'error' in line.lower() else 'yellow' click.secho(line, fg=fg)
def __init__(self, package, platform='', force=False): # Parse version if '@' in package: split = package.split('@') self.package = split[0] self.version = split[1] else: self.package = package self.version = None self.forced_install = force self.packages_dir = '' self.resources = Resources(platform) if self.package in self.resources.packages: self.profile = Profile() dirname = 'packages' self.packages_dir = join(get_home_dir(), dirname) # Check version data = self.resources.packages[self.package] distribution = self.resources.distribution self.specversion = distribution['packages'][self.package] version = self._get_valid_version( data['repository']['name'], data['repository']['organization'], data['release']['tag_name'], self.version, self.specversion) # Valid version added with @ if version and self.version: self.forced_install = True self.version = version if version else '' # Valid version if version: self.platform = platform or self._get_platform() release = data['release'] self.compressed_name = release['compressed_name'].replace( '%V', self.version).replace('%P', self.platform) self.uncompressed_name = release['uncompressed_name'].replace( '%V', self.version).replace('%P', self.platform) self.package_name = data['release']['package_name'] if isinstance(data['release']['extension'], dict): for os in ['linux', 'darwin', 'windows']: if os in self.platform: self.extension = data['release']['extension'][os] else: self.extension = data['release']['extension'] self.tarball = self._get_tarball_name(self.compressed_name, self.extension) self.download_url = self._get_download_url( data['repository']['name'], data['repository']['organization'], data['release']['tag_name'].replace('%V', self.version), self.tarball)
class Installer(object): def __init__(self, package, platform='', force=False): # Parse version if '@' in package: split = package.split('@') self.package = split[0] self.version = split[1] else: self.package = package self.version = None self.forced_install = force self.packages_dir = '' self.resources = Resources(platform) if self.package in self.resources.packages: self.profile = Profile() dirname = 'packages' self.packages_dir = join(get_home_dir(), dirname) # Check version data = self.resources.packages[self.package] distribution = self.resources.distribution self.specversion = distribution['packages'][self.package] version = self._get_valid_version( data['repository']['name'], data['repository']['organization'], data['release']['tag_name'], self.version, self.specversion) # Valid version added with @ if version and self.version: self.forced_install = True self.version = version if version else '' # Valid version if version: self.platform = platform or self._get_platform() release = data['release'] self.compressed_name = release['compressed_name'].replace( '%V', self.version).replace('%P', self.platform) self.uncompressed_name = release['uncompressed_name'].replace( '%V', self.version).replace('%P', self.platform) self.package_name = data['release']['package_name'] if isinstance(data['release']['extension'], dict): for os in ['linux', 'darwin', 'windows']: if os in self.platform: self.extension = data['release']['extension'][os] else: self.extension = data['release']['extension'] self.tarball = self._get_tarball_name(self.compressed_name, self.extension) self.download_url = self._get_download_url( data['repository']['name'], data['repository']['organization'], data['release']['tag_name'].replace('%V', self.version), self.tarball) def install(self): if self.packages_dir == '': click.secho('Error: No such package \'{0}\''.format(self.package), fg='red') elif self.version == '': click.secho('Error: No valid version found. Semantic {0}'.format( self.specversion), fg='red') else: click.echo('Installing %s package:' % click.style(self.package, fg='cyan')) if not isdir(self.packages_dir): makedirs(self.packages_dir) assert isdir(self.packages_dir) try: dlpath = None dlpath = self._download(self.download_url) if dlpath: package_dir = join(self.packages_dir, self.package) if isdir(package_dir): shutil.rmtree(package_dir) if self.uncompressed_name: self._unpack(dlpath, self.packages_dir) else: self._unpack( dlpath, join(self.packages_dir, self.package_name)) except Exception as e: click.secho('Error: ' + str(e), fg='red') else: if dlpath: remove(dlpath) self.profile.add_package(self.package, self.version) self.profile.save() click.secho("""Package \'{}\' has been """ """successfully installed!""".format( self.package), fg='green') # Rename unpacked dir to package dir if self.uncompressed_name: unpack_dir = join(self.packages_dir, self.uncompressed_name) package_dir = join(self.packages_dir, self.package_name) if isdir(unpack_dir): rename(unpack_dir, package_dir) def uninstall(self): if self.packages_dir == '': click.secho('Error: No such package \'{0}\''.format(self.package), fg='red') else: if isdir(join(self.packages_dir, self.package_name)): click.echo('Uninstalling %s package' % click.style(self.package, fg='cyan')) shutil.rmtree(join(self.packages_dir, self.package_name)) click.secho("""Package \'{}\' has been """ """successfully uninstalled!""".format( self.package), fg='green') else: click.secho('Package \'{0}\' is not installed'.format( self.package), fg='red') self.profile.remove_package(self.package) self.profile.save() def _get_platform(self): return get_systype() def _get_download_url(self, name, organization, tag, tarball): url = 'https://github.com/{0}/{1}/releases/download/{2}/{3}'.format( organization, name, tag, tarball) return url def _get_tarball_name(self, name, extension): tarball = '{0}.{1}'.format(name, extension) return tarball def _get_valid_version(self, name, organization, tag_name, version='', specversion=''): # Check spec version try: spec = semantic_version.Spec(specversion) except ValueError: click.secho('Invalid distribution version {0}: {1}'.format( name, specversion), fg='red') exit(1) # Download latest releases list releases = api_request('{}/releases'.format(name), organization) if releases is not None: for release in releases: if 'tag_name' in release: if version: # Version number via @ tag = tag_name.replace('%V', version) if tag == release['tag_name']: return self._check_sem_version(version, spec) else: pattern = tag_name.replace('%V', '(?P<v>.*?)') + '$' match = re.search(pattern, release['tag_name']) if match: version = match.group('v') return self._check_sem_version(version, spec) def _check_sem_version(self, version, spec): try: if semantic_version.Version(version) in spec: return version except ValueError: if version in str(spec): return version def _download(self, url): # Note: here we check only for the version of locally installed # packages. For this reason we don't say what's the installation # path. if self.profile.check_package_version(self.package, self.version) \ or self.forced_install: fd = FileDownloader(url, self.packages_dir) filepath = fd.get_filepath() click.secho('Download ' + basename(filepath)) try: fd.start() except KeyboardInterrupt: if isfile(filepath): remove(filepath) click.secho('Abort download!', fg='red') exit(1) return filepath else: click.secho('Already installed. Version {0}'.format( self.profile.get_package_version(self.package)), fg='yellow') return None def _unpack(self, pkgpath, pkgdir): fu = FileUnpacker(pkgpath, pkgdir) return fu.start()
class Resources(object): def __init__(self, platform=''): self.packages = self._load_resource('packages') self.boards = self._load_resource('boards') self.fpgas = self._load_resource('fpgas') self.programmers = self._load_resource('programmers') self.distribution = self._load_resource('distribution') # Check available packages self.packages = self._check_packages(self.packages, platform) # Sort resources self.packages = OrderedDict(sorted(self.packages.items(), key=lambda t: t[0])) self.boards = OrderedDict(sorted(self.boards.items(), key=lambda t: t[0])) self.fpgas = OrderedDict(sorted(self.fpgas.items(), key=lambda t: t[0])) def _load_resource(self, name): resource = None filepath = util.safe_join(util.get_folder('resources'), name + '.json') with open(filepath, 'r') as f: # Load the JSON file resource = json.loads(f.read()) return resource def get_package_release_name(self, package): return self.packages.get(package).get('release').get('package_name') def list_packages(self, installed=True, notinstalled=True): """Return a list with all the installed/notinstalled packages""" self.profile = Profile() # Classify packages installed_packages = [] notinstalled_packages = [] for package in self.packages: data = { 'name': package, 'version': None, 'description': self.packages.get(package).get('description') } if package in self.profile.packages: data['version'] = self.profile.get_package_version( package, self.get_package_release_name(package)) installed_packages += [data] else: notinstalled_packages += [data] # Print tables terminal_width, _ = click.get_terminal_size() if installed and installed_packages: # - Print installed packages table click.echo('\nInstalled packages:\n') PACKAGELIST_TPL = ('{name:20} {description:30} {version:<8}') click.echo('-' * terminal_width) click.echo(PACKAGELIST_TPL.format( name=click.style('Name', fg='cyan'), version='Version', description='Description')) click.echo('-' * terminal_width) for package in installed_packages: click.echo(PACKAGELIST_TPL.format( name=click.style(package.get('name'), fg='cyan'), version=package.get('version'), description=package.get('description'))) if notinstalled and notinstalled_packages: # - Print not installed packages table click.echo('\nNot installed packages:\n') PACKAGELIST_TPL = ('{name:20} {description:30}') click.echo('-' * terminal_width) click.echo(PACKAGELIST_TPL.format( name=click.style('Name', fg='yellow'), description='Description')) click.echo('-' * terminal_width) for package in notinstalled_packages: click.echo(PACKAGELIST_TPL.format( name=click.style(package.get('name'), fg='yellow'), description=package.get('description'))) def list_boards(self): """Return a list with all the supported boards""" # Print table click.echo('\nSupported boards:\n') BOARDLIST_TPL = ('{board:25} {fpga:20} {type:<5} {size:<5} {pack:<10}') terminal_width, _ = click.get_terminal_size() click.echo('-' * terminal_width) click.echo(BOARDLIST_TPL.format( board=click.style('Board', fg='cyan'), fpga='FPGA', type='Type', size='Size', pack='Pack')) click.echo('-' * terminal_width) for board in self.boards: fpga = self.boards.get(board).get('fpga') click.echo(BOARDLIST_TPL.format( board=click.style(board, fg='cyan'), fpga=fpga, type=self.fpgas.get(fpga).get('type'), size=self.fpgas.get(fpga).get('size'), pack=self.fpgas.get(fpga).get('pack'))) click.secho(BOARDS_MSG, fg='green') def list_fpgas(self): """Return a list with all the supported FPGAs""" # Print table click.echo('\nSupported FPGAs:\n') FPGALIST_TPL = ('{fpga:30} {type:<5} {size:<5} {pack:<10}') terminal_width, _ = click.get_terminal_size() click.echo('-' * terminal_width) click.echo(FPGALIST_TPL.format( fpga=click.style('FPGA', fg='cyan'), type='Type', size='Size', pack='Pack')) click.echo('-' * terminal_width) for fpga in self.fpgas: click.echo(FPGALIST_TPL.format( fpga=click.style(fpga, fg='cyan'), type=self.fpgas.get(fpga).get('type'), size=self.fpgas.get(fpga).get('size'), pack=self.fpgas.get(fpga).get('pack'))) def _check_packages(self, packages, current_platform=''): filtered_packages = {} for pkg in packages.keys(): check = True release = packages.get(pkg).get('release') if 'available_platforms' in release: platforms = release.get('available_platforms') check = False current_platform = current_platform or util.get_systype() for platform in platforms: check |= current_platform in platform if check: filtered_packages[pkg] = packages.get(pkg) return filtered_packages
class Resources(object): def __init__(self, platform=''): self.packages = self._load_resource('packages') self.boards = self._load_resource('boards') self.fpgas = self._load_resource('fpgas') self.programmers = self._load_resource('programmers') self.distribution = self._load_resource('distribution') # Check available packages self.packages = self._check_packages(self.packages, platform) # Sort resources self.packages = OrderedDict( sorted(self.packages.items(), key=lambda t: t[0])) self.boards = OrderedDict( sorted(self.boards.items(), key=lambda t: t[0])) self.fpgas = OrderedDict(sorted(self.fpgas.items(), key=lambda t: t[0])) def _load_resource(self, name): resource = None filepath = util.safe_join(util.get_folder('resources'), name + '.json') with open(filepath, 'r') as f: # Load the JSON file resource = json.loads(f.read()) return resource def get_package_release_name(self, package): return self.packages.get(package).get('release').get('package_name') def list_packages(self, installed=True, notinstalled=True): """Return a list with all the installed/notinstalled packages""" self.profile = Profile() # Classify packages installed_packages = [] notinstalled_packages = [] for package in self.packages: data = { 'name': package, 'version': None, 'description': self.packages.get(package).get('description') } if package in self.profile.packages: data['version'] = self.profile.get_package_version( package, self.get_package_release_name(package)) installed_packages += [data] else: notinstalled_packages += [data] # Print tables terminal_width, _ = click.get_terminal_size() if installed and installed_packages: # - Print installed packages table click.echo('\nInstalled packages:\n') PACKAGELIST_TPL = ('{name:20} {description:30} {version:<8}') click.echo('-' * terminal_width) click.echo( PACKAGELIST_TPL.format(name=click.style('Name', fg='cyan'), version='Version', description='Description')) click.echo('-' * terminal_width) for package in installed_packages: click.echo( PACKAGELIST_TPL.format( name=click.style(package.get('name'), fg='cyan'), version=package.get('version'), description=package.get('description'))) if notinstalled and notinstalled_packages: # - Print not installed packages table click.echo('\nNot installed packages:\n') PACKAGELIST_TPL = ('{name:20} {description:30}') click.echo('-' * terminal_width) click.echo( PACKAGELIST_TPL.format(name=click.style('Name', fg='yellow'), description='Description')) click.echo('-' * terminal_width) for package in notinstalled_packages: click.echo( PACKAGELIST_TPL.format( name=click.style(package.get('name'), fg='yellow'), description=package.get('description'))) def list_boards(self): """Return a list with all the supported boards""" # Print table click.echo('\nSupported boards:\n') BOARDLIST_TPL = ('{board:25} {fpga:20} {type:<5} {size:<5} {pack:<10}') terminal_width, _ = click.get_terminal_size() click.echo('-' * terminal_width) click.echo( BOARDLIST_TPL.format(board=click.style('Board', fg='cyan'), fpga='FPGA', type='Type', size='Size', pack='Pack')) click.echo('-' * terminal_width) for board in self.boards: fpga = self.boards.get(board).get('fpga') click.echo( BOARDLIST_TPL.format(board=click.style(board, fg='cyan'), fpga=fpga, type=self.fpgas.get(fpga).get('type'), size=self.fpgas.get(fpga).get('size'), pack=self.fpgas.get(fpga).get('pack'))) click.secho(BOARDS_MSG, fg='green') def list_fpgas(self): """Return a list with all the supported FPGAs""" # Print table click.echo('\nSupported FPGAs:\n') FPGALIST_TPL = ('{fpga:30} {type:<5} {size:<5} {pack:<10}') terminal_width, _ = click.get_terminal_size() click.echo('-' * terminal_width) click.echo( FPGALIST_TPL.format(fpga=click.style('FPGA', fg='cyan'), type='Type', size='Size', pack='Pack')) click.echo('-' * terminal_width) for fpga in self.fpgas: click.echo( FPGALIST_TPL.format(fpga=click.style(fpga, fg='cyan'), type=self.fpgas.get(fpga).get('type'), size=self.fpgas.get(fpga).get('size'), pack=self.fpgas.get(fpga).get('pack'))) def _check_packages(self, packages, current_platform=''): filtered_packages = {} for pkg in packages.keys(): check = True release = packages.get(pkg).get('release') if 'available_platforms' in release: platforms = release.get('available_platforms') check = False current_platform = current_platform or util.get_systype() for platform in platforms: check |= current_platform in platform if check: filtered_packages[pkg] = packages.get(pkg) return filtered_packages
class Drivers(object): # pragma: no cover # FTDI rules files paths ftdi_rules_local_path = util.safe_join( util.get_folder('resources'), '80-fpga-ftdi.rules') ftdi_rules_system_path = '/etc/udev/rules.d/80-fpga-ftdi.rules' old_ftdi_rules_system_path = '/etc/udev/rules.d/80-icestick.rules' # Serial rules files paths serial_rules_local_path = util.safe_join( util.get_folder('resources'), '80-fpga-serial.rules') serial_rules_system_path = '/etc/udev/rules.d/80-fpga-serial.rules' # Driver to restore: mac os driverC = '' def ftdi_enable(self): if 'linux' in platform: return self._ftdi_enable_linux() elif 'darwin' in platform: self._setup_darwin() return self._ftdi_enable_darwin() elif 'windows' in platform: self._setup_windows() return self._ftdi_enable_windows() def ftdi_disable(self): if 'linux' in platform: return self._ftdi_disable_linux() elif 'darwin' in platform: self._setup_darwin() return self._ftdi_disable_darwin() elif 'windows' in platform: self._setup_windows() return self._ftdi_disable_windows() def serial_enable(self): if 'linux' in platform: return self._serial_enable_linux() elif 'darwin' in platform: self._setup_darwin() return self._serial_enable_darwin() elif 'windows' in platform: self._setup_windows() return self._serial_enable_windows() def serial_disable(self): if 'linux' in platform: return self._serial_disable_linux() elif 'darwin' in platform: self._setup_darwin() return self._serial_disable_darwin() elif 'windows' in platform: self._setup_windows() return self._serial_disable_windows() def pre_upload(self): if 'darwin' in platform: self._setup_darwin() self._pre_upload_darwin() def post_upload(self): if 'darwin' in platform: self._setup_darwin() self._post_upload_darwin() def _setup_darwin(self): self.profile = Profile() def _setup_windows(self): profile = Profile() resources = Resources() self.name = 'drivers' self.version = util.get_package_version(self.name, profile) self.spec_version = util.get_package_spec_version(self.name, resources) def _ftdi_enable_linux(self): click.secho('Configure FTDI drivers for FPGA') if not isfile(self.ftdi_rules_system_path): subprocess.call(['sudo', 'cp', self.ftdi_rules_local_path, self.ftdi_rules_system_path]) self._reload_rules() click.secho('FTDI drivers enabled', fg='green') click.secho('Unplug and reconnect your board', fg='yellow') else: click.secho('Already enabled', fg='yellow') def _ftdi_disable_linux(self): if isfile(self.old_ftdi_rules_system_path): subprocess.call(['sudo', 'rm', self.old_ftdi_rules_system_path]) if isfile(self.ftdi_rules_system_path): click.secho('Revert FTDI drivers configuration') subprocess.call(['sudo', 'rm', self.ftdi_rules_system_path]) self._reload_rules() click.secho('FTDI drivers disabled', fg='green') click.secho('Unplug and reconnect your board', fg='yellow') else: click.secho('Already disabled', fg='yellow') def _serial_enable_linux(self): click.secho('Configure Serial drivers for FPGA') if not isfile(self.serial_rules_system_path): group_added = self._add_dialout_group() subprocess.call(['sudo', 'cp', self.serial_rules_local_path, self.serial_rules_system_path]) self._reload_rules() click.secho('Serial drivers enabled', fg='green') click.secho('Unplug and reconnect your board', fg='yellow') if group_added: click.secho('Restart your machine to enable the dialout group', fg='yellow') else: click.secho('Already enabled', fg='yellow') def _serial_disable_linux(self): if isfile(self.serial_rules_system_path): click.secho('Revert Serial drivers configuration') subprocess.call(['sudo', 'rm', self.serial_rules_system_path]) self._reload_rules() click.secho('Serial drivers disabled', fg='green') click.secho('Unplug and reconnect your board', fg='yellow') else: click.secho('Already disabled', fg='yellow') def _reload_rules(self): subprocess.call(['sudo', 'udevadm', 'control', '--reload-rules']) subprocess.call(['sudo', 'udevadm', 'trigger']) subprocess.call(['sudo', 'service', 'udev', 'restart']) def _add_dialout_group(self): groups = subprocess.check_output('groups') if 'dialout' not in groups: subprocess.call('sudo usermod -a -G dialout $USER', shell=True) return True def _ftdi_enable_darwin(self): # Check homebrew brew = subprocess.call('which brew > /dev/null', shell=True) if brew != 0: click.secho('Error: homebrew is required', fg='red') else: click.secho('Enable FTDI drivers for FPGA') subprocess.call(['brew', 'update']) self._brew_install('libffi') self._brew_install('libftdi') self.profile.add_setting('macos_ftdi_drivers', True) self.profile.save() click.secho('FTDI drivers enabled', fg='green') def _ftdi_disable_darwin(self): click.secho('Disable FTDI drivers configuration') self.profile.add_setting('macos_ftdi_drivers', False) self.profile.save() click.secho('FTDI drivers disabled', fg='green') def _serial_enable_darwin(self): # Check homebrew brew = subprocess.call('which brew > /dev/null', shell=True) if brew != 0: click.secho('Error: homebrew is required', fg='red') else: click.secho('Enable Serial drivers for FPGA') subprocess.call(['brew', 'update']) self._brew_install('libffi') self._brew_install('libusb') # self._brew_install_serial_drivers() click.secho('Serial drivers enabled', fg='green') def _serial_disable_darwin(self): click.secho('Disable Serial drivers configuration') click.secho('Serial drivers disabled', fg='green') def _brew_install(self, package): subprocess.call(['brew', 'install', '--force', package]) subprocess.call(['brew', 'unlink', package]) subprocess.call(['brew', 'link', '--force', package]) def _brew_install_serial_drivers(self): subprocess.call( ['brew', 'tap', 'mengbo/ch340g-ch34g-ch34x-mac-os-x-driver', 'https://github.com/mengbo/ch340g-ch34g-ch34x-mac-os-x-driver']) subprocess.call( ['brew', 'cask', 'install', 'wch-ch34x-usb-serial-driver']) def _pre_upload_darwin(self): if self.profile.settings.get('macos_ftdi_drivers', False): # Check and unload the drivers driverA = 'com.FTDI.driver.FTDIUSBSerialDriver' driverB = 'com.apple.driver.AppleUSBFTDI' if self._check_ftdi_driver_darwin(driverA): subprocess.call(['sudo', 'kextunload', '-b', driverA]) self.driverC = driverA elif self._check_ftdi_driver_darwin(driverB): subprocess.call(['sudo', 'kextunload', '-b', driverB]) self.driverC = driverB def _post_upload_darwin(self): if self.profile.settings.get('macos_ftdi_drivers', False): # Restore previous driver configuration if self.driverC: subprocess.call(['sudo', 'kextload', '-b', self.driverC]) def _check_ftdi_driver_darwin(self, driver): return driver in str(subprocess.check_output(['kextstat'])) def _ftdi_enable_windows(self): drivers_base_dir = util.get_package_dir('tools-drivers') drivers_bin_dir = util.safe_join(drivers_base_dir, 'bin') drivers_share_dir = util.safe_join(drivers_base_dir, 'share') zadig_ini_path = util.safe_join(drivers_share_dir, 'zadig.ini') zadig_ini = 'zadig.ini' try: if util.check_package( self.name, self.version, self.spec_version, drivers_bin_dir ): click.secho('Launch drivers configuration tool') click.secho(FTDI_INSTALL_DRIVER_INSTRUCTIONS, fg='yellow') # Copy zadig.ini with open(zadig_ini, 'w') as ini_file: with open(zadig_ini_path, 'r') as local_ini_file: ini_file.write(local_ini_file.read()) result = util.exec_command( util.safe_join(drivers_bin_dir, 'zadig.exe')) click.secho('FTDI drivers configuration finished', fg='green') else: result = 1 except Exception as e: click.secho('Error: ' + str(e), fg='red') result = 1 finally: # Remove zadig.ini if isfile(zadig_ini): os.remove(zadig_ini) if not isinstance(result, int): result = result.get('returncode') return result def _ftdi_disable_windows(self): click.secho('Launch device manager') click.secho(FTDI_UNINSTALL_DRIVER_INSTRUCTIONS, fg='yellow') result = util.exec_command('mmc devmgmt.msc') return result.get('returncode') def _serial_enable_windows(self): drivers_base_dir = util.get_package_dir('tools-drivers') drivers_bin_dir = util.safe_join(drivers_base_dir, 'bin') try: if util.check_package( self.name, self.version, self.spec_version, drivers_bin_dir ): click.secho('Launch drivers configuration tool') click.secho(SERIAL_INSTALL_DRIVER_INSTRUCTIONS, fg='yellow') result = util.exec_command( util.safe_join(drivers_bin_dir, 'serial_install.exe')) click.secho('Serial drivers configuration finished', fg='green') else: result = 1 except Exception as e: click.secho('Error: ' + str(e), fg='red') result = 1 if not isinstance(result, int): result = result.get('returncode') return result def _serial_disable_windows(self): click.secho('Launch device manager') click.secho(SERIAL_UNINSTALL_DRIVER_INSTRUCTIONS, fg='yellow') result = util.exec_command('mmc devmgmt.msc') return result.get('returncode')
def post_upload(self): if 'darwin' in platform: self.profile = Profile() self._post_upload_darwin()
class Resources(object): def __init__(self): self.packages = self._load_resource('packages') self.boards = self._load_resource('boards') self.fpgas = self._load_resource('fpgas') def _load_resource(self, name): resource = None filepath = os.path.join(os.path.dirname(__file__), 'resources', name + '.json') with open(filepath, 'r') as f: # Load the JSON file resource = json.loads(f.read()) return resource def list_packages(self, installed=True, notinstalled=True): """Return a list with all the installed/notinstalled packages""" self.profile = Profile() # Classify packages installed_packages = [] notinstalled_packages = [] for package in self.packages: data = { 'name': package, 'version': None, 'description': self.packages[package]['description'] } if self.profile.check_package(package): data['version'] = self.profile.get_version(package) installed_packages += [data] else: notinstalled_packages += [data] # Print tables terminal_width, _ = click.get_terminal_size() if installed and installed_packages: # - Print installed packages table click.echo('\nInstalled packages:\n') PACKAGELIST_TPL = ('{name:20} {description:30} {version:<8}') click.echo('-' * terminal_width) click.echo( PACKAGELIST_TPL.format(name=click.style('Name', fg='cyan'), version='Version', description='Description')) click.echo('-' * terminal_width) for package in installed_packages: click.echo( PACKAGELIST_TPL.format(name=click.style(package['name'], fg='cyan'), version=package['version'], description=package['description'])) if notinstalled and notinstalled_packages: # - Print not installed packages table click.echo('\nNot installed packages:\n') PACKAGELIST_TPL = ('{name:20} {description:30}') click.echo('-' * terminal_width) click.echo( PACKAGELIST_TPL.format(name=click.style('Name', fg='yellow'), description='Description')) click.echo('-' * terminal_width) for package in notinstalled_packages: click.echo( PACKAGELIST_TPL.format(name=click.style(package['name'], fg='yellow'), description=package['description'])) def list_boards(self): """Return a list with all the supported boards""" # Print table click.echo('\nSupported boards:\n') BOARDLIST_TPL = ('{board:22} {fpga:20} {type:<5} {size:<5} {pack:<10}') terminal_width, _ = click.get_terminal_size() click.echo('-' * terminal_width) click.echo( BOARDLIST_TPL.format(board=click.style('Board', fg='cyan'), fpga='FPGA', type='Type', size='Size', pack='Pack')) click.echo('-' * terminal_width) for board in self.boards: fpga = self.boards[board]['fpga'] click.echo( BOARDLIST_TPL.format(board=click.style(board, fg='cyan'), fpga=fpga, type=self.fpgas[fpga]['type'], size=self.fpgas[fpga]['size'], pack=self.fpgas[fpga]['pack'])) click.secho(BOARDS_MSG, fg='green') def list_fpgas(self): """Return a list with all the supported FPGAs""" # Print table click.echo('\nSupported FPGAs:\n') FPGALIST_TPL = ('{fpga:30} {type:<5} {size:<5} {pack:<10}') terminal_width, _ = click.get_terminal_size() click.echo('-' * terminal_width) click.echo( FPGALIST_TPL.format(fpga=click.style('FPGA', fg='cyan'), type='Type', size='Size', pack='Pack')) click.echo('-' * terminal_width) for fpga in self.fpgas: click.echo( FPGALIST_TPL.format(fpga=click.style(fpga, fg='cyan'), type=self.fpgas[fpga]['type'], size=self.fpgas[fpga]['size'], pack=self.fpgas[fpga]['pack']))
class Installer(object): def __init__(self, package, platform='', force=False, checkversion=True): # Parse version if '@' in package: split = package.split('@') self.package = split[0] self.version = split[1] else: self.package = package self.version = None self.force_install = force self.packages_dir = '' self.resources = Resources(platform) if self.package in self.resources.packages: self.profile = Profile() dirname = 'packages' self.packages_dir = util.safe_join(util.get_home_dir(), dirname) # Get data data = self.resources.packages.get(self.package) distribution = self.resources.distribution self.spec_version = distribution.get('packages').get(self.package) self.package_name = data.get('release').get('package_name') self.extension = data.get('release').get('extension') platform = platform or self._get_platform() if checkversion: # Check version valid_version = self._get_valid_version( data.get('repository').get('name'), data.get('repository').get('organization'), data.get('release').get('tag_name') ) # Valid version if not valid_version: # Error click.secho('Error: no valid version found', fg='red') exit(1) self.version = valid_version # e.g., [linux_x86_64, linux] platform_os = platform.split('_')[0] self.download_urls = [ { 'url': self.get_download_url(data, platform), 'platform': platform }, { 'url': self.get_download_url(data, platform_os), 'platform': platform_os } ] if self.packages_dir == '': click.secho( 'Error: no such package \'{}\''.format(self.package), fg='red') exit(1) def get_download_url(self, data, platform): compressed_name = data.get('release').get('compressed_name') self.compressed_name = compressed_name.replace( '%V', self.version).replace('%P', platform) uncompressed_name = data.get('release').get('uncompressed_name') self.uncompressed_name = uncompressed_name.replace( '%V', self.version).replace('%P', platform) tarball = self._get_tarball_name( self.compressed_name, self.extension ) download_url = self._get_download_url( data.get('repository').get('name'), data.get('repository').get('organization'), data.get('release').get('tag_name').replace( '%V', self.version), tarball ) return download_url def install(self): click.echo('Installing %s package:' % click.style( self.package, fg='cyan')) if not isdir(self.packages_dir): makedirs(self.packages_dir) assert isdir(self.packages_dir) dlpath = None try: # Try full platform platform_download_url = self.download_urls[0].get('url') dlpath = self._download(platform_download_url) except IOError as e: click.secho('Warning: permission denied in packages directory', fg='yellow') click.secho(str(e), fg='red') except Exception: # Try os name dlpath = self._install_os_package(platform_download_url) # Install downloaded package self._install_package(dlpath) # Rename unpacked dir to package dir self._rename_unpacked_dir() def _install_os_package(self, platform_download_url): os_download_url = self.download_urls[1].get('url') if platform_download_url != os_download_url: click.secho( 'Warning: full platform does not match: {}\ '.format(self.download_urls[0].get('platform')), fg='yellow') click.secho( ' Trying OS name: {}\ '.format(self.download_urls[1].get('platform')), fg='yellow') try: return self._download(os_download_url) except Exception as e: click.secho( 'Error: {}'.format(str(e)), fg='red') else: click.secho( 'Error: package not availabe for this platform', fg='red') def _install_package(self, dlpath): if dlpath: package_dir = util.safe_join( self.packages_dir, self.package_name) if isdir(package_dir): shutil.rmtree(package_dir) if self.uncompressed_name: self._unpack(dlpath, self.packages_dir) else: self._unpack(dlpath, util.safe_join( self.packages_dir, self.package_name)) remove(dlpath) self.profile.add_package(self.package, self.version) self.profile.save() click.secho( """Package \'{}\' has been """ """successfully installed!""".format(self.package), fg='green') def _rename_unpacked_dir(self): if self.uncompressed_name: unpack_dir = util.safe_join( self.packages_dir, self.uncompressed_name) package_dir = util.safe_join( self.packages_dir, self.package_name) if isdir(unpack_dir): rename(unpack_dir, package_dir) def uninstall(self): if isdir(util.safe_join(self.packages_dir, self.package_name)): click.echo('Uninstalling %s package:' % click.style( self.package, fg='cyan')) shutil.rmtree( util.safe_join(self.packages_dir, self.package_name)) click.secho( """Package \'{}\' has been """ """successfully uninstalled!""".format(self.package), fg='green') else: util.show_package_path_error(self.package) self.profile.remove_package(self.package) self.profile.save() def _get_platform(self): return util.get_systype() def _get_download_url(self, name, organization, tag, tarball): url = 'https://github.com/{0}/{1}/releases/download/{2}/{3}'.format( organization, name, tag, tarball) return url def _get_tarball_name(self, name, extension): tarball = '{0}.{1}'.format( name, extension) return tarball def _get_valid_version(self, rel_name, organization, tag_name): # Download latest releases list releases = api_request('{}/releases'.format(rel_name), organization) if self.version: # Find required version via @ if not util.check_package_version(self.version, self.spec_version): util.show_package_version_warning( self.package, self.version, self.spec_version) exit(1) return self._find_required_version( releases, tag_name, self.version, self.spec_version) else: # Find latest version release return self._find_latest_version( releases, tag_name, self.spec_version) def _find_required_version(self, releases, tag_name, req_v, spec_v): for release in releases: if 'tag_name' in release: tag = tag_name.replace('%V', req_v) if tag == release.get('tag_name'): prerelease = release.get('prerelease', False) if prerelease and not self.force_install: click.secho( 'Warning: ' + req_v + ' is' + ' a pre-release. Use --force to install', fg='yellow') exit(1) return req_v def _find_latest_version(self, releases, tag_name, spec_v): for release in releases: if 'tag_name' in release: pattern = tag_name.replace('%V', '(?P<v>.*?)') + '$' match = re.search(pattern, release.get('tag_name')) if match: prerelease = release.get('prerelease', False) if not prerelease: version = match.group('v') if util.check_package_version(version, spec_v): return version def _download(self, url): # Note: here we check only for the version of locally installed # packages. For this reason we don't say what's the installation # path. if not self.profile.installed_version(self.package, self.version) \ or self.force_install: fd = FileDownloader(url, self.packages_dir) filepath = fd.get_filepath() click.secho('Download ' + basename(filepath)) try: fd.start() except KeyboardInterrupt: if isfile(filepath): remove(filepath) click.secho('Abort download!', fg='red') exit(1) return filepath else: click.secho('Already installed. Version {0}'.format( self.profile.get_package_version(self.package)), fg='yellow') return None def _unpack(self, pkgpath, pkgdir): fu = FileUnpacker(pkgpath, pkgdir) return fu.start()
class Installer(object): def __init__(self, package, platform='', force=False, checkversion=True): # Parse version if '@' in package: split = package.split('@') self.package = split[0] self.version = split[1] else: self.package = package self.version = None self.force_install = force self.packages_dir = '' self.resources = Resources(platform) if self.package in self.resources.packages: self.profile = Profile() dirname = 'packages' self.packages_dir = util.safe_join(util.get_home_dir(), dirname) # Get data data = self.resources.packages.get(self.package) distribution = self.resources.distribution self.specversion = distribution.get('packages').get(self.package) self.package_name = data.get('release').get('package_name') self.extension = data.get('release').get('extension') platform = platform or self._get_platform() if checkversion: # Check version valid_version = self._get_valid_version( data.get('repository').get('name'), data.get('repository').get('organization'), data.get('release').get('tag_name'), self.version, self.specversion, force ) # Valid version if not valid_version: # Error click.secho( 'Error: No version {} found'.format(self.version), fg='red') exit(1) self.version = valid_version # e.g., [linux_x86_64, linux] platform_os = platform.split('_')[0] self.download_urls = [ { 'url': self.get_download_url(data, platform), 'platform': platform }, { 'url': self.get_download_url(data, platform_os), 'platform': platform_os } ] if self.packages_dir == '': click.secho( 'Error: No such package \'{0}\''.format(self.package), fg='red') exit(1) def get_download_url(self, data, platform): compressed_name = data.get('release').get('compressed_name') self.compressed_name = compressed_name.replace( '%V', self.version).replace('%P', platform) uncompressed_name = data.get('release').get('uncompressed_name') self.uncompressed_name = uncompressed_name.replace( '%V', self.version).replace('%P', platform) tarball = self._get_tarball_name( self.compressed_name, self.extension ) download_url = self._get_download_url( data.get('repository').get('name'), data.get('repository').get('organization'), data.get('release').get('tag_name').replace( '%V', self.version), tarball ) return download_url def install(self): click.echo('Installing %s package:' % click.style( self.package, fg='cyan')) if not isdir(self.packages_dir): makedirs(self.packages_dir) assert isdir(self.packages_dir) dlpath = None try: # Try full platform platform_download_url = self.download_urls[0].get('url') dlpath = self._download(platform_download_url) except IOError as e: click.secho('Warning: permission denied in packages directory', fg='yellow') click.secho(str(e), fg='red') except Exception as e: # Try os name dlpath = self._install_os_package(platform_download_url) # Install downloaded package self._install_package(dlpath) # Rename unpacked dir to package dir self._rename_unpacked_dir() def _install_os_package(self, platform_download_url): os_download_url = self.download_urls[1].get('url') if platform_download_url != os_download_url: click.secho( 'Warning: full platform does not match: {}\ '.format(self.download_urls[0].get('platform')), fg='yellow') click.secho( ' Trying OS name: {}\ '.format(self.download_urls[1].get('platform')), fg='yellow') try: return self._download(os_download_url) except Exception as e: click.secho( 'Error: {}'.format(str(e)), fg='red') else: click.secho( 'Error: package not availabe for this platform', fg='red') def _install_package(self, dlpath): if dlpath: package_dir = util.safe_join( self.packages_dir, self.package_name) if isdir(package_dir): shutil.rmtree(package_dir) if self.uncompressed_name: self._unpack(dlpath, self.packages_dir) else: self._unpack(dlpath, util.safe_join( self.packages_dir, self.package_name)) remove(dlpath) self.profile.add_package(self.package, self.version) self.profile.save() click.secho( """Package \'{}\' has been """ """successfully installed!""".format(self.package), fg='green') def _rename_unpacked_dir(self): if self.uncompressed_name: unpack_dir = util.safe_join( self.packages_dir, self.uncompressed_name) package_dir = util.safe_join( self.packages_dir, self.package_name) if isdir(unpack_dir): rename(unpack_dir, package_dir) def uninstall(self): if isdir(util.safe_join(self.packages_dir, self.package_name)): click.echo('Uninstalling %s package' % click.style( self.package, fg='cyan')) shutil.rmtree( util.safe_join(self.packages_dir, self.package_name)) click.secho( """Package \'{}\' has been """ """successfully uninstalled!""".format(self.package), fg='green') else: click.secho('Package \'{0}\' is not installed'.format( self.package), fg='red') self.profile.remove_package(self.package) self.profile.save() def _get_platform(self): return util.get_systype() def _get_download_url(self, name, organization, tag, tarball): url = 'https://github.com/{0}/{1}/releases/download/{2}/{3}'.format( organization, name, tag, tarball) return url def _get_tarball_name(self, name, extension): tarball = '{0}.{1}'.format( name, extension) return tarball def _get_valid_version(self, name, organization, tag_name, req_version='', specversion='', force=False): # Check spec version try: spec = semantic_version.Spec(specversion) except ValueError: click.secho('Invalid distribution version {0}: {1}'.format( name, specversion), fg='red') exit(1) # Download latest releases list releases = api_request('{}/releases'.format(name), organization) if req_version: # Find required version via @ return self._find_required_version( releases, tag_name, req_version, spec, force) else: # Find latest version release return self._find_latest_version( releases, tag_name, req_version, spec) def _find_required_version(self, releases, tag_name, req_version, spec, force): version = self._check_sem_version(req_version, spec) for release in releases: prerelease = 'prerelease' in release and release.get('prerelease') if 'tag_name' in release: tag = tag_name.replace('%V', req_version) if tag == release.get('tag_name'): if prerelease and not force: click.secho( 'Warning: ' + req_version + ' is' + ' a pre-release.\n' + ' Use --force to install', fg='yellow') exit(2) return version def _find_latest_version(self, releases, tag_name, req_version, spec): for release in releases: prerelease = 'prerelease' in release and release.get('prerelease') if 'tag_name' in release: pattern = tag_name.replace('%V', '(?P<v>.*?)') + '$' match = re.search(pattern, release.get('tag_name')) if match: if not prerelease: version = match.group('v') return self._check_sem_version(version, spec) def _check_sem_version(self, version, spec): try: if semantic_version.Version(version) in spec: return version else: click.secho( 'Error: Invalid semantic version ({0})'.format( self.specversion), fg='red') exit(1) except ValueError: click.secho( 'Error: Invalid semantic version ({0})'.format( self.specversion), fg='red') exit(1) def _download(self, url): # Note: here we check only for the version of locally installed # packages. For this reason we don't say what's the installation # path. if self.profile.check_package_version(self.package, self.version) \ or self.force_install: fd = FileDownloader(url, self.packages_dir) filepath = fd.get_filepath() click.secho('Download ' + basename(filepath)) try: fd.start() except KeyboardInterrupt: if isfile(filepath): remove(filepath) click.secho('Abort download!', fg='red') exit(1) return filepath else: click.secho('Already installed. Version {0}'.format( self.profile.get_package_version(self.package)), fg='yellow') return None def _unpack(self, pkgpath, pkgdir): fu = FileUnpacker(pkgpath, pkgdir) return fu.start()
def __init__(self, package): # Parse version if '@' in package: split = package.split('@') self.package = split[0] self.version = split[1] else: self.package = package self.version = None self.forced_install = False self.valid_version = True self.resources = Resources() self.profile = Profile() if self.package in self.resources.packages: data = self.resources.packages[self.package] if self.version: # Validate version valid = self._validate_version( data['repository']['name'], data['repository']['organization'], data['release']['tag_name'], self.version ) if valid: self.forced_install = True else: self.valid_version = False else: # Get latest version self.version = self._get_latest_version( data['repository']['name'], data['repository']['organization'], data['release']['tag_name'] ) self.arch = self._get_architecture() release = data['release'] self.compressed_name = release['compressed_name'].replace( '%V', self.version).replace('%A', self.arch) self.uncompressed_name = release['uncompressed_name'].replace( '%V', self.version).replace('%A', self.arch) self.package_name = data['release']['package_name'] if isinstance(data['release']['extension'], dict): for os in ['linux', 'darwin', 'windows']: if os in self.arch: self.extension = data['release']['extension'][os] else: self.extension = data['release']['extension'] self.tarball = self._get_tarball_name( self.compressed_name, self.extension ) self.download_url = self._get_download_url( data['repository']['name'], data['repository']['organization'], data['release']['tag_name'].replace('%V', self.version), self.tarball ) if 'main_dir' in data.keys(): self.packages_dir = join(expanduser('~'), data['main_dir']) else: self.packages_dir = join(util.get_home_dir(), 'packages')
def list_packages(self, installed=True, notinstalled=True): """Return a list with all the installed/notinstalled packages""" self.profile = Profile() # Classify packages installed_packages = [] notinstalled_packages = [] for package in self.packages: data = { 'name': package, 'version': None, 'description': self.packages.get(package).get('description') } if package in self.profile.packages: data['version'] = self.profile.get_package_version( package, self.get_package_release_name(package)) installed_packages += [data] else: notinstalled_packages += [data] # Print tables terminal_width, _ = click.get_terminal_size() if installed and installed_packages: # - Print installed packages table click.echo('\nInstalled packages:\n') PACKAGELIST_TPL = ('{name:20} {description:30} {version:<8}') click.echo('-' * terminal_width) click.echo(PACKAGELIST_TPL.format( name=click.style('Name', fg='cyan'), version='Version', description='Description')) click.echo('-' * terminal_width) for package in installed_packages: click.echo(PACKAGELIST_TPL.format( name=click.style(package.get('name'), fg='cyan'), version=package.get('version'), description=package.get('description'))) if notinstalled and notinstalled_packages: # - Print not installed packages table click.echo('\nNot installed packages:\n') PACKAGELIST_TPL = ('{name:20} {description:30}') click.echo('-' * terminal_width) click.echo(PACKAGELIST_TPL.format( name=click.style('Name', fg='yellow'), description='Description')) click.echo('-' * terminal_width) for package in notinstalled_packages: click.echo(PACKAGELIST_TPL.format( name=click.style(package.get('name'), fg='yellow'), description=package.get('description')))
def __init__(self, package, platform='', force=False, checkversion=True): # Parse version if '@' in package: split = package.split('@') self.package = split[0] self.version = split[1] else: self.package = package self.version = None self.force_install = force self.packages_dir = '' self.resources = Resources(platform) if self.package in self.resources.packages: self.profile = Profile() dirname = 'packages' self.packages_dir = util.safe_join(util.get_home_dir(), dirname) # Get data data = self.resources.packages.get(self.package) distribution = self.resources.distribution self.specversion = distribution.get('packages').get(self.package) self.package_name = data.get('release').get('package_name') self.extension = data.get('release').get('extension') platform = platform or self._get_platform() if checkversion: # Check version valid_version = self._get_valid_version( data.get('repository').get('name'), data.get('repository').get('organization'), data.get('release').get('tag_name'), self.version, self.specversion, force ) # Valid version if not valid_version: # Error click.secho( 'Error: No version {} found'.format(self.version), fg='red') exit(1) self.version = valid_version # e.g., [linux_x86_64, linux] platform_os = platform.split('_')[0] self.download_urls = [ { 'url': self.get_download_url(data, platform), 'platform': platform }, { 'url': self.get_download_url(data, platform_os), 'platform': platform_os } ] if self.packages_dir == '': click.secho( 'Error: No such package \'{0}\''.format(self.package), fg='red') exit(1)
def list_packages(self, installed=True, notinstalled=True): """Return a list with all the installed/notinstalled packages""" self.profile = Profile() # Classify packages installed_packages = [] notinstalled_packages = [] for package in self.packages: data = { 'name': package, 'version': None, 'description': self.packages.get(package).get('description') } if package in self.profile.packages: data['version'] = self.profile.get_package_version( package, self.get_package_release_name(package)) installed_packages += [data] else: notinstalled_packages += [data] # Print tables terminal_width, _ = click.get_terminal_size() if installed and installed_packages: # - Print installed packages table click.echo('\nInstalled packages:\n') PACKAGELIST_TPL = ('{name:20} {description:30} {version:<8}') click.echo('-' * terminal_width) click.echo( PACKAGELIST_TPL.format(name=click.style('Name', fg='cyan'), version='Version', description='Description')) click.echo('-' * terminal_width) for package in installed_packages: click.echo( PACKAGELIST_TPL.format( name=click.style(package.get('name'), fg='cyan'), version=package.get('version'), description=package.get('description'))) if notinstalled and notinstalled_packages: # - Print not installed packages table click.echo('\nNot installed packages:\n') PACKAGELIST_TPL = ('{name:20} {description:30}') click.echo('-' * terminal_width) click.echo( PACKAGELIST_TPL.format(name=click.style('Name', fg='yellow'), description='Description')) click.echo('-' * terminal_width) for package in notinstalled_packages: click.echo( PACKAGELIST_TPL.format( name=click.style(package.get('name'), fg='yellow'), description=package.get('description')))
def _setup_darwin(self): self.profile = Profile()