File: pyedit-products/unzipped/build/build-app-exe/build3.0/macosx/build.py
#!/usr/bin/env python3 """ ============================================================================= Make a Mac OS X App bundle (folder), using py2app. Python recoding of an original shell-script version. Main file: run me in this folder to build the app. An app allows text files to be associated to auto-open in PyEdit on clicks; makes the program immune from Python changes; requires no Python installs; and better supports file drag-and-drops, program icons, and more (but also does a woefully-good job of hiding user-config source files and auto-saves!). Didn't get cx_freeze or pyinstaller to work, but stopped short... -pyinstaller had issues with Active Tcl/Tk, cx_freeze had xcode issues (?) -pyinstaller may be retried if use homebrew python/tk for menu/dock issues; For the build (only), moves PP4E pkg root (less TextEditor) to become nested in TextEditor, not above it. Both are moved to a temp build folder for simplicity here (and to avoid source-code tragedies). Mac app also required... tailoring apple/window menus, __main__ workaround for opendoc events, sys.path=., a mac-format .icns icon (the latter became an iconify.py extension), and more; see texEditor.py's "[3.0]" changes. Besides the executable, the app needs: -textConfig.py: must be source code, and user-visible and editable; -icon for the exe/app (window borders/app bar for Windows/Linux); -UserGuide.html and docetc items it uses at runtime; -docetc image for the Help dialog -subprocProxy.py for the Run Code's Capture-mode launcher py2app sets the cwd to the bundle's Contents/Resource folder in all cases: add '.' to sys.path in textEditor.py (now via importing fixfrozenpaths.py), and use files copied to .=Resources, to which __file__ will refer. Note: subprocproxy.py need not be frozen here, because py2app bundles include a Python executable for running source, and all modules the proxy needs. Windows were not opening on first text-file click (in dock): needed __main__ Apple opendoc event workaround and disable py2app's broken argv emulation in setup.py (as of the version used, at least). For the Run Code subprocprocy, must force all Python stdlibs to be included in the app's bundled Python, by uing a generated setup.py which lists them all. See include-full-stdlib.py for details. This scheme is imitated by Windows/Linux builds via PyInstaller hook files. NOTE: it's assumed that the frozen app, plus the Python and Tk included in the app bundle are univeral binaries, supporting both 64- and 32-bit machines. This appears to be so, though the app's portability to older OS X versions remains to be shown (10.11 El Capitan is build host). NOTE: tools scripts here are shipped in the Resources/tools folder in source-code forms, and hence require a separately-installed Python to run (or the app budle's Python, but this is obcure). These are too few and minor to warrant extra separate feezes in PyEdit. NOTE: py2app's --use-pythonpath flag doesn't incorporate PYTHONPATH settings if the app is run from Finder (by a click). This is an Apple quirk, not a py2app oer PyEdit bug. Run from a command line if needed. NOTE: the version of Pyhon you use to run this script is crucial--it's the version bundled with the app. ============================================================================= """ import os, sys, shutil join, sep = os.path.join, os.path.sep force = len(sys.argv) > 1 # remake icon if any arg startdir = os.getcwd() # this build script's dir # 'python3' fails in both IDLE and PyEdit RunCode (env's PATH not inherited?) python = sys.executable #---------------------------------------------------------------------------- # force all stdlib mods to be baked-in to the app's python, for Run Code; # else user code has minimal library access (though this is still not # sufficient for local package installs); see include-full-stdlib.py; #---------------------------------------------------------------------------- exitstat = os.system(python + ' include-full-stdlib.py') # makes setup.py assert exitstat == 0 #---------------------------------------------------------------------------- # make app's icon if one doesn't already exist #---------------------------------------------------------------------------- iconship = join('..', '..', '..', 'icons') iconmake = join('..', '..', 'build-icons') iconname = 'pyedit' iconfile = iconname + '.icns' # step into icon build dir and make if force or not os.path.exists(iconship + sep + iconfile): os.chdir(iconmake) #os.system('iconutil -c icns %s.iconset' % iconname) # Apple util #os.system('./resize-on-mac.sh Pyedit1024new images-pyedit') # just once os.system('%s iconify.py -mac images-pyedit %s' % (python, iconname)) # iconify 2.0 os.chdir(startdir) shutil.move(join(iconmake, iconfile), join(iconship, iconfile)) # mv to ship #---------------------------------------------------------------------------- # first: copy PP4E, move its TextEditor to root, and nest PP4E inside it; # [update - setup and teardown steps are now automated (run this script in # its dir), and Info.plist edits are now automatic by setup.py options;] #---------------------------------------------------------------------------- # automated setup - run in this file's dir temp = '/Users/blue/Desktop/tempsrc' # cp can't include self! if os.path.exists(temp): shutil.rmtree(temp) os.mkdir(temp) # move all to temp, nest PP4E in TextEditor shutil.copytree('../../../../../../PP4E', temp+'/PP4E', symlinks=True) shutil.move(temp+'/PP4E/Gui/TextEditor', temp) # move nested up to temp root shutil.move(temp+'/PP4E', temp+'/TextEditor') # move PP4E down to nested shutil.copy('setup.py', temp+'/TextEditor') # add setup script to root os.chdir(temp+'/TextEditor') # goto temp build dir #---------------------------------------------------------------------------- # cp so use PyEdit name for both the app-bundle folder and auto-menu title; # this name must also be used in this project's setup.py file here; # we can't rename these after the build, and "textEditor.py" is ingrained; #---------------------------------------------------------------------------- shutil.copy('textEditor.py', 'PyEdit.py') #---------------------------------------------------------------------------- # build app-bundle folder in ./dist, using ./setup.py, copying N extras # to the main Resources folder along with the generated program itself; # note: subprocproxy.py could be built via --extra-scripts, but this seems # no better than running it as source via sys.executable=python in bundle # (all modules imported by it will be available in the frozen app bundle); # # of course, this assumes that PyEdit.app/Contents/MacOS/python suffices: # must either '--includes' *all* of std lib, or ask user to install Python; # see include-full-stdlib.py here for one (arguably hackish) way to do it, # and ../../textConfig.py for interpreter and path settings that override # defaults: including stdlib still does not pickup locally-installed libs; #---------------------------------------------------------------------------- extras = ['textConfig.py', 'README.txt', 'icons', 'tools', 'docetc', 'UserGuide.html', 'subprocproxy.py'] # proxy is run as source here, not frozen exitstat = os.system( '%s setup.py py2app' ' --excludes textConfig' ' --resources %s' ' --iconfile icons/pyedit.icns' % (python, ','.join(extras))) if exitstat: print('ERROR: build failed:', exitstat) sys.exit(exitstat) # don't continue here #---------------------------------------------------------------------------- # cleanup: move and zip the app folder for easy xfer and backup (it has # _very_ many files: nearly 3K files+folders for the current PyEdit App); # [update - teardown actions are now automated (but still no data to copy)] # don't copy extras to Contents/Resources folder here: automatic via the # py2app args above; fixfrozenpaths.py arranges to see these as needed; # DON'T -skipcruft in the zip command: py2app makes a Resources/site.pyc! #---------------------------------------------------------------------------- # zip command: use portable ziptools (vs: 'zip -r %s %s' % (thezip, thedir)) thedir = 'PyEdit.app' thezip = thedir + '.zip' code = '/MY-STUFF/Code/ziptools/link' zipit = '%s %s/zip-create.py ../%s %s' % (python, code, thezip, thedir) # move dist product folder here os.chdir(startdir) if os.path.exists('dist'): shutil.rmtree('dist') # nuke bogus retained temp? shutil.move(temp+'/TextEditor/dist', '.') shutil.rmtree(temp) # rm temp build tree # zip the nested app folder - unzip to test and use here or elsewhere if os.path.exists(thezip): shutil.move(thezip, 'prev-'+thezip) # save previous version if os.path.exists(thedir): shutil.rmtree(thedir) # nuke unzipped version os.chdir('dist') os.system(zipit) # run zip in dist: has app dir os.chdir('..') shutil.rmtree('dist') # rm dist: _very_ many files print('Done: see ./%s' % thezip) # +unzip app and copy it to /Applications to make it official