Browse Source

support macOS (BTC-only, no autosign)

Testing:

    $ test/test-release.sh -FCSTA

Tested on macOS 13.6.7 (Ventura) + Xcode 15.0.1
The MMGen Project 7 months ago
parent
commit
91991a13de

+ 1 - 1
mmgen/cfg.py

@@ -55,7 +55,7 @@ class GlobalConstants(Lockable):
 	prog_name = os.path.basename(sys.argv[0])
 	is_txprog = prog_name == 'mmgen-regtest' or prog_name.startswith('mmgen-tx')
 
-	if sys.platform not in ('linux','win32'):
+	if sys.platform not in ('linux', 'win32', 'darwin'):
 		die2(1,f'{sys.platform!r}: platform not supported by {proj_name}')
 
 	if os.getenv('HOME'):   # Linux or MSYS2

+ 1 - 1
mmgen/daemon.py

@@ -133,7 +133,7 @@ class Daemon(Lockable):
 				if len(pids) > 1:
 					self.pids = pids
 				return pids[0]
-		elif self.platform == 'linux':
+		elif self.platform in ('linux', 'darwin'):
 			ss = ' '.join(self.start_cmd)
 			cp = self.run_cmd(['pgrep','-f',ss],silent=True)
 			if cp.stdout:

+ 1 - 1
mmgen/data/version

@@ -1 +1 @@
-15.0.dev0
+15.0.dev1

+ 1 - 1
mmgen/main.py

@@ -24,7 +24,7 @@ import sys,os
 
 def launch(*, mod=None, func=None, fqmod=None, package='mmgen'):
 
-	if sys.platform == 'linux' and sys.stdin.isatty():
+	if sys.platform in ('linux', 'darwin') and sys.stdin.isatty():
 		import termios,atexit
 		fd = sys.stdin.fileno()
 		old = termios.tcgetattr(fd)

+ 2 - 1
mmgen/proto/btc/daemon.py

@@ -29,6 +29,7 @@ class bitcoin_core_daemon(CoinDaemon):
 	nonstd_datadir = False
 	datadirs = {
 		'linux': [gc.home_dir,'.bitcoin'],
+		'darwin': [gc.home_dir, '.bitcoin'],
 		'win32': [os.getenv('APPDATA'),'Bitcoin']
 	}
 	avail_opts = ('no_daemonize', 'online', 'bdb_wallet')
@@ -76,7 +77,7 @@ class bitcoin_core_daemon(CoinDaemon):
 			['--rpcallowip=127.0.0.1'],
 			[f'--rpcbind=127.0.0.1:{self.rpc_port}'],
 			['--pid='+self.pidfile,    self.use_pidfile],
-			['--daemon',               self.platform == 'linux' and not self.opt.no_daemonize],
+			['--daemon',               self.platform in ('linux', 'darwin') and not self.opt.no_daemonize],
 			['--fallbackfee=0.0002',   self.coin == 'BTC' and self.network == 'regtest'],
 			['--usecashaddr=0',        self.coin == 'BCH'],
 			['--deprecatedrpc=create_bdb', self.coin == 'BTC' and self.opt.bdb_wallet],

+ 1 - 0
mmgen/rpc.py

@@ -293,6 +293,7 @@ class RPCClient(MMGenObject):
 		if backend_id == 'auto':
 			return {
 				'linux': RPCBackends.httplib,
+				'darwin': RPCBackends.httplib,
 				'win32': RPCBackends.requests
 			}[sys.platform](self)
 		else:

+ 4 - 3
mmgen/term.py

@@ -27,9 +27,10 @@ from collections import namedtuple
 
 from .util import msg,msg_r,die
 
-if sys.platform == 'linux':
+if sys.platform in ('linux', 'darwin'):
 	import tty,termios
 	from select import select
+	hold_protect_timeout = 2 if sys.platform == 'darwin' else 0.3
 elif sys.platform == 'win32':
 	try:
 		import msvcrt
