I’ve written andspokenat some length about shipping software in the abstract. Sometimes I’ve evenhad the occasional concrete tidbit, but thatadvice wasn’t really complete.
In honor of Eevee’s delightful Games MadeQuick???, I’d like to help youpackage your games even quicker than you made them.
This will also install Pygame. Pre-compiled Pygame packages are available to pip for Windows & Linux (32-bit and 64-bit), and for Mac OS (64-bit only). If you have a different system, you’ll need to find a way to install pygame first. Pygame is a Free and Open Source cross-platform library for the development of multimedia applications like video games on Python. It uses the Simple DirectMedia Layer library and several other popular libraries to abstract most common functions and makes writing these program a more intuitive task. The only guide you need to learn how to install pygame using pip on Windows 10, Mac and Linux. Guide to installing pygame. The official pygame documentation recommends using pip for installation. Pip is a package management system that allows you to install and modify software packages written in python. I recently installed PyGame 1.9.1 on Mac OS X 10.6 (Snow Leopard) running Python 3.1 - (instructions at the bottom of the page. Cheers, and good luck! (Paul Barry, Dec 2009). A method of installing pygame on Mac OS X 10.7 (Lion) Here's how I installed PyGame 1.9.1 on Mac OS X 10.6 (Snow Leopard) for Python 2.7.2. Mountain Lion install. The PyGame project now ships just such wheels for a variety of Python versions on Mac, Windows, and Linux, which removes a whole huge pile of complexity both in generally understanding the C toolchain and specifically understanding the SDL build process.
About ten years ago I made a prototype of a little PyGame thing which I wantedto share with a few friends. Building said prototype was quick and fun, andvery different from the usual sort of work I do. But then, the project gotjust big enough that I started to wonder if it would be possible to share theresult, and thus began the long winter of my discontent with packaging tools.
I might be the only one, but... I don’t think so. The history ofPyWeek, for example, looks to be a history of gamesdistributed as Github repositories, or, at best, apps which don’t launch. Itseems like people who participate in game jams with Unity push a button andpublish their games to Steam; people who participate in game jams with Pythonwander away once the build toolchain defeats them.
So: perhaps you’re also a Python programmer, and you’ve built something withPyGame, and you want to put it on your website so your friends can download it.Perhaps many or most of your friends and family are Mac users. Perhaps youtried to make a thing with py2app once, and got nothing but inscrutabletracebacks or corrupt app bundles for your trouble.
If so, read on and enjoy.
If things didn’t work for me when I first tried to do this, what’s differentnow?
There are still weird little corner cases you have to work around — hence thispost – but mostly this is the story of how years of effort by the Pythonpackaging community have resulted in tools that are pretty close to working outof the box now.
Get a good Python.
My recommendation is to use an official build from python.org; these are already compiled in such a way that they will run on a wide range of macs, both new and old. Use a recent Python 3 version, if you can; there are a variety of low-level improvements which make it better for redistribution.
You probably also want to use a
virtualenv for development. This post isabout how to build a for-real thing that other people can download, but part ofthe magic of Python is the interactive, real-time dynamic nature of everything.Running the full build pipeline every time you change a file or an asset isslow and annoying. However, there’s a weird thing where certain parts of themacOS GUI won’t work right (in PyGame’s case, mostly keyboard focus) unlessyour code appears to be in an application bundle.
I made this dumb littlethingwhich lets you fake out enough of this that the OS won’t hassle you: you needto
pip install venvdotapp; venvdotapp inside the virtualenv where you’remaking your pygame app.
pip install all your requirements into your
virtualenv, includingPyGame itself.
All good apps need an icon, right?
When I was young, one would open up
ResEdit Resorcerer MPW CodeWarrior Project Builder Icon Composer Xcode andcreate a new ICON resource cicn resource
.icns file. Nowadays there’s some weird opaquestuff with
xcassets files and
Contents.json and “Copy Bundle Resources” inthe default Swift and Objective C project templates and honestly I can’t bebothered to keep track of what’s going on with this nonsense any more.
Luckily the OS ships with the macOS-specific “scriptable image processingsystem”, which can helpfully convert an icon for you. Make yourself a 512x512PNG file in your favorite image editor (with analpha channel!) that you want to use as your icon, then run it something likethis:
somewhere in your build process, to produce an icon in the appropriate format.
There’s also one additional wrinkle with PyGame: once you’ve launched thegame, PyGame helpfully assigns the cute, but ugly, default PyGame icon toyour running process. To avoid this, you’ll need these two lines somewhere inyour initialization code, somewhere before
pygame.display.init (or, for thatmatter,
Obviously this is pretty Mac-specific so you probably want this under some kindof platform-detection conditional, perhaps thisone.
Unfortunately py2app still tries really hard to jam all your code into a
.zipfile, which breaks the world in various hilarious ways. Your app will probablyhave some resources you want to load, as will PyGame itself.
packages=['your_package'] in your setup.py should address this,and it comes with a “pygame” recipe, but neither of these things worked for me.Instead, I convinced py2app to splat out all the files by using thenot-quite-public “recipe” plugin API:
This is definitely somewhat less efficient than py2app’s default of stuffingthe code into a single zip file, but, as a counterpoint to that: it actuallyworks.
Hopefully, at this point you can do
python setup.py py2app and get a shinynew app bundle in
dist/$NAME.app. We haven’t had to go through the hell ofquarantineyet, so it should launch at this point. If it doesn’t, sorry :-(.
You can often debug more obvious fail-to-launch issues by running theexecutable in the command line, by running
./dist/$NAME.app/Contents/MacOS/$NAME. Although this will run in a slightlydifferent environment than double clicking (it will have all your shell’s envvars, for example, so if your app needs an env var to work it mightmysteriously work there) it will also print out any tracebacks to yourterminal, where they’ll be slightly easier to find than in Console.app.
Once your app at least runs locally, it’s time to...
All the tutorials that I’ve found on how to do this involve doing Xcode projectgoop where it’s not clear what’s happening underneath. But despite the factthat the introductory docs aren’t quite there, the underlying model forcodesigning stuff is totally common across GUI and command-line cases.However, actually getting your cert requires Xcode, an apple ID, and acredit card.
After paying your hundred dollars, go into Xcode, go to Accounts, hit “+”,“Apple ID”, then log in. Then, in your shiny new account, go to “ManageCertificates”, hit the little “+”, and (assuming, like me, you want to putsomething up on your own website, and not submit to the Mac App Store), andchoose Developer ID Application. You probably think you want “mac appdistribution” because you are wanting to distribute a mac app! But you don’t.
Next, before you do anything else, make sure you have backups of your certificate and private key.You really don’t want to lose the private key associated with that cert.
Now quit Xcode; you’re done with the GUI.
You will need to know the identifier of your signing key though, which should be output from the command:
You probably want to put that in your build script, since you want to sign withthe same identity every time. Further commands here will assume you’ve copied one of the lines of results from that command and done
export IDENTITY='...' with it.
Update for macOS Catalina: In Catalina, Apple has added a newcode-signing requirement; evenfor apps distributed outside of the app store, they still have to be submittedto and approved by Apple.
In order to be notarized, you will need to codesign not only your app itself,but to also:
So the actual code-signing step is now a little more complicated.
One of the features that notarization is intended to strongly encourage1 isthe “hardened runtime”, a feature of macOS which opts in to stricter run-timebehavior designed to stop malware. One thing that the hardened runtime does isto disable writable, executable memory, which is used by JITs, FFIs ... andmalware.
Unfortunately, both Python’s built-in
ctypes module and various popular bitsof 3rd-party stuff that uses
pyOpenSSL, require writable,executable memory to work. Furthermore,
py2app actually imports
ctypesduring its bootstrapping phase, so you can’t even get your own code to startrunning to perform any workarounds unless this is enabled. So this is justif you want to use Python, not if your project requires
To make this long, sad story significantly shorter and happier, you cancreate an entitlements property list that enables the magical property whichallows this to work. It looks like this:
Subsequent steps assume that you’ve put this into a file called
entitleme.plist in your project root.
Notarization also requires that all the executable files in your bundle, notjust the main executable, are properly code-signed before submitting. Soyou’ll need to first run the
codesign command across all your sharedlibraries, something like this:
Then finally, sign the bundle itself.
Now, your app is code-signed.
The right way to do this is probably to usedmgbuild or something like it,but what I promised here was quick and dirty, not beautiful and best practices.
You have to make a Zip archive that preserves symbolic links. There are acouple of options for this:
open dist/, then in the Finder window that comes up, right click on the app and “compress” it
cd dist; zip -yr $NAME.app.zip $NAME.app
Most importantly, if you use the
zip command line tool, you must use the
-y option. Without it, your downloadable app bundle will be somewhatmysteriously broken even though the one before you
zipped it will be fine.
Notarization is a 2-step process, which is somewhat resistant to fullyautomating. You submit to Apple, then they email you the results of doingthe notarization, then if that email indicates that your notarization succeded,you can “staple” the successful result to your bundle.
The thing you notarize is an archive, which is why you need to do step 5 first.Then, you need to do this:
Be sure that
YOUR_BUNDLE_ID matches the
CFBundleIdentifier you told py2appabout before, so that the tool can find your app bundle inside the archive.
You’ll also need to type in the iCloud password for your Developer ID accounthere.2
Anxiously check your email for an hour or so. Hope you don’t get any errors.
Once Apple has a record of the app’s notarization, their tooling will recognizeit, so you don’t need any information from the confirmation email or theprevious command; just make sure that you are running this on the exact same
.app directory you just built and archived and not a version that differs inany way.
Finally, you will want to archive it again:
Ideally, at this point, everything should be working. But to make sure thatcode-signing and archiving and notarizing and re-archiving went correctly, youshould have either a pristine virtual machine with no dev tools and no Pythoninstalled, or a non-programmer friend’s machine that can serve the samepurpose. They probably need a relatively recent macOS - my own experience hasshown that apps made using the above technique will definitely work on HighSierra (and later) and will definitely break on Yosemite (and earlier); theyprobably start working at some OS version between those.
There’s no tooling that I know of that can clearly tell you whether your macapp depends on some detail of your local machine.
Even for yourdependencies, there’s no auditwheel for macOS.
Updated 2019-06-27: It turns out there is an
auditwheel like thing for macOS:
delocate! Infact, it predated and inspired
Thanks to Nathaniel Smith for the update(which he provided in, uh, January of 2018 and I’ve only just now gotten aroundto updating...).
Nevertheless, it’s always a good idea to check your final app build on a freshcomputer before you announce it.
If you were expecting to get to the end and download my cool game, sorry todisappoint! It really is a half-broken prototype that is in no way ready forpublic consumption, and given my current load ofpersonal andprofessional responsibilities, you definitely shouldn’t expect anything from mein this area any time soon, or, you know, ever.
But, from years of experience, I know that it’s nearly impossible to summon anymotivation to work on small projects like this without the knowledge that theend result will be usable in some way, so I hope that this helps someone elseset up their Python game-dev pipeline.
I’d really like to turn this into a 3-part series, with a part for Linux(perhaps using flatpak? is that a good thing?) and apart for Windows. However, given my aforementioned time constraints, I don’tthink I’m going to have the time or energy to do that research, so if you’vegot the appropriate knowledge, I’d love to host a guest post on this blog, oreven just a link to yours.
If this post helped you, if you have questions or corrections, or if you’d liketo write the Linux or Windows version of this post, let meknow.
The hardened runtime was originally required when notarization wasintroduced. Apparently this broke too much software and now therequirement is relaxed until January2020. But it’s probablybest to treat it as if it is required, since the requirement is almostcertainly coming back, and may in fact be back by the time you’re readingthis. ↩
You can pass it via the
--password option but there are all kinds ofsecurity issues with that so I wouldn’t recommend it. ↩
For the next two months, I'd like to try to focus on pygame 2 critical issues.
Backwards compatibility issues people are reporting with the pygame 2 dev releases.
So there's a feature freeze on anything not related to these issues. One issue at a time until the two months are up or we are done.
The first issue is: 'SDL 2: Per Pixel Alpha error'.
Over the next weeks we have plenty of game jams that people from the pygame communities take part in.
Alakajam is first starting on September 20th. 'Alakajam! is a young community gathering game development enthusiasts from all backgrounds. We host a series of informal events, that give you a chance to both make games and get people to play them. Alakajam! competitions, taking place over a week-end, three times a year. Start/end times are suited to European timezones.'
Then the must-use-python PyWeek challenge 'Invites entrants to write a game in one week from scratch either as an individual or in a team. Is intended to be challenging and fun. Will hopefully increase the public body of python game tools, code and expertise. Will let a lot of people actually finish a game, and may inspire new projects (with ready made teams!).' PyWeek runs from Sept. 22, 2019 to Sept. 29, 2019, and theme voting is already on.
Finally, Ludum Dare is an event where you create a game from scratch in a weekend based on a theme. Saturday October 5th to Tuesday October 8th, 2019. Starts at 12:00 AM CEST *. Ludumdare is the oldest online game jam, and has the largest number of participants. There is a Jam (72h, less restrictive rules), and a compo (48h more rules). The Jam now lets people submit paper board games, and even things like crafts that aren't games at all!
Want to join in as part of a team? The pygame community game (stuntcat) is also taking part, by creating a new mini game for some of these game jams. Drop into the discord chat #communitygame channel and say hi ( https://discord.gg/r8yreB6.
Also, how about inviting women and non-binary people to make the game with you?
If you want to join in online, come into our 'Discord' #contributing channel https://discord.gg/r8yreB6. If you're at EuroPython you can find information about the sprints on their website.
Every single source file has been heavily modified and moved in this release. Initial (source code only) support for SDL2 has been merged in. We also support compiling with SDL1 in the same code base, so the migration to pygame 2 is easier. pygame 2 will be released with SDL2 being the default backend when some remaining issues are ironed out. The 1.9.x releases will continue with SDL1 until then. Also, the C API of pygame is undergoing a transformation with lots of cleanups. Then there have been plenty of other cleanups all throughout the python code as well. There's still lots to clean up, but things should be significantly easier for people to contribute (👋 hello and thanks new contributors!). The documentation has been improved with better examples links, search functionality, and improved navigation. Support for older Macs, and newer Macs has been improved. The mask, midi, draw, and math modules have gotten lots of polish with rough edges removed.
A very special thanks to the people who have volunteered commits to pygame since the last release. In alphabetical order...
(@alphaCTzo7G) Amos Bastian (@amosbastian) Andrey Lopukhov (@andreyx86) Augusto Noronha (@augusto2112) Bernardo Sulzbach (@bernardosulzbach) (@Bottersnike) Cai Q.T. (@CAIQT) (@Cerealdragon) Charles (@charlesej) (@ChrisWeissNike) (@cmtrapp02) Daniel Molina (@daniel-molina) David Caughell (@DavidCaughell) David Lönnhager (@dlon) (@dr0id) burmer (@dtschirmer) (@e1000) xFly_Dragon (@husano896) (@IchMageBaume) René Dudfield (@illume) (@LambdaGoblin) Lenard Lindstrom (@llindstrom) François Magimel (@Linkid) (@LiquidFenrir) Mark Hurley (@markph0204) Marius Gedminas (@mgedmin) (@metulburr) Michael Farrell (@micolous) Dominik George (@Natureshadow) Nik (@nikolas) Nunu-Willump (@Nunu-Willump) (@pleboulanger) Rebecca Chen (@rchen152) (@robertpfeiffer) Sett (@sw00)
Quite a few new people have helped out with this release 🤗
An equally special thanks to everyone else who has contributed in other ways. Thanks to claudeb, imallet, and deshipu for moderating the discord chat to keep it friendly. Thanks to the twitter folk, and the stackoverflow Q&A people. Thanks to everyone who puts their game up on the pygame website for others to look at. Thanks to people making tutorials and sharing articles & books they've written. Thanks to the r/pygame mods and community. Thanks to pyladies groups for running fun sessions, and for making things better. Thanks to the teachers providing feedback, and for doing something fun with their students. Thanks to Debian, Raspberrypi, Arch, Fedora, and all the other community groups. 🖤
#945 Vector: Explain slices and swizzling
#944 Update surface documentation
#937 Make mask offset boundary checking consistent
#936 Update midi documentation
#931 Fix drawing 1 pixel wide off-surface ellipses
#929 Use SDL_RWops instead of pg_FopenUTF8.
#928 Fix LayeredDirty's inconsistent use of its source rect
#927 Workaround for locale errors with PyPy
#926 Update dependencies for manylinux builds.
#924 Upload .whl binaries to a github draft release when creating tag.
#923 Update scrap documentation
#922#881#879 Fix zero-sized mask segfaults
#920 Fix the SystemError when no type specific clipboard content exists.
#917 Update scrap tests
#914 Update freetype tests
#911#893#889#876#874#844#840#831#819 Improved mask tests
#900 Add sprite tests
#899 bugfix for dirty sprite when using a source rect
#891 Add missing Vector2.magnitude()
#873 Fix mask index out of bounds segmentation fault
#806 Mask constructor 'fill' argument
#807 Use version directives consistently in documentation and update style
#805 Event functions: pump parameter and keyword arguments
#803 Add customized repr()/str() output for Mask objects
#802 Fix aaline()/aalines() not drawing on a surface's border
#797 Keep surface alpha value for copied surfaces with SRCALPHA flag
#760#707 Load images on multiple threads properly
#783 Fix overlap_mask() making incorrect mask
#751#749#748#746#745 Added get_init() to all modules with an init() function
#725 Allow camera module to be used on Windows
#722 API version macros
#698 Use pre styles in docs
#670 Converting between ANSI notes and MIDI notes or frequencies
#663 Compile for PyPy 3 and PyPy fixes
#665 Chimp tutorial: reindent and clean some code
#664 Chimp tutorial: fix reST syntax
#656#649 Handle Unicode objects and paths properly
#631 Fix failing tests test_aapolygon and test_pie
#630 Fix freetype rotation rendering
#596 aaline cleanup
#582 draw.aaline: blend correctly
#577#573 Fixed FreeType memory leaks
#571 Fix surface.blits() bugs
#560 Added math.Vector2 subclass test for issue
#558 Raise TypeError on invalid point arguments
#557 Update typecheck to allow subtypes
#545 update system font lists in sysfont.py
#538 Mac fonts support using system_profiler
#559 Allow clean silencing of support prompt
#553 Fix homebrew travis mac builds
#551 Fixed freezing while playing music from file objects
#550 updated for python 3
#546 Improve message on failed imports & cleanup
#544 Spelling fix in examples/glcube.py
#540 DirtySprite subclass allow other _layer values
#536 dependency version into README & Cleanup SDL1.2
#534 More test cleanups.
#535 draw_py for Python algos & aaline testing
#534 More Test cleanup
#518 Organise the docs modules by usage, add search form.
#517 Test cleanup n+1 : yet another test cleanup branch
#516 PY_VERSION_HEX < 0x02070000 : cleanup
#515 Fix draw polygon ; tests & code cleanup
#514 Cleanup Python < 2.7 references
#511 Fix odd-width ellipses not drawing correctly
#510 Update unit tests draw.ellipse, add unit tests draw.(aa)line(s)
#509 Midi tests : add, refactor, cleanup
#508 More test method cleanup
#507 Improve mac compile times on travis.
#505 Remove 'if 1:' pattern in tests
#504 Cleanup mixer test
#503 Clang format all the src_c/.c files
#502 Cleanup some test methods & test/sndarray_test.py
#501 Add draw_ellipse unit test
#500 Test for overlap mask
#499 Remove boilerplate imports from top of test files
#498 Adding a test for Thick Line Bug
#493 0 sized masks are now possible
#489 transform.scale does not crash for zero sized surfaces
#488 Added unit test 'test_zero_surface_transform' for issue 411
#486 Moved jquery.plugin.docscomments.js into pygameweb.
#485 Repo cleanup. 8 things in top level. Rest in buildconfig/. Old stuff removed.
#484 Added mac sdl2 support. Changed config.py -sdl2
#483 Support older mac core duo 2 cpus
#482 The giant SDL2_patches merging PR.
#476 Do not try to add portmidi on freebsd. It does not exist.
#475 Resurrect Python 3.4 builds on Appveyor
🐱🏍 stuntcat is our open source mini-game we worked on in order to drive pygame 2 development forward. We wanted to make a real mini-game and try to release it for sale on as many platforms as possible. So that other people can look at the source code and distribute their game on more platforms too.
We only had 4 days... but during that time more than 14 people got involved. We learnt a lot during this. The compilation instructions for pygame 2 were improved so that more people could build development versions. We automated the installer generation for Windows, Mac, and pypi on github (so other projects can copy this base-code and do it too). Gif animation saving was started (because when you release your game you want a gif right?). Missing features and areas where documentation could be improved were identified (animated sprites, and scenes anyone?). Fixes to pymunk (a great 2D physics engine) were made so distributing physics games to end users is now easier. There's also an example of using the tile map loader (PyTMX) with the physics engine. More than a month after the game jam finished we are working on issues discovered during the making of 🐱🏍 stuntcat.
We want to make distributing pygame apps easier for people. You made your game, now you want to share it with your friends or even sell it right? The first place we made a release was on itch.io, a platform for selling games and apps. There we uploaded the Mac, Windows, and source for people to download with a pay-what-you-feel option.
Next step is to release the game on Steam (another popular games platform). For the next step we need to raise $100 for the Steam App fee. We also have to make the game better! It's already a pretty fun mini game, but requires more mini games and more polish. We also need to make pygame 2 pre-release binaries so that we can actually publish on Steam.
Thanks to the makers of stuntcat: bitcraft, blubberquark, Bottersnake, claudeb, illume, Kuba ThePolish, TJWhale, hfoxp, xeno, M, CaptHurls, dlon, dirk0, viblo, and kleines filmröllchen.
day 0: $0 of $100 raised for Steam app fee.
day 1: $5 of $100
day 2: $57 of $100
day 3: $100 of $113.20
day 14: $107 of $113.20
day 20: $127 of $113.20
If you ever wanted to support pygame development financially and you can afford it, this is how you can by purchasing stuntcat on itch.io for $2 or what ever you feel. update: we raised enough to make a steam release. Thanks to everyone who chipped in!
See the post on the PyPy blog: 'PyPy for low latency systems',
and also this post: Experiments with new low latency PyPy garbage collector in a thread.
welcome to our humble (and slightly strange) little part of the World Wide Web. Let me give you a quick introduction about what you've stumbled upon here.
pygame (the library) is a Free and Open Source python programming language library for making multimedia applications like games.
pygame.org (the website) welcomes all Python game, art, music, sound, video and multimedia projects. Once you have finished getting started you could add a new project or learn about pygame by reading the docs. For more information on what is happening in the pygame world see the community dashboard web page, which lists many things like our projects we are working on, news (our blog with rss), twitter, reddit (forum), stackoverflow (Q&A), Bitbucket (development), irc(chat), mailinglist (we love writing electronic mail to each other) and other various bits and pieces about pygame from around the internets.
pygame (the community) is a small volunteer group of creative humans who ♥ making things (there may also be a few cats, several koalas, dozens of doggos, 3.14 gnomes, and 42 robots who also tinker amongst us). We respect each other, and follow the Python community code of conduct, whilst we help each other make interesting things.
Enjoy yourself whilst looking around. We look forward to your creations.
Best humanly possible wishes and extra toasty warm regards,
ps. We spell colour without the u (sorry), and we always spell pygame with lower case letters. Also, please do excuse the mess - we're doing some renovations around here you see.