source: trunk/makeMacApp.py @ 4736

Last change on this file since 4736 was 4736, checked in by toby, 10 months ago

more install work

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 9.0 KB
Line 
1#!/usr/bin/env python
2'''
3*makeMacApp: Create Mac Applet*
4===============================
5
6This script creates an AppleScript app bundle to launch GSAS-II. The app is
7created in the directory where the GSAS-II script (.../GSASII/GSASII.py)
8is located. A softlink to Python is created inside that app bundle,
9but the softlink name is GSAS-II so that "GSAS-II" shows up as the name
10of the app in the menu bar, etc. rather than "Python". A soft link named
11GSAS-II.py, referencing the GSASII.py script, is created so that some file
12menu items also are labeled with GSAS-II (but not the right capitalization,
13alas).
14
15This has been tested with several versions of Python interpreters
16from Anaconda and does not require pythonw (Python.app). It tests to
17make sure that a wxpython script will run inside the app but if not,
18it searches for a pythonw image and tries that.
19
20Run this script with no arguments or with one or two arguments.
21The first argument, if supplied, is a reference to the GSASII.py script,
22which can have a relative or absolute path (the absolute path is determined).
23If not supplied, the GSASII.py script will be used from the directory where
24this (makeMacApp.py) script is found. The second argument, if supplied,
25provides the name for the app to be created. This can be used to create
26multiple copies of the app using different Python versions (likely use for
27development only).
28'''
29
30from __future__ import division, print_function
31import sys, os, os.path, stat, shutil, subprocess, plistlib
32def Usage():
33    print("\n\tUsage: python "+sys.argv[0]+" [<GSAS-II script>] [project]\n")
34    sys.exit()
35
36def RunPython(image,cmd):
37    'Run a command in a python image'
38    try:
39        err=None
40        p = subprocess.Popen([image,'-c',cmd],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
41        out = p.stdout.read()
42        err = p.stderr.read()
43        p.communicate()
44        return out,err
45    except Exception(err):
46        return '','Exception = '+err
47
48AppleScript = ''
49'''Contains an AppleScript to start GSAS-II, launching Python and the
50GSAS-II python script
51'''
52
53if __name__ == '__main__' and sys.platform == "darwin":
54    project="GSAS-II"
55
56    # set scriptdir: find the main GSAS-II script if not on command line
57    if len(sys.argv) == 1:
58        script = os.path.abspath(os.path.join(
59                os.path.split(__file__)[0],
60                "GSASII.py"
61                ))
62    elif len(sys.argv) == 2:
63        script = os.path.abspath(sys.argv[1])
64    elif len(sys.argv) == 3:
65        script = os.path.abspath(sys.argv[1])
66        project = sys.argv[2]
67    else:
68        Usage()
69        raise Exception
70    # make sure we found it
71    if not os.path.exists(script):
72        print("\nFile "+script+" not found")
73        Usage()
74    if os.path.splitext(script)[1].lower() != '.py':
75        print("\nScript "+script+" does not have extension .py")
76        Usage()
77    # where the app will be created
78    scriptdir = os.path.split(script)[0]
79   
80    appPath = os.path.abspath(os.path.join(scriptdir,project+".app"))
81    iconfile = os.path.join(scriptdir,'gsas2.icns') # optional icon file
82   
83    AppleScript = '''(*   GSAS-II AppleScript by B. Toby (brian.toby@anl.gov)
84     It can launch GSAS-II by double clicking or by dropping a data file
85     or folder over the app.
86     It runs GSAS-II in a terminal window.
87*)
88
89(* test if a file is present and exit with an error message if it is not  *)
90on TestFilePresent(appwithpath)
91        tell application "System Events"
92                if (file appwithpath exists) then
93                else
94                        display dialog "Error: file " & appwithpath & " not found. If you have moved this file recreate the AppleScript with bootstrap.py." with icon caution buttons {{"Quit"}}
95                        return
96                end if
97        end tell
98end TestFilePresent
99
100(*
101------------------------------------------------------------------------
102this section responds to a double-click. No file is supplied to GSAS-II
103------------------------------------------------------------------------
104*)
105on run
106        set python to "{:s}"
107        set appwithpath to "{:s}"
108        set env to "{:s}"
109        TestFilePresent(appwithpath)
110        TestFilePresent(python)
111        tell application "Terminal"
112                activate
113                do script env & python & " " & appwithpath & "; exit"
114        end tell
115end run
116
117(*
118-----------------------------------------------------------------------------------------------
119this section handles starting with files dragged into the AppleScript
120 o it goes through the list of file(s) dragged in
121 o then it converts the colon-delimited macintosh file location to a POSIX filename
122 o for every non-directory file dragged into the icon, it starts GSAS-II, passing the file name
123------------------------------------------------------------------------------------------------
124*)
125
126on open names
127        set python to "{:s}"
128        set appwithpath to "{:s}"
129        set env to "{:s}"
130 
131        TestFilePresent(appwithpath)
132        repeat with filename in names
133                set filestr to (filename as string)
134                if filestr ends with ":" then
135                        (* should not happen, skip directories *)
136                else
137                        (* if this is an input file, open it *)
138                        set filename to the quoted form of the POSIX path of filename
139                        tell application "Terminal"
140                                activate
141                                do script env & python & " " & appwithpath & " " & filename & "; exit"
142                        end tell
143                end if
144        end repeat
145end open
146'''
147    # create a link named GSAS-II.py to the script
148    newScript = os.path.join(scriptdir,'GSAS-II.py')
149    if os.path.exists(newScript): # cleanup
150        print("\nRemoving sym link",newScript)
151        os.remove(newScript)
152    os.symlink(os.path.split(script)[1],newScript)
153    script=newScript
154
155    # find Python used to run GSAS-II and set a new to use to call it
156    # inside the app that will be created
157    pythonExe = os.path.realpath(sys.executable)
158    newpython =  os.path.join(appPath,"Contents","MacOS",project)
159
160    # create an app using this python and if that fails to run wx, look for a
161    # pythonw and try that with wx
162    for i in 1,2,3:
163        if os.path.exists(appPath): # cleanup
164            print("\nRemoving old "+project+" app ("+str(appPath)+")")
165            shutil.rmtree(appPath)
166       
167        shell = os.path.join("/tmp/","appscrpt.script")
168        f = open(shell, "w")
169        f.write(AppleScript.format(newpython,script,'',newpython,script,''))
170        f.close()
171
172        try: 
173            subprocess.check_output(["osacompile","-o",appPath,shell],stderr=subprocess.STDOUT)
174        except subprocess.CalledProcessError as msg:
175            print('''Error compiling AppleScript.
176            Report the next message along with details about your Mac to toby@anl.gov''')
177            print(msg.output)
178            sys.exit()
179        # create a link to the python inside the app, if named to match the project
180        if pythonExe != newpython: os.symlink(pythonExe,newpython)
181
182        # test if newpython can run wx
183        testout,errout = RunPython(newpython,'import numpy; import wx; wx.App(); print("-OK-")')
184        if isinstance(testout,bytes): testout = testout.decode()
185        if "-OK-" in testout:
186            break
187        elif i == 1:
188            print('Run of wx in',pythonExe,'failed, looking for pythonw')
189            pythonExe = os.path.join(os.path.split(sys.executable)[0],'pythonw')
190            if not os.path.exists(pythonExe):
191                print('Warning no pythonw found with ',sys.executable,
192                      '\ncontinuing, hoping for the best')
193        elif i == 2:
194            print('Warning could not run wx with',pythonExe,
195                      'will try with that external to app')
196            newpython = pythonExe
197        else:
198            print('Warning still could not run wx with',pythonExe,
199                      '\ncontinuing, hoping for the best')
200
201    # change the icon
202    oldicon = os.path.join(appPath,"Contents","Resources","droplet.icns")
203    #if os.path.exists(iconfile) and os.path.exists(oldicon):
204    if os.path.exists(iconfile):
205        shutil.copyfile(iconfile,oldicon)
206
207    # Edit the app plist file to restrict the type of files that can be dropped
208    if hasattr(plistlib,'load'):
209        fp = open(os.path.join(appPath,"Contents",'Info.plist'),'rb')
210        d = plistlib.load(fp)
211        fp.close()
212    else:
213        d = plistlib.readPlist(os.path.join(appPath,"Contents",'Info.plist'))
214    d['CFBundleDocumentTypes'] = [{
215        'CFBundleTypeExtensions': ['gpx'],
216        'CFBundleTypeName': 'GSAS-II project',
217        'CFBundleTypeRole': 'Editor'}]
218   
219    if hasattr(plistlib,'dump'):
220        fp = open(os.path.join(appPath,"Contents",'Info.plist'),'wb')
221        plistlib.dump(d,fp)
222        fp.close()
223    else:
224        plistlib.writePlist(d,os.path.join(appPath,"Contents",'Info.plist'))
225
226    # open & save the file in the editor to help set authorization levels
227    osascript = '''
228    tell application "Script Editor"
229       activate
230       set MyName to open "{}"
231       save MyName
232       close MyName
233       quit
234    end tell   
235'''.format(appPath)
236    print(script)
237    subprocess.Popen(["osascript","-e",osascript])
238       
239    print("\nCreated "+project+" app ("+str(appPath)+
240          ").\nViewing app in Finder so you can drag it to the dock if, you wish.")
241    subprocess.call(["open","-R",appPath])
Note: See TracBrowser for help on using the repository browser.