@@ -118,9 +119,8 @@ class MMGenTermLinux(MMGenTerm):
 		if cls.cfg.hold_protect_disable:
 			return
 		tty.setcbreak(cls.stdin_fd)
-		timeout = 0.3
 		while True:
-			key = select([sys.stdin], [], [], timeout)[0]
+			key = select([sys.stdin], [], [], hold_protect_timeout)[0]
 			if key:
 				sys.stdin.read(1)
 			else:
@@ -285,6 +285,7 @@ class MMGenTermMSWinStub(MMGenTermMSWin):
 def get_term():
 	return {
 		'linux': (MMGenTermLinux if sys.stdin.isatty() else MMGenTermLinuxStub),
+		'darwin': (MMGenTermLinux if sys.stdin.isatty() else MMGenTermLinuxStub),
 		'win32': (MMGenTermMSWin if sys.stdin.isatty() else MMGenTermMSWinStub),
 	}[sys.platform]
 

+ 1 - 0
mmgen/tw/view.py

@@ -181,6 +181,7 @@ class TwView(MMGenObject,metaclass=AsyncInit):
 			'\xe0O': 'm_bot',
 		}
 	}
+	scroll_keys['darwin'] = scroll_keys['linux']
 
 	def __new__(cls,cfg,proto,*args,**kwargs):
 		return MMGenObject.__new__(proto.base_proto_subclass(cls,cls.mod_subpath))

+ 6 - 2
test/cmdtest.py

@@ -30,7 +30,7 @@ def create_shm_dir(data_dir,trash_dir):
 	# under '/dev/shm' and put datadir and tmpdirs here.
 	import shutil
 	from subprocess import run
-	if sys.platform == 'win32':
+	if sys.platform in ('win32', 'darwin'):
 		for tdir in (data_dir,trash_dir):
 			try:
 				os.listdir(tdir)
@@ -293,7 +293,7 @@ def do_between():
 		sys.stderr.write('\n')
 
 def create_tmp_dirs(shm_dir):
-	if sys.platform == 'win32':
+	if sys.platform in ('win32', 'darwin'):
 		for cfg in sorted(cfgs):
 			mk_tmpdir(cfgs[cfg]['tmpdir'])
 	else:
@@ -664,6 +664,10 @@ class CmdTestRunner:
 			omsg(gray(f'INFO → skipping test {gname!r} (platform=win32)'))
 			return None
 
+		if sys.platform == 'darwin' and ct_cls.mac_skip:
+			omsg(gray(f'INFO → skipping test {gname!r} (platform=darwin)'))
+			return None
+
 		for k in ('segwit','segwit_random','bech32'):
 			if getattr(cfg,k):
 				segwit_opt = k

+ 1 - 0
test/cmdtest_py_d/ct_autosign.py

@@ -54,6 +54,7 @@ class CmdTestAutosignBase(CmdTestBase):
 	tmpdir_nums  = [18]
 	color        = True
 	win_skip     = True
+	mac_skip     = True
 	threaded     = False
 	daemon_coins = []
 

+ 4 - 0
test/cmdtest_py_d/ct_base.py

@@ -38,6 +38,7 @@ class CmdTestBase:
 	color = False
 	need_daemon = False
 	win_skip = False
+	mac_skip = False
 	tmpdir_nums = []
 	test_name = None
 
@@ -94,6 +95,9 @@ class CmdTestBase:
 		else:
 			return False
 
+	def skip_for_mac(self, extra_msg=None):
+		return self.skip_for_platform('darwin', extra_msg)
+
 	def skip_for_win(self, extra_msg=None):
 		return self.skip_for_platform('win32', extra_msg)
 

+ 23 - 10
test/cmdtest_py_d/ct_input.py

@@ -29,6 +29,8 @@ from .common import Ctrl_U,ref_dir
 from .ct_base import CmdTestBase
 from .input import stealth_mnemonic_entry,user_dieroll_entry
 
+hold_protect_delay = 2 if sys.platform == 'darwin' else None
+
 class CmdTestInput(CmdTestBase):
 	'user input'
 	networks = ('btc',)
