Browse Source

full macOS support

All coins and features supported on Linux are now supported on macOS

Testing:

    $ test/test-release.sh -F
The MMGen Project 6 months ago
parent
commit
bcff2e4fa3

+ 1 - 1
mmgen/data/version

@@ -1 +1 @@
-15.0.dev3
+15.0.dev4

+ 6 - 2
mmgen/filename.py

@@ -42,7 +42,7 @@ class File:
 
 		import stat
 		if stat.S_ISBLK(st.st_mode):
-			if sys.platform in ('win32', 'darwin'):
+			if sys.platform in ('win32',):
 				die(2, 'Access to raw block devices not supported on platform {sys.platform!r}')
 			mode = (os.O_RDONLY,os.O_RDWR)[bool(write)]
 			try:
@@ -52,7 +52,11 @@ class File:
 					die(2,f'{fn!r}: permission denied')
 #				if e.errno != 17: raise
 			else:
-				self.size = os.lseek(fd, 0, os.SEEK_END)
+				if sys.platform == 'linux':
+					self.size = os.lseek(fd, 0, os.SEEK_END)
+				elif sys.platform == 'darwin':
+					from .platform.darwin.util import get_device_size
+					self.size = get_device_size(fn)
 				os.close(fd)
 		else:
 			self.size  = st.st_size

+ 1 - 1
mmgen/fileutil.py

@@ -86,7 +86,7 @@ def _check_file_type_and_access(fname,ftype,blkdev_ok=False):
 			(stat.S_ISLNK,'symbolic link')
 		]
 		if blkdev_ok:
-			if not sys.platform in ('win32', 'darwin'):
+			if not sys.platform in ('win32',):
 				ok_types.append((stat.S_ISBLK, 'block device'))
 
 	try:

+ 10 - 0
mmgen/platform/darwin/util.py

@@ -18,6 +18,16 @@ from subprocess import run, PIPE, DEVNULL
 from ...color import cyan
 from ...obj import MMGenLabel
 
+def get_device_size(fn):
+	import re
+	cp = run(['diskutil', 'info', fn], text=True, stdout=PIPE, check=True)
+	res = [e for e in cp.stdout.splitlines() if 'Disk Size' in e]
+	errmsg = '‘diskutil info’ output could not be parsed for device size'
+	assert len(res) == 1, f'{errmsg}:\n{cp.stdout}'
+	m = re.search(r'\((\d+) Bytes\)', res[0])
+	assert m, f'{errmsg}:\n{res[0]}'
+	return int(m[1])
+
 class RamDiskLabel(MMGenLabel):
 	max_len = 24
 	desc = 'ramdisk label'

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

@@ -133,6 +133,7 @@ class bitcoin_cash_node_daemon(bitcoin_core_daemon):
 	nonstd_datadir = True
 	datadirs = {
 		'linux': [gc.home_dir,'.bitcoin-bchn'],
+		'darwin': [gc.home_dir, 'Library', 'Application Support', 'Bitcoin-Cash-Node'],
 		'win32': [os.getenv('APPDATA'),'Bitcoin-Cash-Node']
 	}
 
@@ -162,5 +163,6 @@ class litecoin_core_daemon(bitcoin_core_daemon):
 	cfg_file_hdr = '# Litecoin Core config file\n'
 	datadirs = {
 		'linux': [gc.home_dir,'.litecoin'],
+		'darwin': [gc.home_dir, 'Library', 'Application Support', 'Litecoin'],
 		'win32': [os.getenv('APPDATA'),'Litecoin']
 	}

+ 6 - 3
mmgen/proto/eth/daemon.py

@@ -63,12 +63,14 @@ class openethereum_daemon(ethereum_daemon):
 	cfg_file = 'parity.conf'
 	datadirs = {
 		'linux': [gc.home_dir,'.local','share','io.parity.ethereum'],
+		'darwin': [gc.home_dir, 'Library', 'Application Support', 'io.parity.ethereum'],
 		'win32': [os.getenv('LOCALAPPDATA'),'Parity','Ethereum']
 	}
 
 	def init_subclass(self):
 
-		ld = self.platform == 'linux' and not self.opt.no_daemonize
+		self.use_pidfile = self.platform == 'linux' and not self.opt.no_daemonize
+		self.use_threads = self.platform in ('win32', 'darwin')
 
 		self.coind_args = list_gen(
 			['--no-ws'],
@@ -81,8 +83,8 @@ class openethereum_daemon(ethereum_daemon):
 			['--config=dev', self.network=='regtest'], # no presets for mainnet or testnet
 			['--mode=offline', self.test_suite or self.network=='regtest'],
 			[f'--log-file={self.logfile}', self.non_dfl_datadir],
-			['daemon', ld],
-			[self.pidfile, ld],
+			['daemon', self.use_pidfile],
+			[self.pidfile, self.use_pidfile],
 		)
 
 class parity_daemon(openethereum_daemon):
@@ -104,6 +106,7 @@ class geth_daemon(ethereum_daemon):
 	version_info_arg = 'version'
 	datadirs = {
 		'linux': [gc.home_dir,'.ethereum','geth'],
+		'darwin': [gc.home_dir, 'Library', 'Ethereum', 'geth'],
 		'win32': [os.getenv('LOCALAPPDATA'),'Geth'] # FIXME
 	}
 

+ 7 - 2
mmgen/proto/xmr/daemon.py

