From 68c2d54c0b8f86b19574f314d063cd123b40a57a Mon Sep 17 00:00:00 2001 From: robk-tahoe Date: Tue, 22 Jan 2008 19:32:26 -0700 Subject: [PATCH] add mac native build This patch adds support for a mac native build. At the moment it's a fairly simple .app - i.e. so simple as to be unacceptable for a shipping product, but ok for testing and experiment at this point. notably once launched, the app's ui does not respond at all, although its dock icon does allow it to be force-quit. this produces a single .app bundle, which when run will look for a node basedir in ~/.tahoe. If one is not found, one will be created in ~/Library/Application Support/Allmydata Tahoe, and that will be symlinked to ~/.tahoe if the basedir is lacking basic config (introducer.furl and root_dir.cap) then the wx config wizard will be launched to log into an account and to set up those files. if a webport file is not found, the default value of 8123 will be written into it. once the node has started running, a webbrowser will be opened to the webish interface at the users root_dir note that, once configured, the node runs as the main thread of the .app, no daemonisation is done, twistd is not involved. the binary itself, from within the .app bundle, i.e. "Allmydata Tahoe.app/Contents/MacOS/Allmydata Tahoe" can be used from the command line and functions as the 'tahoe' executable would in a unix environment, with one exception - when launched with no args it triggers the default behaviour of running a node, and if necessary config wizard, as if the user had launched the .app one other gotcha to be aware of is that symlinking to this binary from some other place in ones $PATH will most likely not work. when I tried this, something - wx I believe - exploded, since it seems to use argv[0] to figure out where necessary libraries reside and fails if argv[0] isn't in the .app bundle. it's pretty easy to set up a script a la #!/bin/bash /Blah/blah/blah/Allmydata\ Tahoe.app/Contents/MacOS/Allmydata\ Tahoe "${@}" --- Makefile | 15 ++ mac/Makefile | 29 +++ mac/allmydata.icns | Bin 0 -> 22117 bytes mac/allmydata_tahoe.py | 95 ++++++++++ mac/amdicon.py | 69 +++++++ mac/confwiz.py | 407 +++++++++++++++++++++++++++++++++++++++++ mac/depends.py | 27 +++ mac/pkgreshook.py | 46 +++++ mac/setup.py | 61 ++++++ 9 files changed, 749 insertions(+) create mode 100644 mac/Makefile create mode 100644 mac/allmydata.icns create mode 100644 mac/allmydata_tahoe.py create mode 100644 mac/amdicon.py create mode 100644 mac/confwiz.py create mode 100644 mac/depends.py create mode 100644 mac/pkgreshook.py create mode 100644 mac/setup.py diff --git a/Makefile b/Makefile index df7ea567..777ae667 100644 --- a/Makefile +++ b/Makefile @@ -420,4 +420,19 @@ windows-installer-upload: chmod -R o+rx windows/dist/installer rsync -av -e /usr/bin/ssh windows/dist/installer/ amduser@dev:/home/amduser/public_html/dist/tahoe/windows/ +# These targets provide for mac native builds +.PHONY: mac-exe mac-upload mac-cleanup mac-dbg + +mac-exe: .built + $(MAKE) -C mac clean + VERSION=$(VER) $(PP) $(MAKE) -C mac build + +mac-dist: + VERSION=$(VER) $(MAKE) -C mac diskimage upload + +mac-cleanup: + VERSION=$(VER) $(MAKE) -C mac cleanup + +mac-dbg: + cd mac && $(PP) $(PYTHON)w allmydata_tahoe.py diff --git a/mac/Makefile b/mac/Makefile new file mode 100644 index 00000000..d280c3be --- /dev/null +++ b/mac/Makefile @@ -0,0 +1,29 @@ + +.PHONY: all dist build diskimage upload cleanup clean + +all: build + +dist: build diskimage upload cleanup + +build: + python setup.py py2app --no-chdir + +diskimage: + mv dist Allmydata-$(VERSION) + hdiutil create -ov -srcfolder Allmydata-$(VERSION) allmydata-rw.dmg + hdiutil convert -ov allmydata-rw.dmg -format UDRO -o allmydata-ro.dmg + hdiutil convert -ov allmydata-ro.dmg -format UDZO -o Allmydata-$(VERSION).dmg + hdiutil internet-enable -yes Allmydata-$(VERSION).dmg + rm -r Allmydata-$(VERSION) allmydata-rw.dmg allmydata-ro.dmg + +upload: + mkdir $(VERSION) + mv Allmydata-$(VERSION).dmg $(VERSION)/ + chmod go+rx $(VERSION)/ + rsync -av $(VERSION) amduser@svn.allmydata.com:/home/amduser/public_html/dist/tahoe/mac/ + +cleanup: + rm -r $(VERSION) + +clean: + rm -rf build dist diff --git a/mac/allmydata.icns b/mac/allmydata.icns new file mode 100644 index 0000000000000000000000000000000000000000..b7ab064d7d0a4d405a01eaeebabf6caac77bf21b GIT binary patch literal 22117 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\xf3)\x9e\x9aO\xf1\xfdlf\xd7\ +\xd8\xf9\x89\xcbl\\\xef\x0e\x06\xe1W\x03\xa1\x1b\x1d|&\xf6.\xee~:T\xfao\xe2\ +\x8fi\\\xfci\x04\x7f\x15\x9e\x03:\x03\xa4!Q\xd3\x8c\xc1\xde>|\xd8z\xb6\xa4w\ +\xe6\xdb\xcf\x90\x113\xb0\xfbg\xfdk\xa2\\\xea\xc7\x9fL\xf1\x92\xbd\xc8K\xf6"\ +\x8f\xa4o\xb16\xd8\xc3\x91\xd1\xcem\xd1EF;Y\x1b\xec\xe1\x91\xf4\xad\x92\xee\ +\xf8\x93\xa9@K\xa1\xfb\xc1\xc5\xab\xe2\xe8ij\x83$\x81\x97\xc5W\xb8to\x0cf\ +\xb4\xb0\xab\xd2\xad\xbe\x8cfF\x0b\xb8to\x0c/\x8b\xaf I\xa0\xa7\xa9\r\xf1\ +\xea\xb8\xff\x0e\x00\xbc\x01B7:\xf8\x9dD\x12\x92\x05$\x0b|\xf7p\x12y\xe4<\ +\xb7\x99\xd5\x97\xd1\xf2\xc8\xe1\xc7\xc7\xd3%\x9bd\xfd1\x84\x87\x93\xbc\'\ +\x00\x008z \x06I\x02\x92\x04rb\x15\x9a\xa1|\xa3\xd1\x0c\x85\xf9\x95l\xc9\xa6\ +\xbd>\x01&\xff\x12\x08\xf9\r:\xec@\x92(\xfd\x0e"\x95\xda\x94\x01\xb0\xb78\ +\xb3\x03\x02Tf\xe3\t\xa0\xa4\t\x87\x1d\x08*\x02\x00\xea\xa3Qt\x1e\xec\x00\ +\xbe\xe9\xf0]\xd3\xfah\xb4d\xe3\xb0\x03%M_\x00\xd7>\xa0\r\x9e\xe2/\xce~\x82\ +\x0f\xdaO\x96\x8d\xa0\x9c\xdc\x9d{\x80\xab\xd3?\x80/\xa6]\x8b\xc15\x03-\x07\ +\xe3\xe8mm/\xa5\xf2M\xa4\xb7\xb5\x1d\xdf\xff\x16\xc7S\xa4]\xc7]\x01\xaa\xcd\ +\xf0\xbeL\xbe\xd5\x9f\x97\xb8\x020\x13\xc4>\x020Se\x00\x04\x82do\x80\xb9\xec\ +\x02&\x1feP\xb0,\xd4\x98&\xde\xef8\x81\xf6\xc6\x06O}B\xa5\x00L\xa5J\xde)\x0f\ +\x9e\xfd\x83\xaf\x7f\xfe\x05\xe1\xda"\xec\xfeY-<\x9c\xe4\xfb\x93\x7f\xe2\xf3\ +s\xef\xe1\xe4\xb1\xa3\xee\x00{\xca\x80\xcb\x12,;K\xb8\x9dJo;\x0f\xec\xfeY\ +\xcd\x1c9\xc1\xb7S\xbf\xe2\xad\x06\x0bu\xa1\x18\xc2\xba\xb9\xcb\xdf\x1e\x00$\ +\x14;X\xb0\xb3\xf8\xd7z\x81U\xb5\x02b\x85e\xa1\x83\xbf\xdc}\x18\xd9\x03\xd5<\ +\x99\x1b\x07\x81P\xa5W\xe3p\xf8\x08\x8eW\xb5\xe1\x88\x19\xaf\x1c\x80\x99\xf0\ +B>\xc3\xbc|\x0c\x8b,\x10\x14\x14\x13\x88\x15\x98j\\\x1d1i\x90,A\xacP\xa0u,\ +\xda\x0bx\xb8\x9e\xc6a\xb3\x11k\x8ew}\xb8\x1eFy\xb5\x86\xb9\xe2\xef(\xa8\xf5\ +\xcd\x83E\xc2\xa2\x8d\x13\xae\xb1Y\xc0\x18\xea\xda\xd6\r\x8d\xa1.\x8e5\xadl\ +\xeaHX$7\xecX\xe2\xb9x\x8aU\xb5R\x19\xc0\xeb~.Y\xc2\xe2\xff\x9dY$\xd1p|\t\ +\xb5Q\xa7\x04a\x0cuq\xa4V\xa2\xae\xe5\xefM\x1d\x01\x8b$\x1c\xb672\x06\xdf\ +\xce\xed]\x03\x16I((\x10+\x106\xd2\xafX\x81\x0cB]3!\xff\xa8\x11\x00\xc0\xac\ +\xe1@"\x0bG/\x80\xc9\x7f\xb2\xe0\x00\xbc\xd1\x07\x88is\xfd\xb7C\xd8\xac\xef\ +\xd2/\x17iE\x00\xbc3\x03L\xdb\xb2\xe1\x90wk\xdd\x17\x00\x02C\x92\x00\x816\ +\xa3W`\xe6=GY9\x00\x13,\x96\xbe\xa9%+\x04m\xa0\x9b\xc9\xaa\xec%\x16\x08\x80\ +\xc1P\xec}\xff\x8b\xd4\xad"q.\xf5F\x13\xbf\x16\xd7mHN\xf0\x07k\x10\xf1\xf3\ +\xe7\n\xa0\xa4\x89\xe2bl_&/.\xc6\xa0D\xc4s\xdc\xf3i\xa6\rt\xb3Q%\xa1\x87\xfc\ +\xaf\xe2~B\x8e\x01%M\xcf\xeb\x18\x00\xfc\x07\x9aY\xdf_&\xed\xfd\xe8\x00\x00\ +\x00\x00IEND\xaeB`\x82' + +def getBitmap(): + return BitmapFromImage(getImage()) + +def getImage(): + stream = cStringIO.StringIO(getData()) + return ImageFromStream(stream) + +def getIcon(): + icon = EmptyIcon() + icon.CopyFromBitmap(getBitmap()) + return icon + + diff --git a/mac/confwiz.py b/mac/confwiz.py new file mode 100644 index 00000000..6860df5f --- /dev/null +++ b/mac/confwiz.py @@ -0,0 +1,407 @@ + +BACKEND_URL = 'https://www-test.allmydata.com/native_client.php' +#REGISTER_PAGE = 'https://www-test.allmydata.com/register' +TAHOESVC_NAME = 'Tahoe' +WINFUSESVC_NAME = 'Allmydata Tahoe SMB' + +import os +import sys +#import time +import traceback +import urllib2 +from urllib import urlencode +#import webbrowser +import wx + +from allmydata.util.assertutil import precondition +from allmydata import uri + +import amdicon + + +class AuthError(Exception): + pass + +def unicode_to_utf8(uobj): + assert precondition(isinstance(uobj, unicode)) + return uobj.encode('utf-8') + + +def post(url, args): + argstr = urlencode(args) + conn = urllib2.urlopen(url, argstr) + return conn.read() + +def get_root_cap(url, user, passwd): + args = { + 'action': 'authenticate', + 'email': unicode_to_utf8(user), + 'passwd': unicode_to_utf8(passwd), + } + root_cap = post(url, args) + if root_cap == '0': + raise AuthError() + elif not uri.is_uri(root_cap): + raise ValueError('%r is not a URI' % (root_cap,)) + else: + return root_cap + +def create_account(url, user, passwd, subscribe): + args = { + 'action': 'create_account', + 'email': unicode_to_utf8(user), + 'passwd': unicode_to_utf8(passwd), + 'subscribe': subscribe and 'true' or 'false', + } + result_code = post(url, args) + return result_code + +def get_introducer_furl(url): + return post(url, { 'action': 'getintroducerfurl' }) + +def write_config_file(filename, contents): + if sys.platform == 'win32': + from allmydata.windows import registry + basedir = registry.get_base_dir_path() + else: + basedir = os.path.expanduser('~/.tahoe') + path = os.path.join(basedir, filename) + dirname = os.path.dirname(path) + if not os.path.exists(dirname): + os.makedirs(dirname) + iff = file(path, 'wb') + iff.write(contents) + iff.close() + + +def DisplayTraceback(message): + xc = traceback.format_exception(*sys.exc_info()) + wx.MessageBox(u"%s\n (%s)"%(message,''.join(xc)), 'Error') + +class ConfWizApp(wx.App): + def __init__(self): + wx.App.__init__(self, 0) + + def OnInit(self): + try: + wx.InitAllImageHandlers() + + self.login_frame = LoginFrame(self) + self.login_frame.CenterOnScreen() + self.SetTopWindow(self.login_frame) + #self.SetExitOnFrameDelete(True) + self.login_frame.Show(True) + + return True + except: + DisplayTraceback('config wizard init threw an exception') + + def swap_to_register_frame(self): + try: + self.login_frame.Show(False) + self.regiser_frame = RegisterFrame(self) + self.regiser_frame.CenterOnScreen() + self.SetTopWindow(self.regiser_frame) + self.SetExitOnFrameDelete(True) + self.regiser_frame.Show(True) + except: + DisplayTraceback('config wizard threw an exception') + +class LoginFrame(wx.Frame): + def __init__(self, app): + title = 'Allmydata Tahoe Config Wizard' + wx.Frame.__init__(self, None, -1, title) + self.app = app + self.SetSizeHints(100, 100, 600, 800) + self.SetIcon(amdicon.getIcon()) + self.Bind(wx.EVT_CLOSE, self.close) + + background = wx.Panel(self, -1) + background.parent = self + self.login_panel = LoginPanel(background) + self.reg_btn_panel = RegisterButtonPanel(background, app) + sizer = wx.BoxSizer(wx.VERTICAL) + background_sizer = wx.BoxSizer(wx.VERTICAL) + background_sizer.Add(self.login_panel, 1, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 26) + background_sizer.Add(self.reg_btn_panel, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 26) + background.SetSizer(background_sizer) + sizer.Add(background, 0, wx.EXPAND | wx.ALL, 0) + self.SetSizer(sizer) + self.SetAutoLayout(True) + self.Fit() + self.Layout() + + def close(self, event): + self.Show(False) + self.app.ExitMainLoop() + +class RegisterFrame(wx.Frame): + def __init__(self, app): + title = 'Allmydata Tahoe Config Wizard' + wx.Frame.__init__(self, None, -1, title) + self.app = app + self.SetSizeHints(100, 100, 600, 800) + self.SetIcon(amdicon.getIcon()) + self.Bind(wx.EVT_CLOSE, self.close) + + background = wx.Panel(self, -1) + background.parent = self + self.register_panel = RegisterPanel(background) + sizer = wx.BoxSizer(wx.VERTICAL) + background_sizer = wx.BoxSizer(wx.VERTICAL) + background_sizer.Add(self.register_panel, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 26) + background.SetSizer(background_sizer) + sizer.Add(background, 0, wx.EXPAND | wx.ALL, 0) + self.SetSizer(sizer) + self.SetAutoLayout(True) + self.Fit() + self.Layout() + + def close(self, event): + self.Show(False) + self.app.ExitMainLoop() + + +class LoginPanel(wx.Panel): + def __init__(self, parent): + wx.Panel.__init__(self, parent, -1) + self.parent = parent + + self.sizer = wx.BoxSizer(wx.VERTICAL) + + self.user_label = wx.StaticText(self, -1, 'Email') + self.pass_label = wx.StaticText(self, -1, 'Password') + self.user_field = wx.TextCtrl(self, -1, u'', size=(260,-1)) + self.pass_field = wx.TextCtrl(self, -1, u'', size=(260,-1), style=wx.TE_PASSWORD) + self.login_button = wx.Button(self, -1, 'Sign In') + self.warning_label = wx.StaticText(self, -1, '') + self.warning_label.SetOwnForegroundColour(wx.RED) + wx.EVT_CHAR(self.user_field, self.on_user_entry) + wx.EVT_CHAR(self.pass_field, self.on_pass_entry) + self.Bind(wx.EVT_BUTTON, self.on_login, self.login_button) + login_sizer = wx.FlexGridSizer(3, 2, 5, 4) + login_sizer.Add(self.user_label, 0, wx.ALIGN_RIGHT | wx.ALL, 2) + login_sizer.Add(self.user_field, 0, wx.EXPAND | wx.ALL, 2) + login_sizer.Add(self.pass_label, 0, wx.ALIGN_RIGHT | wx.ALL, 2) + login_sizer.Add(self.pass_field, 0, wx.EXPAND | wx.ALL, 2) + login_sizer.Add(wx.Size(2,2), 0, wx.ALIGN_RIGHT | wx.ALL, 2) + login_sizer.Add(self.login_button, 0, wx.ALIGN_RIGHT | wx.ALL, 2) + self.sizer.Add(login_sizer, 1, wx.EXPAND | wx.ALL, 2) + self.sizer.Add(self.warning_label, 0, wx.CENTER | wx.ALL, 2) + self.SetSizer(self.sizer) + self.SetAutoLayout(True) + + def on_user_entry(self, event): + if event.GetKeyCode() == wx.WXK_RETURN: + self.pass_field.SetFocus() + else: + event.Skip() + + def on_pass_entry(self, event): + if event.GetKeyCode() == wx.WXK_RETURN: + self.on_login(event) + else: + event.Skip() + + def on_login(self, event): + user = self.user_field.GetValue() + passwd = self.pass_field.GetValue() + self.warning_label.SetLabel('Connecting...') + self.Layout() + wx.Yield() + + if passwd == '': + self.warning_label.SetLabel('You must enter a password') + self.pass_field.SetFocus() + self.Layout() + return + + try: + root_cap = get_root_cap(BACKEND_URL, user, passwd) + write_config_file('private/root_dir.cap', root_cap+'\n') + except AuthError: + self.warning_label.SetLabel('Your email and/or password is incorrect') + self.user_field.SetFocus() + self.Layout() + return + + # fetch the introducer furl + ifurl = get_introducer_furl(BACKEND_URL) + write_config_file('introducer.furl', ifurl+'\n') + + # start service etc. + if sys.platform == 'win32': + self.start_windows_service(TAHOESVC_NAME) + self.start_windows_service(WINFUSESVC_NAME) + + # exit + self.parent.parent.Close() + + def start_windows_service(self, svc_name): + try: + import win32service + import win32serviceutil as wsu + if wsu.QueryServiceStatus(svc_name)[1] != win32service.SERVICE_RUNNING: + wsu.StartService(svc_name) + except: + DisplayTraceback('Failed to start windows service "%s"' % (svc_name,)) + +class RegisterButtonPanel(wx.Panel): + def __init__(self, parent, app): + wx.Panel.__init__(self, parent, -1) + self.parent = parent + self.app = app + + self.sizer = wx.BoxSizer(wx.VERTICAL) + + self.reg_label = wx.StaticText(self, -1, "Don't have an account?") + self.reg_button = wx.Button(self, -1, 'Create Account') + self.Bind(wx.EVT_BUTTON, self.on_reg_button, self.reg_button) + reg_sizer = wx.FlexGridSizer(1, 2, 5, 4) + reg_sizer.Add(self.reg_label, 0, wx.ALIGN_RIGHT | wx.ALL, 2) + reg_sizer.Add(self.reg_button, 0, wx.ALIGN_RIGHT | wx.ALL, 2) + self.sizer.Add(reg_sizer, 1, wx.EXPAND | wx.ALL, 2) + self.SetSizer(self.sizer) + self.SetAutoLayout(True) + + def on_reg_button(self, event): + #webbrowser.open(REGISTER_PAGE) + self.app.swap_to_register_frame() + +class RegisterPanel(wx.Panel): + def __init__(self, parent): + wx.Panel.__init__(self, parent, -1) + self.parent = parent + + self.sizer = wx.BoxSizer(wx.VERTICAL) + + self.user_label = wx.StaticText(self, -1, 'Email') + self.pass_label = wx.StaticText(self, -1, 'Password') + self.conf_label = wx.StaticText(self, -1, 'Confirm Password') + self.user_field = wx.TextCtrl(self, -1, u'', size=(260,-1)) + self.pass_field = wx.TextCtrl(self, -1, u'', size=(260,-1), style=wx.TE_PASSWORD) + self.conf_field = wx.TextCtrl(self, -1, u'', size=(260,-1), style=wx.TE_PASSWORD) + self.create_account_button = wx.Button(self, -1, 'Create Account') + self.subscribe_box = wx.CheckBox(self, -1, 'Sign up for our Newsletter') + self.subscribe_box.SetValue(True) + self.warning_label = wx.StaticText(self, -1, '') + self.warning_label.SetOwnForegroundColour(wx.RED) + wx.EVT_CHAR(self.user_field, self.on_user_entry) + wx.EVT_CHAR(self.pass_field, self.on_pass_entry) + wx.EVT_CHAR(self.conf_field, self.on_conf_entry) + self.Bind(wx.EVT_BUTTON, self.on_create_account, self.create_account_button) + login_sizer = wx.FlexGridSizer(4, 2, 5, 4) + login_sizer.Add(self.user_label, 0, wx.ALIGN_RIGHT | wx.ALL, 2) + login_sizer.Add(self.user_field, 0, wx.EXPAND | wx.ALL, 2) + login_sizer.Add(self.pass_label, 0, wx.ALIGN_RIGHT | wx.ALL, 2) + login_sizer.Add(self.pass_field, 0, wx.EXPAND | wx.ALL, 2) + login_sizer.Add(self.conf_label, 0, wx.ALIGN_RIGHT | wx.ALL, 2) + login_sizer.Add(self.conf_field, 0, wx.EXPAND | wx.ALL, 2) + login_sizer.Add(wx.Size(2,2), 0, wx.ALIGN_RIGHT | wx.ALL, 2) + login_sizer.Add(self.create_account_button, 0, wx.ALIGN_RIGHT | wx.ALL, 2) + self.sizer.Add(login_sizer, 1, wx.EXPAND | wx.ALL, 2) + self.sizer.Add(self.warning_label, 0, wx.CENTER | wx.ALL, 2) + self.sizer.Add(self.subscribe_box, 0, wx.CENTER | wx.ALL, 2) + self.SetSizer(self.sizer) + self.SetAutoLayout(True) + + def on_user_entry(self, event): + if event.GetKeyCode() == wx.WXK_RETURN: + self.pass_field.SetFocus() + else: + event.Skip() + + def on_pass_entry(self, event): + if event.GetKeyCode() == wx.WXK_RETURN: + self.conf_field.SetFocus() + else: + event.Skip() + + def on_conf_entry(self, event): + if event.GetKeyCode() == wx.WXK_RETURN: + self.on_create_account(event) + else: + event.Skip() + + def on_create_account(self, event): + user = self.user_field.GetValue() + passwd = self.pass_field.GetValue() + pconf = self.conf_field.GetValue() + subscribe = self.subscribe_box.IsChecked() + self.warning_label.SetLabel('Connecting...') + self.Layout() + wx.Yield() + + if passwd == '': + self.warning_label.SetLabel('You must enter a password') + self.pass_field.SetFocus() + self.Layout() + return + + if passwd != pconf: + self.warning_label.SetLabel("Passwords don't match") + self.pass_field.SetValue('') + self.conf_field.SetValue('') + self.pass_field.SetFocus() + self.Layout() + return + + #print 'calling create_account', time.asctime() + result_code = create_account(BACKEND_URL, user, passwd, subscribe) + + if result_code == 'account_exists': + # try and log into it; if valid, use it anyway + try: + #print 'calling get_root_cap (ae)', time.asctime() + root_cap = get_root_cap(BACKEND_URL, user, passwd) + write_config_file('private/root_dir.cap', root_cap+'\n') + except AuthError: + self.warning_label.SetLabel('That email address is already registered') + self.user_field.SetFocus() + self.Layout() + return + elif result_code == 'error': + self.warning_label.SetLabel('an error occurred') + self.user_field.SetFocus() + self.Layout() + return + elif result_code == 'ok': + #print 'calling get_root_cap (ok)', time.asctime() + root_cap = get_root_cap(BACKEND_URL, user, passwd) + write_config_file('private/root_dir.cap', root_cap+'\n') + else: + self.warning_label.SetLabel('an unexpected error occurred ("%s")' % (result_code,)) + self.user_field.SetFocus() + self.Layout() + return + + # fetch the introducer furl + #print 'calling get_introducer_furl', time.asctime() + ifurl = get_introducer_furl(BACKEND_URL) + write_config_file('introducer.furl', ifurl+'\n') + + # start service etc. + if sys.platform == 'win32': + self.start_windows_service(TAHOESVC_NAME) + self.start_windows_service(WINFUSESVC_NAME) + + # exit + self.parent.parent.Close() + + def start_windows_service(self, svc_name): + try: + import win32service + import win32serviceutil as wsu + if wsu.QueryServiceStatus(svc_name)[1] != win32service.SERVICE_RUNNING: + wsu.StartService(svc_name) + except: + DisplayTraceback('Failed to start windows service "%s"' % (svc_name,)) + + +def main(): + app = ConfWizApp() + app.MainLoop() + + +if __name__ == '__main__': + main() diff --git a/mac/depends.py b/mac/depends.py new file mode 100644 index 00000000..6edc850f --- /dev/null +++ b/mac/depends.py @@ -0,0 +1,27 @@ + +# nevow requires all these for its voodoo module import time adaptor registrations +from nevow import accessors, appserver, static, rend, url, util, query, i18n, flat +from nevow import guard, stan, testutil, context +from nevow.flat import flatmdom, flatstan, twist +from formless import webform, processors, annotate, iformless +from decimal import Decimal + + +#if sys.platform in ['darwin', ]: +from nevow.flat import flatsax +from xml.parsers import expat +from xml.sax import expatreader, sax2exts +from xml.sax.drivers2 import drv_pyexpat, drv_xmlproc +junk = [ flatsax, expat, expatreader, sax2exts, drv_pyexpat, drv_xmlproc, ] + + + +import allmydata.web + +# junk to appease pyflakes's outrage at py2exe's needs +junk = [ + accessors, appserver, static, rend, url, util, query, i18n, flat, guard, stan, testutil, + context, flatmdom, flatstan, twist, webform, processors, annotate, iformless, Decimal, + allmydata, + ] + diff --git a/mac/pkgreshook.py b/mac/pkgreshook.py new file mode 100644 index 00000000..61233778 --- /dev/null +++ b/mac/pkgreshook.py @@ -0,0 +1,46 @@ + +def install(): + """ + This installs a hook into setuptools' pkg_resources infrastructure, so that resource + files can be found in files relative to the runnin executable, in addition to the + usual egg and source lookup mechanisms. This overrides the ZipProvider, since that + is the lookup mechanism triggered within pkg_resources when running code out of a + py2exe or py2app build's library.zip. + """ + import os, sys + import pkg_resources, zipimport + + platform_libdirs = { + 'darwin': '../Resources/pkg_resources', + } + exedir = os.path.dirname(sys.executable) + libdir = platform_libdirs.get(sys.platform, 'pkg_resources') + + class Provider(pkg_resources.ZipProvider): + + def __init__(self, module): + self._module_name = module.__name__ + pkg_resources.ZipProvider.__init__(self, module) + + def get_resource_filename(self, manager, resource_name): + #print 'get_resource_filename(%s, %s)' % (manager, resource_name) + path = [exedir, libdir] + self._module_name.split('.') + [resource_name] + localfile = os.path.join(*path) + #print ' checking(%s)' % (localfile,) + if os.path.exists(localfile): + #print 'found locally' + return localfile + else: + try: + ret = pkg_resources.ZipProvider.get_resource_filename(self, manager, resource_name) + #print 'returning %s' % (ret,) + return ret + except NotImplementedError: + print 'get_resource_filename(%s,%s): not found' % (self._module_name, resource_name) + import traceback + traceback.print_exc() + return '' + + pkg_resources.register_loader_type(zipimport.zipimporter, Provider) + + diff --git a/mac/setup.py b/mac/setup.py new file mode 100644 index 00000000..bf8cf299 --- /dev/null +++ b/mac/setup.py @@ -0,0 +1,61 @@ +from setuptools import setup +import py2app + +import glob +import os +import sys + +# pull in formless, as best way to grab its .css file depenedency +import formless + +def find_formless_css(): + fpath = formless.__path__[0] + # first look for it from a regular package install + f = os.path.join(fpath, 'freeform-default.css') + if os.path.exists(f): + return f + # then try looking within .egg structured files + pyver = 'python%s.%s' % (sys.version_info[0], sys.version_info[1]) + f = os.path.join(fpath, '../lib', pyver, 'site-packages/formless/freeform-default.css') + if os.path.exists(f): + return f + raise RuntimeError("Can't find formless .css file") + +data_files = [ + ('pkg_resources/allmydata/web', glob.glob('../src/allmydata/web/*')), + ('pkg_resources/formless', [find_formless_css()]), + ] + +from setuptools import find_packages + +packages = find_packages('../src') + +py2app_options = { + 'argv_emulation': True, + 'iconfile': 'allmydata.icns', + 'plist': { 'CFBundleIconFile': 'allmydata.icns', }, + } + +setup_args = { + 'name': 'Allmydata Tahoe', + 'description': 'The various parts of the Allmydata Tahoe system', + 'author': 'Allmydata, Inc.', + 'app': [ 'allmydata_tahoe.py' ], + 'options': { 'py2app': py2app_options }, + 'data_files': data_files, + 'setup_requires': [ 'py2app' ], + 'packages': packages, +} + + +if __name__ == '__main__': + if not os.path.exists('allmydata'): + os.symlink('../src/allmydata', 'allmydata') + setup(**setup_args) + +junk = [formless, py2app] +del junk + + + + -- 2.45.2