@@ -238,7 +240,7 @@ class CmdTestInput(CmdTestBase):
 
 		return t
 
-	def _input_func(self,func_name,arg_dfls,func_args,text,expect,term):
+	def _input_func(self, func_name, arg_dfls, func_args, text, expect, term, delay=None):
 		if term and sys.platform == 'win32':
 			return ('skip_warn','pexpect_spawn not supported on Windows platform')
 		func_args = dict(zip(arg_dfls.keys(),func_args))
@@ -259,9 +261,9 @@ class CmdTestInput(CmdTestBase):
 		prompt = func_args['prompt'] + prompt_add
 		t.expect('Calling ')
 		if prompt:
-			t.expect(prompt,text)
+			t.expect(prompt, text, delay=delay)
 		else:
-			t.send(text)
+			t.send(text, delay=delay)
 		ret = t.expect_getend('  ==> ')
 		assert ret == repr(expect), f'Text mismatch! {ret} != {repr(expect)}'
 		return t
@@ -275,14 +277,14 @@ class CmdTestInput(CmdTestBase):
 		}
 		return self._input_func('get_char',arg_dfls,func_args,text,expect,term)
 
-	def _line_input(self,func_args,text,expect,term):
+	def _line_input(self, func_args, text, expect, term, delay=None):
 		arg_dfls = {
 			'prompt': '', # positional
 			'echo': True,
 			'insert_txt': '',
 			'hold_protect': True,
 		}
-		return self._input_func('line_input',arg_dfls,func_args,text+'\n',expect,term)
+		return self._input_func('line_input', arg_dfls, func_args, text+'\n', expect, term, delay=delay)
 
 	def get_char1(self):
 		return self._get_char(['prompt> ','',True,5],'x','x',False)
@@ -321,7 +323,8 @@ class CmdTestInput(CmdTestBase):
 			['prompt> ', True, '', True],
 			'foo',
 			'foo',
-			True)
+			True,
+			hold_protect_delay)
 
 	def line_input_term2(self):
 		return self._line_input(
@@ -338,13 +341,18 @@ class CmdTestInput(CmdTestBase):
 			False)
 
 	def line_input_insert_term1(self):
+		if self.skip_for_mac('readline text buffer issues'):
+			return 'skip'
 		return self._line_input(
 			['prompt> ', True, 'foo', True],
 			'bar',
 			'foobar',
-			True)
+			True,
+			hold_protect_delay)
 
 	def line_input_insert_term2(self):
+		if self.skip_for_mac('readline text buffer issues'):
+			return 'skip'
 		return self._line_input(
 			['prompt> ', True, 'foo', False],
 			'bar',
@@ -356,7 +364,8 @@ class CmdTestInput(CmdTestBase):
 			['prompt> ', True, '', True],
 			'\b\bφυφυ\b\bβαρ',
 			'φυβαρ',
-			True)
+			True,
+			hold_protect_delay)
 
 	def line_input_edit_term_insert(self):
 		if self.skip_for_mac('readline text buffer issues'):
@@ -365,14 +374,18 @@ class CmdTestInput(CmdTestBase):
 			['prompt> ', True, 'φυφυ', True],
 			'\b\bβαρ',
 			'φυβαρ',
-			True)
+			True,
+			hold_protect_delay)
 
 	def line_input_erase_term(self):
+		if self.skip_for_mac('readline text buffer issues'):
+			return 'skip'
 		return self._line_input(
 			['prompt> ', True, 'foobarbaz', True],
 			Ctrl_U + 'foobar',
 			'foobar',
-			True)
+			True,
+			hold_protect_delay)
 
 	def _password_entry(self,prompt,opts=[],term=False):
 		if term and sys.platform == 'win32':

+ 1 - 1
test/cmdtest_py_d/ct_misc.py

@@ -135,7 +135,7 @@ class CmdTestMisc(CmdTestBase):
 				die('TestSuiteException', 'Terminal echoed in noecho mode!')
 			t.send('x')
 