@@ -12,7 +12,7 @@
 proto.xmr.daemon: Monero base protocol daemon classes
 """
 
-import os
+import sys, os
 
 from ...cfg import gc
 from ...util import list_gen,die,contains_any
@@ -28,6 +28,7 @@ class monero_daemon(CoinDaemon):
 	cfg_file = 'bitmonero.conf'
 	datadirs = {
 		'linux': [gc.home_dir,'.bitmonero'],
+		'darwin': [gc.home_dir,'.bitmonero'],
 		'win32': ['/','c','ProgramData','bitmonero']
 	}
 
@@ -53,6 +54,8 @@ class monero_daemon(CoinDaemon):
 			test_connection = False,
 			daemon = self )
 
+		self.use_pidfile = sys.platform == 'linux'
+
 		self.shared_args = list_gen(
 			['--no-zmq'],
 			[f'--p2p-bind-port={self.p2p_port}', self.p2p_port],
@@ -64,7 +67,7 @@ class monero_daemon(CoinDaemon):
 			['--hide-my-port'],
 			['--no-igd'],
 			[f'--data-dir={self.datadir}', self.non_dfl_datadir],
-			[f'--pidfile={self.pidfile}', self.platform == 'linux'],
+			[f'--pidfile={self.pidfile}', self.use_pidfile],
 			['--detach',                  not (self.opt.no_daemonize or self.platform=='win32')],
 			['--offline',                 not self.opt.online],
 		)
@@ -132,6 +135,8 @@ class MoneroWalletDaemon(RPCDaemon):
 		self.pidfile = os.path.join(self.datadir,id_str+'.pid')
 		self.logfile = os.path.join(self.datadir,id_str+'.log')
 
+		self.use_pidfile = sys.platform == 'linux'
+
 		self.proxy = proxy
 		self.monerod_addr = monerod_addr
 		self.monerod_port = (

+ 1 - 1
mmgen/proto/xmr/rpc.py

@@ -91,7 +91,7 @@ class MoneroRPCClient(RPCClient):
 		),json_rpc=False)
 
 	async def do_stop_daemon(self,silent=False):
-		return self.call_raw('stop_daemon')
+		return self.call_raw('stop_daemon') # unreliable on macOS (daemon stops, but closes connection)
 
 	rpcmethods = ( 'get_info', )
 	rpcmethods_raw = ( 'get_height', 'send_raw_transaction', 'stop_daemon' )

+ 1 - 1
test/cmdtest_py_d/ct_wallet.py

@@ -172,7 +172,7 @@ class CmdTestWalletConv(CmdTestBase,CmdTestShared):
 
 		b = VirtBlockDevice(os.path.join(self.tmpdir, 'hincog_blkdev_img'), '1K')
 		b.create()
-		b.attach(dev_mode='0666')
+		b.attach(dev_mode='0666' if sys.platform == 'linux' else None)
 		self.ref_hincog_conv_out(ic_f=b.dev)
 		b.detach()
 

+ 1 - 1
test/cmdtest_py_d/ct_xmrwallet.py

@@ -48,7 +48,7 @@ from .ct_base import CmdTestBase
 # atexit functions:
 def stop_daemons(self):
 	for v in self.users.values():
-		if '--restricted-rpc' in v.md.start_cmd:
+		if sys.platform == 'darwin' or '--restricted-rpc' in v.md.start_cmd:
 			v.md.stop()
 		else:
 			async_run(v.md_rpc.stop_daemon())

+ 30 - 0
test/include/common.py

@@ -421,3 +421,33 @@ class VirtBlockDeviceLinux(VirtBlockDeviceBase):
 
 	def do_detach(self, dev, check=True):
 		do_run(['/sbin/losetup', '-d', dev], check=check)
+
+class VirtBlockDeviceMacOS(VirtBlockDeviceBase):
+
+	def __init__(self, img_path, size):
+		self.img_path = Path(img_path + '.dmg').resolve()
+		self.size = size
+
+	def _get_associations(self):
+		cp = run(['hdiutil', 'info'], stdout=PIPE, stderr=PIPE, text=True, check=False)
+		if cp.returncode == 0:
+			lines = cp.stdout.splitlines()
+			out = [re.sub('.* ', '', s.strip()) for s in lines if re.match(r'image-path|/dev/', s)]
+			def gen_pairs():
+				for n in range(len(out) // 2):
+					yield(out[n*2], out[(n*2)+1])
+			return [dev for path, dev in gen_pairs() if path == str(self.img_path)]
+		else:
+			return []
+
+	def get_new_dev(self):
+		return None
+
+	def do_create(self, size, path):
+		do_run(['hdiutil', 'create', '-size', size, '-layout', 'NONE', str(path)])
+
+	def do_attach(self, path, dev=None):
+		do_run(['hdiutil', 'attach', '-mount', 'suppressed', str(path)])
+
+	def do_detach(self, dev, check=True):
+		do_run(['hdiutil', 'detach', dev], check=check)

+ 4 - 1
test/unit_tests_d/ut_rpc.py

@@ -240,7 +240,10 @@ class unit_tests:
 				await c.stop_daemon()
 
 				if not cfg.no_daemon_stop:
-					await md.rpc.stop_daemon()
+					if sys.platform == 'darwin':
+						md.stop()
+					else:
+						await md.rpc.stop_daemon()
 
 			gmsg('OK')