ncssar / gpsio

GPSIO Browser Extension
GNU General Public License v2.0
8 stars 2 forks source link

Mac installer #27

Closed caver456 closed 2 years ago

caver456 commented 3 years ago

Wait to start this issue until the Windows installer is done.

caver456 commented 2 years ago
caver456 commented 2 years ago

making a .pkg - use built-in pkgbuild command line tool

http://thegreyblog.blogspot.com/2014/06/os-x-creating-packages-from-command_2.html

https://www.manpagez.com/man/1/pkgbuild/

https://macadmins.psu.edu/files/2019/07/psumac2019-345-Installer-Package-Scripting-Making-your-deployments-easier-one-at-a-time.pdf

caver456 commented 2 years ago

using "mdfind kMDItemKind == 'Application'" to get the list of what's installed, rather than just listing the /Applications folder. Wrapping it like so:

def getDarwinApps():    
    return str(subprocess.run(['mdfind',"kMDItemKind == 'Application'"],capture_output=True).stdout).split('\\n')

and then calling it like so:

gpsbabel_appdir=str([x for x in getDarwinApps() if 'GPSBabel' in x][0]) # sometimes it returns bytes!
        if gpsbabel_appdir:
            print('gpsbabel_appdir:'+str(gpsbabel_appdir))
            gpsbabel_exe=os.path.join(gpsbabel_appdir,'Contents','MacOS','gpsbabel')

sometimes, the result is a bytes array which causes failure:

gpsbabel_appdir:b'/Applications/GPSBabelFE.app
checking for: b'/Applications/GPSBabelFE.app/Contents/MacOS/gpsbabel
g1:[True, None]
2. GPSBabel : FAILED

but sometimes it's a string which passes:

gpsbabel_appdir:/Applications/GPSBabelFE.app
checking for: /Applications/GPSBabelFE.app/Contents/MacOS/gpsbabel
g1:[True, '/Applications/GPSBabelFE.app/Contents/MacOS/gpsbabel']
2. GPSBabel : previous installation verified

Can't figure out a pattern just yet. Would be best to figure out why this happens and resolve it; otherwise, the parser could just be made to deal with either string or bytes array (the str() in the code above obviously does not work)

caver456 commented 2 years ago

subprocess return is bytes; that's where the decoding has to happen. After that, escaping the \ in the split is not needed. This fixed it (note this fix does not close this issue):

def getDarwinApps():    
    return str(subprocess.run(['mdfind',"kMDItemKind == 'Application'"],capture_output=True).stdout,'utf-8').split('\n')
caver456 commented 2 years ago

so close... used pkgbuild (by sourcing installer/build_mac which assembles the files then calls pkgbuild). The pkg installer failed, showing a dialog saying that python3 isn't installed. Turns out macs ship with python2 rather than python3.

So, a few options to explore:

  1. change the code to use python2
  2. pyinstaller or equivalent (since the embeddable python installation isn't available for mac) - if the resulting executable isn't flagged by antivirus software on mac, like it is on Windows
  3. install python3 during the gpsio install process
caver456 commented 2 years ago

Good progress with pyinstaller:

Since support files are needed (gpsio-host.ini, gpsio-host-macfile.py, gpsio-osa1_template.txt, gpsio-osa2.txt) you need to build to a dist directory i.e. don't use the onefile option.

This is proof-of-concept - not polished.

  1. collect all the support files in one place (will want to get rid of this step, so the support files can be left in their platform branch dirs)
  2. build the .spec file below
  3. pyinstaller gpsio-host.spec (from the dir with the spec file and all the support files)
  4. edit gpsio-host.ini to use the correct location of gpsbabel (this step will be in the postinstall script)
  5. point the manifest to run 'gpsio-host' from the generated dist directory (also in the postinstall script)

Then it works!

gpsio-host.spec is generated initially from pyinstaller gpsio-host.py, then edited to include the support files:

datas=[('gpsio-host.ini', '.'),('gpsio-osa1_template.txt','.'),('gpsio-osa2.txt','.')],

This also causes permission request popups that are a bit more clear, and gpsio-host shows up as its own entry in System Preferences --> Security and Privacy --> Files and Folders --> Privacy --> Files and Folders which is great.

This brings up the question - can we maybe get rid of the osascripts, such that gpsio-host will ask for access to remote drives with a popup???

caver456 commented 2 years ago

promising: write a simple python script (test.py) to do os.listdir on the removable drive, then turn that into an executable with pyinstaller (no supporting files - just 'pyinstaller test.py') - then point the extension manifest to that executable. On the first attempt, it asks:

Screen Shot 2021-12-15 at 7 47 56 AM

and also asks:

Screen Shot 2021-12-15 at 7 48 12 AM

and it shows up in the security list:

Screen Shot 2021-12-15 at 7 58 03 AM

So - give it a shot - try commenting out the entire 'if darwin' clause in gpsio-host.py which invokes the osascripts etc. That would win.

The main possibility for failure here is if an antivirus flags the pyinstaller result as a threat in the same manner as what it does for Windows.

Guess what: virustotal.com shows ZERO out of 58 virus scan programs that flagged gpsio-host (executable) as a threat. And ZERO out of 59 for test (executable). Very interesting!

caver456 commented 2 years ago

This all appears to work nicely. Pushed a new version of the mac installer, the test of that installer on another mac should happen in the next few days.

caver456 commented 2 years ago

Other user's test failed again with the same symptom as the 'so close...' comment above - a dialog pops up during installation saying that python3 needs to be installed. Duh - the popup shows during installation, meaning that it's the postinstall script that needs python3. The work between then and now addressed the fact that the >host< needs its own python3 - which is true and important and resolved above with pyinstaller, so it wasn't wasted work - but the immediate symptom was that the >installer< needs python3. Duh.

The windows installer already accounts for this, running the embeddable python interpreter provided by the NSIS self-extracting-archive, since the NSIS installer calls this line for each installation stage:

!define py "$INSTDIR/host/dist/python.exe $INSTDIR/install-gpsio.py"

So we need to see if we can do something similar for mac.

Back-porting to python2 is an option but that would completely invalidate the current cross-platform installer script: no sense in back-porting the Windows install script to python2 since it's already running with the embeddable python3.

So, maybe try pyinstaller onefile to make the executable, and use that as the postinstall script?

caver456 commented 2 years ago

pyinstaller --onefile install-gpsio.py (then incorporating the resulting executable as the pkgbuild postinstall script) did the trick. The other user's test worked out. Closing this ticket. Note that the other user's test revealed a new issue #36.