#!/usr/bin/env python3 import configparser import sys import paramiko import base64 import struct import os import json def cmd(cmd, channel): channel.exec_command(base64.encodebytes(cmd)) def sstr(s): return struct.pack('>I', len(s)) + s.encode('utf-8') def rstr(channel): l = struct.unpack('>I', channel.recv(4))[0] return bytes.decode(channel.recv(l), 'utf-8') def send_file(channel, file, othername=None): print(f"> {file}") if not os.path.exists(file): print(f"Error: {file} does not exists") with open(file, 'rb') as f: stat = os.fstat(f.fileno()) print(' -> fsize', stat.st_size) if othername is None: othername = file fsize = struct.pack('>q', stat.st_size) cmd(b'sf'+sstr(othername)+fsize, channel) status = channel.recv(3) if status!=b'OK\n': print('Something went wrong...') exit(1) i = stat.st_size while i>0: fdata = f.read(1024) i -= 1024 channel.sendall(fdata) def recv_file(channel, file): print(f"< {file}") if os.path.dirname(file) != '': os.makedirs(os.path.dirname(file), exist_ok=True) with open(file, 'wb') as f: cmd(b'rf'+sstr(file), channel) while True: status = channel.recv(2) if status != b'\x00\x00': break if status!=b'OK': msg = channel.recv(1024) if bytes.decode(msg, 'ascii').startswith('File not found'): print("Error: File not found...") return else: print("Error:", bytes.decode(msg, 'ascii')) exit(1) fsize = channel.recv(8) fsize = struct.unpack('>q', fsize)[0] print(' -> fsize', fsize) while fsize>0: f.write(channel.recv(1024)) fsize -= 1024 def recv_dir(channel, dr): cmd(b'ls'+sstr(dr), channel) while True: status = channel.recv(2) if status != b'\x00\x00': break if status!=b'OK': msg = channel.recv(1024) print("Error:", bytes.decode(msg, 'ascii')) exit(1) ls = rstr(channel) for p in ls.split('\n'): tp = p[0] name = p[1:] if tp=='d': recv_dir(channel, f'{dr}/{name}') else: recv_file(channel, f'{dr}/{name}') def print_help(): print("Unified FPGA synthesizer frontend - remote execution\r\n(c) Joppe Blondel - 2022\r\n") print(f"Usage: {sys.argv[0]} [ OPTIONS ] action [ target ] ...") print("") print("Options:") print(" -h Show this help message") print(" -c Configuration file, defaults to project.cfg") if __name__=="__main__": # Parse arguments i = 1 nextarg = None configpath = 'project.cfg' target = '' while iq', hash(host_key.get_base64())), channel) # Send config cmd(b'cf' + sstr(json.dumps({s:dict(config.items(s)) for s in config.sections()})), channel) subprocesses = [] try: toolchain = config.get(f'target.{target}', 'toolchain', fallback='NONE') if toolchain=='NONE': print("ERROR: No toolchain specified for target") exit(1) try: exec(f"from remotesyn.toolchains.{toolchain} import do") except ImportError: print(f"ERROR: Unknown toolchain '{toolchain}'") exit(1) # Send all files for it in config.items(f"target.{target}"): if it[0].startswith('files_'): for f in it[1].split(): send_file(channel, f) cmd(b'do'+sstr(target), channel) ret = 0 # Receive output dir recv_dir(channel, config.get('project', 'out_dir', fallback='out')) if ret!=0: exit(ret) except paramiko.ssh_exception.SSHException as e: print("ERROR: Connection error...") for p in subprocesses: p.kill() exit(0) except KeyboardInterrupt: print("\rStopping rmbuild") for p in subprocesses: p.kill() exit(0)