-		if self.skip_for_win('no termios support'):
+		if self.skip_for_win('no termios support') or self.skip_for_mac('termios.ECHO issues'):
 			return 'skip'
 
 		t = self.spawn('test/misc/term_ni.py',['echo'],cmd_dir='.',pexpect_spawn=True,timeout=1)

+ 1 - 1
test/cmdtest_py_d/ct_xmrwallet.py

@@ -57,7 +57,7 @@ def stop_miner_wallet_daemon(self):
 	async_run(self.users['miner'].wd_rpc.stop_daemon())
 
 def kill_proxy(cls,args):
-	if sys.platform == 'linux':
+	if sys.platform in ('linux', 'darwin'):
 		omsg(f'Killing SSH SOCKS server at localhost:{cls.socks_port}')
 		cmd = [ 'pkill', '-f', ' '.join(args) ]
 		run(cmd)

+ 1 - 1
test/misc/term.py

@@ -21,7 +21,7 @@ commands = [
 	'get_char_one',
 	'get_char_one_raw',
 ]
-if sys.platform == 'linux':
+if sys.platform in ('linux', 'darwin'):
 	commands.extend([
 		'get_char',
 		'get_char_immed_chars',

+ 1 - 1
test/overlay/__init__.py

@@ -55,7 +55,7 @@ def overlay_setup(repo_root):
 
 			sys.stderr.write(f'Setting up overlay tree: {pkgname}\n')
 
-			make_link = os.symlink if sys.platform == 'linux' else shutil.copy2
+			make_link = os.symlink if sys.platform in ('linux', 'darwin') else shutil.copy2
 			shutil.rmtree(os.path.join(overlay_tree_dir,pkgname),ignore_errors=True)
 
 			import configparser

+ 4 - 1
test/test-release.sh

@@ -117,7 +117,7 @@ print_ver_hash() {
 }
 
 do_typescript() {
-	script -O "$1" -c "$2"
+	if [ "$DARWIN" ]; then script "$1" $2; else script -O "$1" -c "$2"; fi
 }
 
 install_package() {
@@ -234,6 +234,9 @@ if [ "$(uname -m)" == 'armv7l' ]; then
 	ARM32=1
 elif [ "$(uname -m)" == 'aarch64' ]; then
 	ARM64=1
+elif [ "$(uname -s)" == 'Darwin' ]; then
+	DARWIN=1
+	DISTRO='DARWIN'
 elif [ "$MSYSTEM" ] && uname -a | grep -qi 'msys'; then
 	MSYS2=1
 	DISTRO='MSYS2'

+ 4 - 0
test/unit_tests.py

@@ -187,6 +187,7 @@ def run_test(test,subtest=None):
 		t = getattr(mod,'unit_tests')()
 		altcoin_deps = getattr(t,'altcoin_deps',())
 		win_skip = getattr(t, 'win_skip', ())
+		mac_skip = getattr(t, 'mac_skip', ())
 		arm_skip = getattr(t, 'arm_skip', ())
 		subtests = (
 			[subtest] if subtest else
@@ -202,6 +203,9 @@ def run_test(test,subtest=None):
 			if sys.platform == 'win32' and subtest in win_skip:
 				cfg._util.qmsg(gray(f'Skipping unit subtest {subtest_disp!r} for Windows platform'))
 				continue
+			elif sys.platform == 'darwin' and subtest in mac_skip:
+				cfg._util.qmsg(gray(f'Skipping unit subtest {subtest_disp!r} for macOS platform'))
+				continue
 			elif platform.machine() == 'aarch64' and subtest in arm_skip:
 				cfg._util.qmsg(gray(f'Skipping unit subtest {subtest_disp!r} for ARM platform'))
 				continue

+ 1 - 0
test/unit_tests_d/ut_testdep.py

@@ -16,6 +16,7 @@ class unit_tests:
 
 	altcoin_deps = ('pycoin','monero_python','keyconv','zcash_mini','ethkey','ssh_socks_proxy')
 	win_skip = ('losetup','zcash_mini')
+	mac_skip = ('losetup',)
 
 	def pylint(self,name,ut):
 		try: