1 00:00:00,420 --> 00:00:05,910 [Music] 2 00:00:10,320 --> 00:00:15,599 the last talk for this block. I wonder 3 00:00:13,200 --> 00:00:17,600 if the tram ding is the same one as last 4 00:00:15,599 --> 00:00:19,760 year's. Apparently, it was the wrong 5 00:00:17,600 --> 00:00:22,480 ding for the tram on the PyCon AU logo 6 00:00:19,760 --> 00:00:25,840 last year. So, trust a a conference full 7 00:00:22,480 --> 00:00:30,039 of PyCon devs to to have a slightly 8 00:00:25,840 --> 00:00:30,039 unhealthy obsession with trams. 9 00:00:30,480 --> 00:00:35,040 For our last speaker for this block, we 10 00:00:32,880 --> 00:00:38,559 have Russell, who is going to be talking 11 00:00:35,040 --> 00:00:39,520 about taking wheels mobile. This looks 12 00:00:38,559 --> 00:00:41,280 really interesting because I always 13 00:00:39,520 --> 00:00:45,040 thought wheels were mobile. 14 00:00:41,280 --> 00:00:47,440 Um, so give him the classic big warm 15 00:00:45,040 --> 00:00:50,680 PyCon A welcome. Um, and I'll hand it 16 00:00:47,440 --> 00:00:50,680 over to Russell. 17 00:00:52,399 --> 00:00:56,000 Well, hi there. Uh, my name is Russell 18 00:00:54,160 --> 00:00:58,239 Keith McGee. I am here today to show you 19 00:00:56,000 --> 00:01:00,000 how you can generate binary wheels in 20 00:00:58,239 --> 00:01:01,920 Python and more specifically how to 21 00:01:00,000 --> 00:01:05,119 generate binary wheels for mobile 22 00:01:01,920 --> 00:01:06,880 platforms like iOS and Android. First 23 00:01:05,119 --> 00:01:08,400 off, who am I? I am from Maduk 24 00:01:06,880 --> 00:01:10,000 Nungabuja, otherwise known as Perth 25 00:01:08,400 --> 00:01:11,840 Western Australia. I am a member of the 26 00:01:10,000 --> 00:01:13,439 CPython core team. I am a principal 27 00:01:11,840 --> 00:01:15,920 engineer in the open source group at 28 00:01:13,439 --> 00:01:17,360 Anaconda. Anaconda pays for me to attend 29 00:01:15,920 --> 00:01:18,720 conferences like this one to talk to 30 00:01:17,360 --> 00:01:21,200 people like you. So, I'd like to thank 31 00:01:18,720 --> 00:01:23,360 them for that support. They also support 32 00:01:21,200 --> 00:01:25,360 the Python open source ecosystem more 33 00:01:23,360 --> 00:01:27,600 broadly by providing direct financial 34 00:01:25,360 --> 00:01:29,200 support to organizations like Num Focus 35 00:01:27,600 --> 00:01:30,960 and by employing developers to work 36 00:01:29,200 --> 00:01:33,360 full-time on open source projects. 37 00:01:30,960 --> 00:01:35,119 Projects like Jupiter and Number and the 38 00:01:33,360 --> 00:01:36,560 open source project that I founded 39 00:01:35,119 --> 00:01:38,960 Beware. For those who haven't come 40 00:01:36,560 --> 00:01:40,720 across it before, Beware is a collection 41 00:01:38,960 --> 00:01:42,640 of open source tools and libraries for 42 00:01:40,720 --> 00:01:45,600 creating native user interfaces in 43 00:01:42,640 --> 00:01:47,119 Python for desktop but also for iOS, for 44 00:01:45,600 --> 00:01:50,560 Android, for single page web apps, and 45 00:01:47,119 --> 00:01:52,479 for console. But today I'm not going to 46 00:01:50,560 --> 00:01:54,479 be talking about beware, at least not 47 00:01:52,479 --> 00:01:56,799 directly. For the last 10 months or so, 48 00:01:54,479 --> 00:01:58,799 my co-orker Malcolm Smith and I have 49 00:01:56,799 --> 00:02:01,119 been working on the tools in the broader 50 00:01:58,799 --> 00:02:03,680 Python packaging ecosystem, adding 51 00:02:01,119 --> 00:02:06,000 support for iOS and Android. This work 52 00:02:03,680 --> 00:02:07,759 is not part of Beware itself. Beware 53 00:02:06,000 --> 00:02:09,520 uses these tools, but since iOS and 54 00:02:07,759 --> 00:02:11,680 Android are now officially tier three 55 00:02:09,520 --> 00:02:13,280 supported platforms for CPython, any 56 00:02:11,680 --> 00:02:15,760 other project that wants to target iOS 57 00:02:13,280 --> 00:02:17,120 or Android can also use these tools. So 58 00:02:15,760 --> 00:02:19,120 today I'm going to be talking about that 59 00:02:17,120 --> 00:02:20,720 work about wheels, how you build them, 60 00:02:19,120 --> 00:02:24,400 and more specifically how you build them 61 00:02:20,720 --> 00:02:25,760 for iOS and Android. So what even is a 62 00:02:24,400 --> 00:02:27,440 wheel? Well, a wheel is the 63 00:02:25,760 --> 00:02:29,440 redistributable unit of code in the 64 00:02:27,440 --> 00:02:32,720 Python ecosystem. It was standardized in 65 00:02:29,440 --> 00:02:34,319 PEP 427. It's not a complex instrument. 66 00:02:32,720 --> 00:02:36,640 It is a it is basically a zip file 67 00:02:34,319 --> 00:02:38,400 containing Python code in the installed 68 00:02:36,640 --> 00:02:40,319 directory layout along with some 69 00:02:38,400 --> 00:02:42,400 metadata describing the package and 70 00:02:40,319 --> 00:02:44,480 optionally some actual data for the 71 00:02:42,400 --> 00:02:45,760 package to use. The contents of the 72 00:02:44,480 --> 00:02:48,000 wheel is designed so that it can be 73 00:02:45,760 --> 00:02:49,680 unpacked directly into the lo to a 74 00:02:48,000 --> 00:02:51,680 location on your Python path. Usually 75 00:02:49,680 --> 00:02:53,360 site packages, but not necessarily, and 76 00:02:51,680 --> 00:02:55,440 then used without the need to run any 77 00:02:53,360 --> 00:02:57,840 additional Python code or run a compiler 78 00:02:55,440 --> 00:02:59,519 or anything like that. The wheel itself 79 00:02:57,840 --> 00:03:01,440 has to follow a very specific naming 80 00:02:59,519 --> 00:03:03,040 pattern. What's in a name? Well, an 81 00:03:01,440 --> 00:03:04,480 identifier for the package, the package 82 00:03:03,040 --> 00:03:06,480 version, and then the platform 83 00:03:04,480 --> 00:03:09,280 applicability of the wheel. The package 84 00:03:06,480 --> 00:03:10,959 identifier has to adhere to PET PET 508. 85 00:03:09,280 --> 00:03:12,800 The version number has to adhere to PET 86 00:03:10,959 --> 00:03:14,959 440. And that leaves platform 87 00:03:12,800 --> 00:03:17,519 applicability in three parts. There's a 88 00:03:14,959 --> 00:03:19,599 Python tag that describes which Python 89 00:03:17,519 --> 00:03:22,640 implementation the code is compatible 90 00:03:19,599 --> 00:03:26,000 with. PI3 for any Python 3 interpreter, 91 00:03:22,640 --> 00:03:30,239 CP314 for a CPython 3.14 interpreter, 92 00:03:26,000 --> 00:03:32,560 PP311 for a PI311 interpreter and so on. 93 00:03:30,239 --> 00:03:34,239 Then there's the AI tag, the application 94 00:03:32,560 --> 00:03:36,560 binary interface. This describes the 95 00:03:34,239 --> 00:03:38,560 compatibility of any binary Python 96 00:03:36,560 --> 00:03:40,560 components in the wheel. This will be 97 00:03:38,560 --> 00:03:42,720 none if there's no binary compatibility 98 00:03:40,560 --> 00:03:44,480 concerns. It could also be a specific 99 00:03:42,720 --> 00:03:47,599 Python version that matches the Python 100 00:03:44,480 --> 00:03:50,239 tag CP314. It could also describe a 101 00:03:47,599 --> 00:03:52,080 variant. So something like CP314T 102 00:03:50,239 --> 00:03:54,080 means the code requires a freethreaded 103 00:03:52,080 --> 00:03:56,959 build of Python. Or the wheel could 104 00:03:54,080 --> 00:03:59,040 declare ABI3 that it uses the stable AI 105 00:03:56,959 --> 00:04:00,959 which is a subset of the Python AI that 106 00:03:59,040 --> 00:04:03,120 is guaranteed stable across Python 107 00:04:00,959 --> 00:04:04,640 versions. Lastly, there's the platform 108 00:04:03,120 --> 00:04:06,239 tag. This is where you declare operating 109 00:04:04,640 --> 00:04:07,680 system and architecture compatibility. 110 00:04:06,239 --> 00:04:10,560 This can be a single platform like 111 00:04:07,680 --> 00:04:12,879 Win332 or platform and architecture win 112 00:04:10,560 --> 00:04:14,319 ARM 64 for example. It could include a 113 00:04:12,879 --> 00:04:16,720 minimum version of an operating system 114 00:04:14,319 --> 00:04:18,799 like macOS 11. It could describe a meta 115 00:04:16,720 --> 00:04:20,560 architecture. Universal 2 describes a 116 00:04:18,799 --> 00:04:23,440 wheel that will work on MacOss on both 117 00:04:20,560 --> 00:04:25,680 ARM 64 and x8664. 118 00:04:23,440 --> 00:04:27,520 Even the operating system can be a meta 119 00:04:25,680 --> 00:04:28,800 tag. Linux isn't an operating or Linux 120 00:04:27,520 --> 00:04:30,800 isn't an operating system by itself. 121 00:04:28,800 --> 00:04:32,800 It's just a kernel. So the many Linux 122 00:04:30,800 --> 00:04:34,479 and muscle Linux specifications describe 123 00:04:32,800 --> 00:04:37,520 sets of system libraries that the wheel 124 00:04:34,479 --> 00:04:39,759 can assume exists. As an end user, you 125 00:04:37,520 --> 00:04:41,680 just pip install my package. Pip then 126 00:04:39,759 --> 00:04:43,199 goes to Pippi, finds all the wheels that 127 00:04:41,680 --> 00:04:44,720 match considering your operating system, 128 00:04:43,199 --> 00:04:46,160 operating system version, CPU 129 00:04:44,720 --> 00:04:48,240 architecture, Python version, Python 130 00:04:46,160 --> 00:04:50,320 variant, and builds a list of candidate 131 00:04:48,240 --> 00:04:52,320 wheels including binary wheels and pure 132 00:04:50,320 --> 00:04:54,000 Python wheels, which uh which can 133 00:04:52,320 --> 00:04:56,560 actually themselves be platform specific 134 00:04:54,000 --> 00:04:58,080 if they need to be. Pips's resolver then 135 00:04:56,560 --> 00:05:00,720 works out which wheel is the best match 136 00:04:58,080 --> 00:05:02,560 and installs that wheel. But as a 137 00:05:00,720 --> 00:05:04,320 package developer, that means that for 138 00:05:02,560 --> 00:05:05,840 each release, you need to build wheels 139 00:05:04,320 --> 00:05:07,280 for multiple operating systems and 140 00:05:05,840 --> 00:05:08,720 operating system versions and meta 141 00:05:07,280 --> 00:05:10,560 operating systems and architectures and 142 00:05:08,720 --> 00:05:13,280 meta architectures and multiple Python 143 00:05:10,560 --> 00:05:15,039 implementations and versions and APIs. 144 00:05:13,280 --> 00:05:16,720 Pillow, for example, is the gold 145 00:05:15,039 --> 00:05:21,120 standard library for image manipulation 146 00:05:16,720 --> 00:05:24,240 in Python. And Pillow 11.3 ships 105 147 00:05:21,120 --> 00:05:25,840 different binary wheels. That's 105 148 00:05:24,240 --> 00:05:27,520 different binary configurations that 149 00:05:25,840 --> 00:05:29,840 need to be configured, built, and 150 00:05:27,520 --> 00:05:32,479 tested. So, how do you generate a 151 00:05:29,840 --> 00:05:34,720 release with 105 different binary 152 00:05:32,479 --> 00:05:36,240 artifacts? Well, this is where a tool 153 00:05:34,720 --> 00:05:37,840 called CI build wheel comes to the 154 00:05:36,240 --> 00:05:42,880 rescue. CI build wheel provides the 155 00:05:37,840 --> 00:05:44,880 tooling to help you build a wheel in CI. 156 00:05:42,880 --> 00:05:46,800 Uh, for the uninitiated, CI is 157 00:05:44,880 --> 00:05:48,720 continuous integration. Uh, think GitHub 158 00:05:46,800 --> 00:05:50,720 actions. Despite the name, it's actually 159 00:05:48,720 --> 00:05:52,639 not CI specific. You can also install 160 00:05:50,720 --> 00:05:54,400 and use it locally, but obviously it 161 00:05:52,639 --> 00:05:57,280 does work really well as part of a CI 162 00:05:54,400 --> 00:05:59,680 pipeline generating 105 binary artifacts 163 00:05:57,280 --> 00:06:01,120 as part of a release. CI buildwhe 164 00:05:59,680 --> 00:06:04,560 provides the tools to configure the 165 00:06:01,120 --> 00:06:06,479 matrix of options as a matrix. To make 166 00:06:04,560 --> 00:06:07,840 your package compatible with CI build, 167 00:06:06,479 --> 00:06:10,479 you need to start with a package that is 168 00:06:07,840 --> 00:06:12,720 compliant with PEP 517 and 518. That 169 00:06:10,479 --> 00:06:14,639 means you need a piprotoml file and that 170 00:06:12,720 --> 00:06:16,639 file declares a build system table with 171 00:06:14,639 --> 00:06:19,199 a requires field and a build backend 172 00:06:16,639 --> 00:06:20,800 field. Requires defines the set of tools 173 00:06:19,199 --> 00:06:22,720 that must be in your environment to be 174 00:06:20,800 --> 00:06:25,440 able to build a wheel for your package. 175 00:06:22,720 --> 00:06:27,199 Build backend def specifies the path of 176 00:06:25,440 --> 00:06:29,360 a module that will be installed by the 177 00:06:27,199 --> 00:06:30,720 requires. That module has to adhere to a 178 00:06:29,360 --> 00:06:33,120 specific interface which provides an 179 00:06:30,720 --> 00:06:35,120 entry point to trigger a build. The 180 00:06:33,120 --> 00:06:37,120 build system table defines the interface 181 00:06:35,120 --> 00:06:39,680 that Python will use to build a wheel 182 00:06:37,120 --> 00:06:41,520 for your Python code. It does not care 183 00:06:39,680 --> 00:06:44,400 how that happens, only that the build 184 00:06:41,520 --> 00:06:46,160 back end will define the entry point. In 185 00:06:44,400 --> 00:06:48,400 practice, you'll likely need a bunch of 186 00:06:46,160 --> 00:06:50,720 other configuration details. Uh, PEP 621 187 00:06:48,400 --> 00:06:51,919 and 639 project configurations. You'll 188 00:06:50,720 --> 00:06:53,680 likely also need to provide 189 00:06:51,919 --> 00:06:56,639 configuration that's needed by your 190 00:06:53,680 --> 00:06:59,199 chosen PEP 517 build backend. There are 191 00:06:56,639 --> 00:07:02,080 a whole lot of PEP 517 compliant build 192 00:06:59,199 --> 00:07:04,720 backends. Setup tools, Hatch, Flit, 193 00:07:02,080 --> 00:07:06,800 Poetry, Matarin, Mison. Which one should 194 00:07:04,720 --> 00:07:08,639 you use? That's entirely up to you. From 195 00:07:06,800 --> 00:07:10,800 Python's perspective, it does not 196 00:07:08,639 --> 00:07:12,639 matter. And that is the point of PEP 197 00:07:10,800 --> 00:07:15,199 517. 198 00:07:12,639 --> 00:07:17,520 But once a project aderes to those peps, 199 00:07:15,199 --> 00:07:19,280 CI buildwhe can then do its magic. You 200 00:07:17,520 --> 00:07:21,599 don't need any additional configuration 201 00:07:19,280 --> 00:07:23,919 to use CI buildwhe. PIP install CI 202 00:07:21,599 --> 00:07:26,000 buildwheel then run CI buildwhe. And by 203 00:07:23,919 --> 00:07:28,160 default, CI buildwhe will run through 204 00:07:26,000 --> 00:07:30,960 every currently supported Python version 205 00:07:28,160 --> 00:07:33,120 for your current platform and use the 206 00:07:30,960 --> 00:07:35,039 517 interface to build a wheel for each 207 00:07:33,120 --> 00:07:37,520 currently supported Python version. At 208 00:07:35,039 --> 00:07:40,319 present, that's at least six wheels. One 209 00:07:37,520 --> 00:07:43,360 each for Python 3.10, 10, 311, 312, 313, 210 00:07:40,319 --> 00:07:44,960 314, and one for 314 free thread it. On 211 00:07:43,360 --> 00:07:46,720 MacOss, you have to have the official 212 00:07:44,960 --> 00:07:48,560 CPython interpreter for each Python 213 00:07:46,720 --> 00:07:49,840 version installed. If those interpreters 214 00:07:48,560 --> 00:07:51,840 aren't available, you get an error 215 00:07:49,840 --> 00:07:53,440 during the build process. On Linux, you 216 00:07:51,840 --> 00:07:55,120 need to have Docker installed. The many 217 00:07:53,440 --> 00:07:56,720 Linux and muscle Linux Docker images are 218 00:07:55,120 --> 00:07:58,960 then used to host the builds. On 219 00:07:56,720 --> 00:08:00,800 Windows, CIBU will automatically 220 00:07:58,960 --> 00:08:02,479 download and cache uh Python 221 00:08:00,800 --> 00:08:04,240 interpreters with Nugget as they're 222 00:08:02,479 --> 00:08:06,319 needed. 223 00:08:04,240 --> 00:08:08,479 Just call CI buildwhe is the simplest 224 00:08:06,319 --> 00:08:10,400 possible invocation of CI buildwhe but 225 00:08:08,479 --> 00:08:12,639 CI buildwhe is also very configurable. 226 00:08:10,400 --> 00:08:14,800 You can configure CI buildwhe to skip 227 00:08:12,639 --> 00:08:17,599 certain Python versions if you want or 228 00:08:14,800 --> 00:08:19,520 to include pi builds or to skip and 229 00:08:17,599 --> 00:08:21,280 include specific architectures. And 230 00:08:19,520 --> 00:08:23,759 there are then multiple ways to define 231 00:08:21,280 --> 00:08:26,960 those configurations. As an example, 232 00:08:23,759 --> 00:08:28,960 macOS currently supports both x8664 and 233 00:08:26,960 --> 00:08:32,080 ARM 64 architectures and there is a 234 00:08:28,960 --> 00:08:34,320 universal 2 tag that supports both. All 235 00:08:32,080 --> 00:08:36,640 three options can be compiled from a 236 00:08:34,320 --> 00:08:37,919 single build machine. By default, CI 237 00:08:36,640 --> 00:08:39,839 build will only build the current 238 00:08:37,919 --> 00:08:42,320 architecture, but you can configure CI 239 00:08:39,839 --> 00:08:45,040 build to build all the options. From the 240 00:08:42,320 --> 00:08:47,519 command line, pass in- arches all. And 241 00:08:45,040 --> 00:08:51,120 on a Mac, CI build will generate 18 242 00:08:47,519 --> 00:08:53,839 wheels. Six for x8664, six for ARM 64, 243 00:08:51,120 --> 00:08:55,440 and six that are universal, too. You can 244 00:08:53,839 --> 00:08:57,839 also specify options in your pi 245 00:08:55,440 --> 00:08:59,200 project.l file. If you add a tool.ci 246 00:08:57,839 --> 00:09:00,640 buildw wheel section, you can specify 247 00:08:59,200 --> 00:09:03,120 the architectures that you want to build 248 00:09:00,640 --> 00:09:05,120 by default. So for example here you're 249 00:09:03,120 --> 00:09:07,519 building all available architectures on 250 00:09:05,120 --> 00:09:09,120 every platform. Those configurations can 251 00:09:07,519 --> 00:09:11,680 also be platform specific by adding 252 00:09:09,120 --> 00:09:13,120 subtables for each platform. So in the 253 00:09:11,680 --> 00:09:15,120 second configuration we're saying we 254 00:09:13,120 --> 00:09:17,279 want to build all architectures on macOS 255 00:09:15,120 --> 00:09:19,920 but only the AMD 64 architectures on 256 00:09:17,279 --> 00:09:22,160 Windows. Alternatively you can configure 257 00:09:19,920 --> 00:09:24,800 CI build with environment variables. If 258 00:09:22,160 --> 00:09:26,640 you define CIBW arches that value will 259 00:09:24,800 --> 00:09:28,880 be handled at the same as if it had been 260 00:09:26,640 --> 00:09:31,279 passed in as a command line argument or 261 00:09:28,880 --> 00:09:32,720 specified in the configuration file. The 262 00:09:31,279 --> 00:09:34,640 environment variable can also be 263 00:09:32,720 --> 00:09:37,040 qualified to only apply to specific 264 00:09:34,640 --> 00:09:38,800 platforms. So in a single environment, 265 00:09:37,040 --> 00:09:40,959 you can define environment variables for 266 00:09:38,800 --> 00:09:42,560 both MacOss and Windows. When the build 267 00:09:40,959 --> 00:09:44,000 occurs on MacOss, the Mac OS version 268 00:09:42,560 --> 00:09:45,360 will be on it. When the build occurs on 269 00:09:44,000 --> 00:09:47,760 Windows, the Windows version will be on 270 00:09:45,360 --> 00:09:50,240 it. And that's just one example of 271 00:09:47,760 --> 00:09:51,920 Archers. CI build has dozens of options 272 00:09:50,240 --> 00:09:53,200 for configuring exactly what versions, 273 00:09:51,920 --> 00:09:54,480 interpreters, and architectures are 274 00:09:53,200 --> 00:09:57,519 built. And every option can then be 275 00:09:54,480 --> 00:10:00,480 defined on a platform specific basis. 276 00:09:57,519 --> 00:10:02,800 By default, CI build will just build the 277 00:10:00,480 --> 00:10:05,040 wheels. But any good CI process should 278 00:10:02,800 --> 00:10:07,440 also be running the tests. So you can 279 00:10:05,040 --> 00:10:09,600 tell CI build how to run the tests for 280 00:10:07,440 --> 00:10:11,360 your project. Test command defines the 281 00:10:09,600 --> 00:10:13,040 command line to run the test suite. Test 282 00:10:11,360 --> 00:10:14,720 sources defines what subset of the 283 00:10:13,040 --> 00:10:17,040 entire source tree is necessary for the 284 00:10:14,720 --> 00:10:18,399 tests to run. Test requires defines 285 00:10:17,040 --> 00:10:20,320 specific packages that need to be 286 00:10:18,399 --> 00:10:22,079 installed to run the tests. Or you can 287 00:10:20,320 --> 00:10:24,160 specify test extras to define an 288 00:10:22,079 --> 00:10:26,160 optional dependency set or test groups 289 00:10:24,160 --> 00:10:27,360 to use a PEP 735 dependency group 290 00:10:26,160 --> 00:10:29,519 definition. 291 00:10:27,360 --> 00:10:31,279 And these options can also be defined 292 00:10:29,519 --> 00:10:33,200 using environment variables with CI 293 00:10:31,279 --> 00:10:34,959 build test command for example or 294 00:10:33,200 --> 00:10:38,480 platform specific environment variables 295 00:10:34,959 --> 00:10:40,560 like CIBW test command macOS. If you set 296 00:10:38,480 --> 00:10:42,480 this up and you run CI build wheel, not 297 00:10:40,560 --> 00:10:44,000 only will you get a flood of wheels for 298 00:10:42,480 --> 00:10:46,000 each version of the wheel that is built, 299 00:10:44,000 --> 00:10:48,000 a clean test environment will be 300 00:10:46,000 --> 00:10:51,040 created, the wheel will be installed and 301 00:10:48,000 --> 00:10:53,040 the test suite will be executed. 302 00:10:51,040 --> 00:10:55,040 Okay, so now we can pull all the pieces 303 00:10:53,040 --> 00:10:57,360 together and actually use CI build wheel 304 00:10:55,040 --> 00:10:59,200 in CI. I'm going to show GitHub actions 305 00:10:57,360 --> 00:11:01,120 here, but the CI build docs have 306 00:10:59,200 --> 00:11:03,920 examples for Azure pipelines, TravisCI, 307 00:11:01,120 --> 00:11:06,000 CircleCI, GitLab CI, and Cirrusci. You 308 00:11:03,920 --> 00:11:07,920 set up a workflow that on GitHub actions 309 00:11:06,000 --> 00:11:10,399 runs on a push or a pull request. You 310 00:11:07,920 --> 00:11:12,079 add a build wheels job that runs a build 311 00:11:10,399 --> 00:11:14,480 matrix that's going to run on Linux, 312 00:11:12,079 --> 00:11:16,959 Windows, and MacOss. What's in that 313 00:11:14,480 --> 00:11:18,800 matrix job? Well, CI build publishes a 314 00:11:16,959 --> 00:11:20,959 GitHub actions workflow that will do all 315 00:11:18,800 --> 00:11:22,320 the heavy lifting of getting CI build 316 00:11:20,959 --> 00:11:24,320 set up to execute. So, you only need 317 00:11:22,320 --> 00:11:26,560 three steps. A checkout, so your code is 318 00:11:24,320 --> 00:11:28,160 available. CI buildwhe and then saving 319 00:11:26,560 --> 00:11:30,480 the build artifacts CI buildwheel 320 00:11:28,160 --> 00:11:32,720 generated. That workflow will run the 321 00:11:30,480 --> 00:11:34,560 project a piprotomal configuration for 322 00:11:32,720 --> 00:11:36,720 all three desktop platforms on all 323 00:11:34,560 --> 00:11:38,399 stable Python versions producing dozens 324 00:11:36,720 --> 00:11:41,920 of wheels depending upon what 325 00:11:38,399 --> 00:11:44,399 configuration is in your piprotoml file. 326 00:11:41,920 --> 00:11:46,959 But the configuration doesn't have to be 327 00:11:44,399 --> 00:11:48,480 entirely in piprotoml. This is why 328 00:11:46,959 --> 00:11:50,800 environment variable configuration is 329 00:11:48,480 --> 00:11:52,720 useful. Pyrotoml configurations will 330 00:11:50,800 --> 00:11:54,240 apply to every user who downloads the 331 00:11:52,720 --> 00:11:56,160 project. Environment variable 332 00:11:54,240 --> 00:11:58,959 configuration lets you specify this 333 00:11:56,160 --> 00:12:00,640 specific release configuration and do so 334 00:11:58,959 --> 00:12:03,600 without the need to add complex 335 00:12:00,640 --> 00:12:04,880 branching logic in your workflow. So you 336 00:12:03,600 --> 00:12:06,639 set the variables that apply to the 337 00:12:04,880 --> 00:12:10,320 platforms that need them and CI build 338 00:12:06,639 --> 00:12:12,240 will do the rest. Okay, but can't you do 339 00:12:10,320 --> 00:12:14,160 all this by yourself? Can't you just set 340 00:12:12,240 --> 00:12:15,760 up a CI matrix over operating systems 341 00:12:14,160 --> 00:12:17,279 and Python versions to spit out all the 342 00:12:15,760 --> 00:12:20,000 wheels that you need by just calling, 343 00:12:17,279 --> 00:12:22,880 you know, Python minus mus 344 00:12:20,000 --> 00:12:24,800 build by yourself? Well, yes, you 345 00:12:22,880 --> 00:12:26,720 probably could. However, that means 346 00:12:24,800 --> 00:12:28,639 you're now responsible for a very 347 00:12:26,720 --> 00:12:30,320 complex build matrix. CI build wheel 348 00:12:28,639 --> 00:12:32,320 abstracts the complexity of everything 349 00:12:30,320 --> 00:12:34,320 past the level of the operating system. 350 00:12:32,320 --> 00:12:36,240 And in the case of Linux, that's a 351 00:12:34,320 --> 00:12:38,160 non-trivial matrix because you've got 352 00:12:36,240 --> 00:12:39,360 multiple many Linux and muscle Linux 353 00:12:38,160 --> 00:12:42,240 targets that you need to make sure 354 00:12:39,360 --> 00:12:43,839 you've got correct. Using CI buildwhe 355 00:12:42,240 --> 00:12:45,839 means you need a top level operating 356 00:12:43,839 --> 00:12:48,079 system matrix, but then CI buildwhe 357 00:12:45,839 --> 00:12:50,399 manages everything else, including 358 00:12:48,079 --> 00:12:53,680 automatic updates to your matrix. When 359 00:12:50,399 --> 00:12:55,839 CI build 3.1 was released, Python 3.14 360 00:12:53,680 --> 00:12:58,240 and 3.14 freethreaded support was 361 00:12:55,839 --> 00:13:00,079 automatically added to every matrix by 362 00:12:58,240 --> 00:13:02,000 default. And the only change you needed 363 00:13:00,079 --> 00:13:03,600 to make as an end user was to update 364 00:13:02,000 --> 00:13:05,040 your pinned CI build wheel version, 365 00:13:03,600 --> 00:13:06,959 which is something that depend would 366 00:13:05,040 --> 00:13:09,120 have done automatically. 367 00:13:06,959 --> 00:13:10,800 But one of the biggest reasons to use CI 368 00:13:09,120 --> 00:13:13,519 build wheel, are you sure you are 369 00:13:10,800 --> 00:13:15,839 building your wheels correctly? 370 00:13:13,519 --> 00:13:18,320 CI build wheel does two very important 371 00:13:15,839 --> 00:13:19,920 things you probably aren't doing as part 372 00:13:18,320 --> 00:13:23,040 of your handcrafted CI build wheel 373 00:13:19,920 --> 00:13:25,760 pipeline. Firstly, tests are run using 374 00:13:23,040 --> 00:13:28,240 the built wheel. If you build a wheel 375 00:13:25,760 --> 00:13:30,160 and then run tests, it is probable you 376 00:13:28,240 --> 00:13:32,240 are not actually using the wheel you 377 00:13:30,160 --> 00:13:33,920 built to run your tests. You're using 378 00:13:32,240 --> 00:13:36,560 your local source checkout and local 379 00:13:33,920 --> 00:13:41,200 build artifacts. And that should be the 380 00:13:36,560 --> 00:13:43,360 same unless it isn't. CI build does this 381 00:13:41,200 --> 00:13:44,880 test isolation for you. So you know the 382 00:13:43,360 --> 00:13:46,880 distribution artifact you're uploading 383 00:13:44,880 --> 00:13:49,920 to Pippi is actually the thing you're 384 00:13:46,880 --> 00:13:52,880 testing. Secondly, every wheel that is 385 00:13:49,920 --> 00:13:54,720 produced is also audited and repaired as 386 00:13:52,880 --> 00:13:57,680 part of the build process. If you build 387 00:13:54,720 --> 00:13:59,839 a wheel on your computer, it is trivial 388 00:13:57,680 --> 00:14:02,480 for you to accidentally link in a binary 389 00:13:59,839 --> 00:14:04,000 that exists on your computer but won't 390 00:14:02,480 --> 00:14:05,360 necessarily exist on the machine where 391 00:14:04,000 --> 00:14:08,160 the wheel is ultimately going to be 392 00:14:05,360 --> 00:14:09,839 used. CI build automatically audits 393 00:14:08,160 --> 00:14:12,240 every wheel to make sure this doesn't 394 00:14:09,839 --> 00:14:13,839 happen. And if it does, where it can, it 395 00:14:12,240 --> 00:14:15,519 repairs and rewrites the library 396 00:14:13,839 --> 00:14:18,399 dependencies so that the wheel is 397 00:14:15,519 --> 00:14:20,639 guaranteed to be self-contained. 398 00:14:18,399 --> 00:14:22,880 Now, both of those things are things you 399 00:14:20,639 --> 00:14:24,720 could set up yourself, but in all 400 00:14:22,880 --> 00:14:26,480 likelihood, you won't set up those 401 00:14:24,720 --> 00:14:28,720 protections until after you've published 402 00:14:26,480 --> 00:14:29,839 a wheel that's been broken. With CI 403 00:14:28,720 --> 00:14:32,560 Build Wheel, you get all those 404 00:14:29,839 --> 00:14:34,000 protections for free. It also means that 405 00:14:32,560 --> 00:14:35,839 the complexity of setting up and 406 00:14:34,000 --> 00:14:38,160 managing cross-platform compilation 407 00:14:35,839 --> 00:14:40,320 environments for iOS and Android is 408 00:14:38,160 --> 00:14:43,040 handled for you. From the perspective of 409 00:14:40,320 --> 00:14:45,360 CI buildwhe, iOS and Android are just 410 00:14:43,040 --> 00:14:47,199 another platform. If you've got CI build 411 00:14:45,360 --> 00:14:50,160 configurations that work for desktop, CI 412 00:14:47,199 --> 00:14:52,480 build will likely just work for iOS and 413 00:14:50,160 --> 00:14:53,760 Android as well. To build for Android, 414 00:14:52,480 --> 00:14:54,959 you need to have the Android SDK 415 00:14:53,760 --> 00:14:56,880 installed with the Android home 416 00:14:54,959 --> 00:14:58,720 environment variable set. Uh this can be 417 00:14:56,880 --> 00:15:00,399 either a full Android Studio install or 418 00:14:58,720 --> 00:15:02,639 just the command line tools. You need to 419 00:15:00,399 --> 00:15:04,880 have a Java SDK installed with Java home 420 00:15:02,639 --> 00:15:06,720 defined or at least Java C on your path. 421 00:15:04,880 --> 00:15:08,160 Uh you need curl installed to manage 422 00:15:06,720 --> 00:15:10,240 some downloads and you need patchel 423 00:15:08,160 --> 00:15:12,320 installed to perform some of the postbu 424 00:15:10,240 --> 00:15:14,320 auditing and fixes. 425 00:15:12,320 --> 00:15:16,000 Any Android specific build details can 426 00:15:14,320 --> 00:15:18,160 then be configured with the tool.ci 427 00:15:16,000 --> 00:15:20,880 buildwhe.android table in your piper 428 00:15:18,160 --> 00:15:23,440 autotoml or with the underscore android 429 00:15:20,880 --> 00:15:24,880 suffix on any cipw environment variable. 430 00:15:23,440 --> 00:15:29,040 There are two architectures that are 431 00:15:24,880 --> 00:15:30,399 supported x8664 and arm 64v8a. uh 32-bit 432 00:15:29,040 --> 00:15:31,920 Android is not supported because it's 433 00:15:30,399 --> 00:15:34,800 not a tier three supported platform in 434 00:15:31,920 --> 00:15:36,800 CPython itself. You can build Android 435 00:15:34,800 --> 00:15:38,560 wheels for both architectures on any 436 00:15:36,800 --> 00:15:40,160 Windows, Linux or Mac OS machine, but 437 00:15:38,560 --> 00:15:42,000 only the local architecture will 438 00:15:40,160 --> 00:15:44,240 actually be tested. Uh so if you're on 439 00:15:42,000 --> 00:15:45,519 an x86 Windows machine, only the x86 440 00:15:44,240 --> 00:15:47,199 wheel will actually be tested through 441 00:15:45,519 --> 00:15:49,199 the simulator. 442 00:15:47,199 --> 00:15:50,959 To build for iOS, you need to have Xcode 443 00:15:49,199 --> 00:15:54,160 installed and you also need to install 444 00:15:50,959 --> 00:15:55,680 the iOS SDK add-on uh inside that. iOS 445 00:15:54,160 --> 00:15:58,079 specific build details are configured 446 00:15:55,680 --> 00:16:01,440 with tool.cu.ios iOS table in your 447 00:15:58,079 --> 00:16:03,680 pipro.toml or with the iOS suffix on any 448 00:16:01,440 --> 00:16:05,120 CIBW environment variable. There are 449 00:16:03,680 --> 00:16:08,160 three architectures you need to worry 450 00:16:05,120 --> 00:16:11,680 about with iOS x8664 iPhone simulator, 451 00:16:08,160 --> 00:16:14,560 ARM 64 iPhone simulator and ARM 64 iOS. 452 00:16:11,680 --> 00:16:17,440 Uh iPhone OS iPhone simulator and iPhone 453 00:16:14,560 --> 00:16:19,120 device binaries are not AI compatible. 454 00:16:17,440 --> 00:16:21,920 They are separate targets producing 455 00:16:19,120 --> 00:16:23,199 separate and incompatible wheels even if 456 00:16:21,920 --> 00:16:25,920 they're running on the same CPU 457 00:16:23,199 --> 00:16:28,240 architecture. You can only build iOS 458 00:16:25,920 --> 00:16:30,079 wheels on MacOss because you need Xcode. 459 00:16:28,240 --> 00:16:31,920 Uh, and only again the simulator will 460 00:16:30,079 --> 00:16:33,279 will be tested. As with Android, only 461 00:16:31,920 --> 00:16:35,040 the architecture that matches your 462 00:16:33,279 --> 00:16:36,800 native architecture will be tested. So 463 00:16:35,040 --> 00:16:38,639 on an x86 simulator, if you're on an 464 00:16:36,800 --> 00:16:40,959 older laptop, an ARM 64 simulator on 465 00:16:38,639 --> 00:16:42,720 newer machines. 466 00:16:40,959 --> 00:16:44,240 The complexity of architectures on 467 00:16:42,720 --> 00:16:45,360 mobile platforms means there's some 468 00:16:44,240 --> 00:16:47,839 there's sometimes a need for 469 00:16:45,360 --> 00:16:50,079 architecture specific configuration. Uh, 470 00:16:47,839 --> 00:16:51,680 CI build can do that uh using the 471 00:16:50,079 --> 00:16:54,079 overrides configuration syntax. 472 00:16:51,680 --> 00:16:56,000 Overrides lets you define a pattern of 473 00:16:54,079 --> 00:16:58,000 build targets that match and then 474 00:16:56,000 --> 00:17:00,079 replace or augment settings when that 475 00:16:58,000 --> 00:17:02,240 pattern matches. So for example, you 476 00:17:00,079 --> 00:17:05,199 could specify as we have here that when 477 00:17:02,240 --> 00:17:07,199 targeting any iOS simulator, the build 478 00:17:05,199 --> 00:17:09,919 should redefine path in the build 479 00:17:07,199 --> 00:17:12,240 environment or when targeting any iOS 480 00:17:09,919 --> 00:17:14,079 ARM 64 platform, append an extra 481 00:17:12,240 --> 00:17:16,480 requirement to the test requirements. 482 00:17:14,079 --> 00:17:18,400 Now although these are IO iOS examples 483 00:17:16,480 --> 00:17:19,520 here, the override syntax actually works 484 00:17:18,400 --> 00:17:20,880 everywhere. So you could use it on 485 00:17:19,520 --> 00:17:22,559 Windows, on Android, wherever you need 486 00:17:20,880 --> 00:17:24,240 it. 487 00:17:22,559 --> 00:17:26,160 Some extra details to keep in mind when 488 00:17:24,240 --> 00:17:28,960 configuring tests for mobile platforms. 489 00:17:26,160 --> 00:17:30,640 First, test command must be an a Python 490 00:17:28,960 --> 00:17:33,200 entry point. Essentially, must start 491 00:17:30,640 --> 00:17:35,760 with Python minus M. Android will also 492 00:17:33,200 --> 00:17:38,160 allow Python minus C as an entry point. 493 00:17:35,760 --> 00:17:39,760 On desktop, test command can be a shell 494 00:17:38,160 --> 00:17:41,760 script or any other command line you 495 00:17:39,760 --> 00:17:43,600 might want to invoke. That won't work on 496 00:17:41,760 --> 00:17:45,679 mobile because you don't have a shell 497 00:17:43,600 --> 00:17:48,160 environment. Android and iOS code is 498 00:17:45,679 --> 00:17:49,840 tested using a test bed app, an iOS or 499 00:17:48,160 --> 00:17:52,720 Android application whose sole purpose 500 00:17:49,840 --> 00:17:54,640 is run the test suite. CPython includes 501 00:17:52,720 --> 00:17:56,640 a test bed project for both iOS and 502 00:17:54,640 --> 00:17:58,880 Android and CI build will manage the 503 00:17:56,640 --> 00:18:00,960 process of cloning that test bed so you 504 00:17:58,880 --> 00:18:04,240 can run your test suite as a specific 505 00:18:00,960 --> 00:18:06,480 artifact. You also must define test 506 00:18:04,240 --> 00:18:08,400 sources even if it's empty. Why is this? 507 00:18:06,480 --> 00:18:10,240 Well, at runtime, the test bed app is 508 00:18:08,400 --> 00:18:12,320 running on the iPhone simulator or on 509 00:18:10,240 --> 00:18:15,440 the Android simulator, and it won't have 510 00:18:12,320 --> 00:18:17,200 access to your local code checkout. So, 511 00:18:15,440 --> 00:18:19,280 you need to specify what is going to be 512 00:18:17,200 --> 00:18:22,000 copied into the test bed to be used at 513 00:18:19,280 --> 00:18:24,080 runtime. Uh, on iOS, you also must 514 00:18:22,000 --> 00:18:26,160 define XU build tools. Again, even if 515 00:18:24,080 --> 00:18:28,720 it's empty, Xbuild tools specifies 516 00:18:26,160 --> 00:18:30,160 native local tools like CMake or Ninja 517 00:18:28,720 --> 00:18:32,320 that you want to be available in the 518 00:18:30,160 --> 00:18:33,919 crossplatform build environment. The 519 00:18:32,320 --> 00:18:35,919 wheels that are generated will specify 520 00:18:33,919 --> 00:18:37,039 their OS version compatibility. Uh if 521 00:18:35,919 --> 00:18:38,880 you want to see it at a different 522 00:18:37,039 --> 00:18:40,559 version, you can export the iOS uh 523 00:18:38,880 --> 00:18:42,880 iPhone OS deployment version or Android 524 00:18:40,559 --> 00:18:44,400 API version uh API level environment 525 00:18:42,880 --> 00:18:46,559 variables. 526 00:18:44,400 --> 00:18:47,760 Once you're configured, call CI build. 527 00:18:46,559 --> 00:18:50,960 Pass in the platform you want to build 528 00:18:47,760 --> 00:18:53,440 for-platform iOS-platform Android. And 529 00:18:50,960 --> 00:18:55,200 CI build will create isolated platform 530 00:18:53,440 --> 00:18:56,960 crossplatform build environments. Invoke 531 00:18:55,200 --> 00:18:58,480 a crossplatform build of your wheel. Set 532 00:18:56,960 --> 00:18:59,919 up a test bed application. Copy your 533 00:18:58,480 --> 00:19:01,120 wheel into the test bed. Copy your test 534 00:18:59,919 --> 00:19:02,720 code and test requirements into that 535 00:19:01,120 --> 00:19:05,039 test bed. Compile the test bed. Start a 536 00:19:02,720 --> 00:19:07,039 simulator. Run the test bed. And rinse 537 00:19:05,039 --> 00:19:08,960 and repeat for every supported Python 538 00:19:07,039 --> 00:19:10,880 version. 539 00:19:08,960 --> 00:19:12,320 CI configurations that support mobile 540 00:19:10,880 --> 00:19:13,200 need to be a little bit more complicated 541 00:19:12,320 --> 00:19:14,880 because you actually need to 542 00:19:13,200 --> 00:19:17,039 differentiate between the build platform 543 00:19:14,880 --> 00:19:18,559 and the target platform. To do that, you 544 00:19:17,039 --> 00:19:20,000 either need to define more entries in 545 00:19:18,559 --> 00:19:22,240 the build matrix and configure that 546 00:19:20,000 --> 00:19:24,000 matrix. So there is one build on macOS 547 00:19:22,240 --> 00:19:27,200 to build for macOS and one build on 548 00:19:24,000 --> 00:19:28,720 macOS to build for iOS and so on or you 549 00:19:27,200 --> 00:19:31,200 can split out individual architectures 550 00:19:28,720 --> 00:19:33,120 as matrix targets. Alternatively, you 551 00:19:31,200 --> 00:19:34,960 can specify a separate workflow for your 552 00:19:33,120 --> 00:19:36,559 mobile, iOS, and Android wheels. That 553 00:19:34,960 --> 00:19:38,240 keeps your desktop matrix relatively 554 00:19:36,559 --> 00:19:41,200 simple, but at the cost of repeating 555 00:19:38,240 --> 00:19:42,960 some of your CI configuration. 556 00:19:41,200 --> 00:19:44,400 What platforms can you use to run tests? 557 00:19:42,960 --> 00:19:47,520 Well, as of right now, you can run the 558 00:19:44,400 --> 00:19:49,360 iOS tests on any uh macOS CI 559 00:19:47,520 --> 00:19:50,960 environment. Unfortunately, any day now, 560 00:19:49,360 --> 00:19:52,720 GitHub is going to start deprecating 561 00:19:50,960 --> 00:19:55,600 macOS 13 runners, which is the last 562 00:19:52,720 --> 00:19:57,520 GitHub configurations that run on x8664. 563 00:19:55,600 --> 00:20:00,320 So, that'll lose your ability to test x8 564 00:19:57,520 --> 00:20:02,160 x8664 wheels. As for Android, the 565 00:20:00,320 --> 00:20:03,600 problem is that CI environments are 566 00:20:02,160 --> 00:20:05,520 essentially containerized environments 567 00:20:03,600 --> 00:20:07,679 and the Android emulator doesn't like 568 00:20:05,520 --> 00:20:09,679 running in containerized environments. 569 00:20:07,679 --> 00:20:11,760 Uh, at present, the GitHub actions or 570 00:20:09,679 --> 00:20:13,520 GitHub actions on Linux is the only CI 571 00:20:11,760 --> 00:20:15,440 platform that we know of that supports 572 00:20:13,520 --> 00:20:17,039 running the Android emulator. And even 573 00:20:15,440 --> 00:20:19,520 there, you have to do some preparation 574 00:20:17,039 --> 00:20:22,080 steps to enable KVM virtualization. That 575 00:20:19,520 --> 00:20:24,320 step shouldn't be necessary once Python 576 00:20:22,080 --> 00:20:25,600 314 uh final is out, but at least for 577 00:20:24,320 --> 00:20:27,600 now, you have to add a couple of extra 578 00:20:25,600 --> 00:20:30,960 configuration lines for Android. uh 579 00:20:27,600 --> 00:20:32,640 details are in the CI build wheel docs. 580 00:20:30,960 --> 00:20:33,840 What do you need to do to pack two to a 581 00:20:32,640 --> 00:20:35,679 package to make it compatible with 582 00:20:33,840 --> 00:20:37,600 Python platforms? Well, the good news is 583 00:20:35,679 --> 00:20:40,159 that most Python code and that includes 584 00:20:37,600 --> 00:20:43,039 any Python 3 none any wheel will just 585 00:20:40,159 --> 00:20:44,880 work. Python is ultimately a write once 586 00:20:43,039 --> 00:20:46,880 run anywhere language and for the most 587 00:20:44,880 --> 00:20:48,159 part that holds. The only place it falls 588 00:20:46,880 --> 00:20:50,720 apart is when you start to do things 589 00:20:48,159 --> 00:20:51,919 that are platform specific in code. The 590 00:20:50,720 --> 00:20:54,400 first thing to look out for is any 591 00:20:51,919 --> 00:20:56,559 references to cy.platform or os.name or 592 00:20:54,400 --> 00:20:58,559 platform.system. If you've got code that 593 00:20:56,559 --> 00:20:59,760 is making decisions based upon platform, 594 00:20:58,559 --> 00:21:01,840 you're going to need to add logic 595 00:20:59,760 --> 00:21:04,000 branches to accommodate iOS and Android 596 00:21:01,840 --> 00:21:05,840 as possible responses from cy.platform 597 00:21:04,000 --> 00:21:07,360 and platform.system. 598 00:21:05,840 --> 00:21:09,520 The other big thing to watch out for is 599 00:21:07,360 --> 00:21:11,840 uses of subprocess multipprocessing in 600 00:21:09,520 --> 00:21:14,640 any other API that creates a subprocess 601 00:21:11,840 --> 00:21:16,080 like OS.spawn and OS.fork. iOS and 602 00:21:14,640 --> 00:21:17,600 Android don't do symmetric 603 00:21:16,080 --> 00:21:19,280 multipprocessing. Essentially, the app 604 00:21:17,600 --> 00:21:21,039 that is in the foreground is the only 605 00:21:19,280 --> 00:21:22,799 app that is running at any given point 606 00:21:21,039 --> 00:21:25,039 in time. 607 00:21:22,799 --> 00:21:26,720 So you can't call subprocess to do 608 00:21:25,039 --> 00:21:28,240 something in the background or offload 609 00:21:26,720 --> 00:21:30,240 work to other CPUs by using 610 00:21:28,240 --> 00:21:31,679 multipprocessing. If your package does 611 00:21:30,240 --> 00:21:33,280 that, you'll either need to disable that 612 00:21:31,679 --> 00:21:35,200 functionality or find a different way to 613 00:21:33,280 --> 00:21:37,919 do that task, such as using threads 614 00:21:35,200 --> 00:21:40,000 instead of using processes. And those 615 00:21:37,919 --> 00:21:41,360 caveats also apply to your test suite. 616 00:21:40,000 --> 00:21:43,440 If you've got tests that create 617 00:21:41,360 --> 00:21:45,360 subprocesses, those tests won't work on 618 00:21:43,440 --> 00:21:47,039 mobile. If you need to, you may need to 619 00:21:45,360 --> 00:21:48,880 add additional platformbased skips to 620 00:21:47,039 --> 00:21:50,559 your test suite to move around tests 621 00:21:48,880 --> 00:21:53,520 that are trying to do subprocessbased 622 00:21:50,559 --> 00:21:55,280 activity and so on. But my experience 623 00:21:53,520 --> 00:21:56,880 has been that the Python code is not the 624 00:21:55,280 --> 00:21:59,200 hard part of porting a package to 625 00:21:56,880 --> 00:22:01,200 mobile. The hard part is the non-Python 626 00:21:59,200 --> 00:22:04,000 code and the builds of that code. The 627 00:22:01,200 --> 00:22:05,600 benefit of Pep 57 is that Python doesn't 628 00:22:04,000 --> 00:22:08,159 have to care how to build your package. 629 00:22:05,600 --> 00:22:10,159 It just calls the PEP 517 interface. The 630 00:22:08,159 --> 00:22:12,480 downside is that package maintainers do 631 00:22:10,159 --> 00:22:14,240 need to care. Whatever tool you're using 632 00:22:12,480 --> 00:22:16,880 to build your package needs to be able 633 00:22:14,240 --> 00:22:18,720 to target iOS and Android. Setup tools 634 00:22:16,880 --> 00:22:20,080 has good support for iOS and Android. 635 00:22:18,720 --> 00:22:22,000 The Python environment that is used to 636 00:22:20,080 --> 00:22:24,000 run builds has been patched so that it 637 00:22:22,000 --> 00:22:25,440 pretends to be the target platform. So, 638 00:22:24,000 --> 00:22:27,360 it's relatively straightforward to add 639 00:22:25,440 --> 00:22:29,840 extra code branches to add support for 640 00:22:27,360 --> 00:22:31,760 mobile platforms. CPython even provides 641 00:22:29,840 --> 00:22:33,120 shims to make it easier to configure iOS 642 00:22:31,760 --> 00:22:34,720 compilers using naming schemes that 643 00:22:33,120 --> 00:22:37,760 match the expectations of a lot of Unix 644 00:22:34,720 --> 00:22:39,120 tooling. But in addition to configuring 645 00:22:37,760 --> 00:22:41,120 the build of your package, you also need 646 00:22:39,120 --> 00:22:43,760 to ensure that any binary dependencies 647 00:22:41,120 --> 00:22:45,280 are available. For example, pillow is a 648 00:22:43,760 --> 00:22:47,520 Python package. It's configured using 649 00:22:45,280 --> 00:22:50,240 setup tools. That build almost worked 650 00:22:47,520 --> 00:22:52,799 out of the box. But pillow also uses lib 651 00:22:50,240 --> 00:22:54,960 png and libtiff and freype which are all 652 00:22:52,799 --> 00:22:57,919 non-Python binary dependencies and they 653 00:22:54,960 --> 00:23:00,159 all need to be compiled for each iOS and 654 00:22:57,919 --> 00:23:02,240 Android architecture. And in addition, 655 00:23:00,159 --> 00:23:04,159 when building for iOS, the dependencies 656 00:23:02,240 --> 00:23:06,159 or the dependency libraries need to be 657 00:23:04,159 --> 00:23:08,880 static. uh this is an unfortunate side 658 00:23:06,159 --> 00:23:10,799 effect of how iOS loads libraries. The 659 00:23:08,880 --> 00:23:12,080 compilation or the the complication is 660 00:23:10,799 --> 00:23:13,919 that these dependencies might use 661 00:23:12,080 --> 00:23:15,360 configure and they might use cmake. They 662 00:23:13,919 --> 00:23:17,840 might use a custom pearl script to 663 00:23:15,360 --> 00:23:20,080 manage their builds. If it's a configure 664 00:23:17,840 --> 00:23:22,640 based project, you can usually pass in-- 665 00:23:20,080 --> 00:23:24,159 host and- build to configure for the 666 00:23:22,640 --> 00:23:25,919 platform you want to build for and the 667 00:23:24,159 --> 00:23:27,360 platform that you're building on. Uh 668 00:23:25,919 --> 00:23:29,919 note that you have to use the GNU 669 00:23:27,360 --> 00:23:33,360 preferred AR64 format for describing the 670 00:23:29,919 --> 00:23:36,640 ARM 64 architecture because GNU uh you 671 00:23:33,360 --> 00:23:38,799 can use AR64 Apple iOS for both the 672 00:23:36,640 --> 00:23:40,240 device and the simulator as long as the 673 00:23:38,799 --> 00:23:42,159 rest of your path configurations are 674 00:23:40,240 --> 00:23:45,200 correct. If you do need to differentiate 675 00:23:42,159 --> 00:23:48,240 the simulator, AR64 Apple iOS simulator 676 00:23:45,200 --> 00:23:50,640 is the Apple specified tag. Um but 677 00:23:48,240 --> 00:23:52,480 config.sub doesn't recognize that as a 678 00:23:50,640 --> 00:23:54,400 valid option despite me submitting the 679 00:23:52,480 --> 00:23:56,400 patch multiple times over the last 2 680 00:23:54,400 --> 00:23:58,080 years. So you may need to patch 681 00:23:56,400 --> 00:24:00,080 config.sub. 682 00:23:58,080 --> 00:24:02,720 You may also be able to use d- disable 683 00:24:00,080 --> 00:24:05,919 shared and or- enable static to force a 684 00:24:02,720 --> 00:24:07,360 staticon build. CMake has native iOS 685 00:24:05,919 --> 00:24:08,960 support, but it doesn't do a great job 686 00:24:07,360 --> 00:24:10,880 of differentiating device and simulator 687 00:24:08,960 --> 00:24:12,720 and it seems to expect the use of Xcode 688 00:24:10,880 --> 00:24:14,159 projects a lot of the time. I found the 689 00:24:12,720 --> 00:24:15,919 best approach is to lean on macOS 690 00:24:14,159 --> 00:24:17,760 configurations, but pointing at the iOS 691 00:24:15,919 --> 00:24:19,360 version numbers and system routes. Uh 692 00:24:17,760 --> 00:24:22,000 you can use XC run to get you that 693 00:24:19,360 --> 00:24:24,240 system route. Mison is a cross 694 00:24:22,000 --> 00:24:26,480 compilation uh uh environment based upon 695 00:24:24,240 --> 00:24:28,080 passing a cross file. You pass that in 696 00:24:26,480 --> 00:24:29,760 as an argument and the values that are 697 00:24:28,080 --> 00:24:31,760 provided in the cross file will be used 698 00:24:29,760 --> 00:24:34,000 instead of any automatically detected 699 00:24:31,760 --> 00:24:36,080 platform version values. If your 700 00:24:34,000 --> 00:24:39,520 dependency doesn't use configure cmake 701 00:24:36,080 --> 00:24:42,480 or mison uh good luck, have fun. It very 702 00:24:39,520 --> 00:24:43,760 much depends. The biggest gotcha when 703 00:24:42,480 --> 00:24:46,320 building third party libraries, 704 00:24:43,760 --> 00:24:48,559 compilers are really good at identifying 705 00:24:46,320 --> 00:24:50,880 CPU architectures. They are not good at 706 00:24:48,559 --> 00:24:53,039 identifying platforms. If you are trying 707 00:24:50,880 --> 00:24:55,520 to build an iOS binary wheel and you 708 00:24:53,039 --> 00:24:57,600 have a MacOSS ARM 64 version of a 709 00:24:55,520 --> 00:25:00,640 library on your path and you try to 710 00:24:57,600 --> 00:25:02,720 build the iOS library, it's really easy 711 00:25:00,640 --> 00:25:04,559 to accidentally include the macOS 712 00:25:02,720 --> 00:25:07,279 version of the library in your iOS 713 00:25:04,559 --> 00:25:09,279 wheel. For this reason, CI build 714 00:25:07,279 --> 00:25:11,520 explicitly clears your path during 715 00:25:09,279 --> 00:25:13,760 builds to only include paths that are 716 00:25:11,520 --> 00:25:15,600 known to be appropriate for iOS. So you 717 00:25:13,760 --> 00:25:18,400 may need to configure CI build wheel to 718 00:25:15,600 --> 00:25:20,080 explicitly reallow certain paths if 719 00:25:18,400 --> 00:25:22,080 you've got local binaries for iOS in 720 00:25:20,080 --> 00:25:23,120 some location. All right. So you've 721 00:25:22,080 --> 00:25:25,440 built your wheels, you've published them 722 00:25:23,120 --> 00:25:27,279 to Pippi. How do you use them? Well, pip 723 00:25:25,440 --> 00:25:29,600 allows you to just install iOS and 724 00:25:27,279 --> 00:25:30,960 Android wheels. Pass in d-platform. You 725 00:25:29,600 --> 00:25:33,039 can specify the wheel tag that you want 726 00:25:30,960 --> 00:25:35,039 to satis satisfy with your downloads. 727 00:25:33,039 --> 00:25:36,320 That will put iOS compatible wheels into 728 00:25:35,039 --> 00:25:37,840 whatever virtual environment you're 729 00:25:36,320 --> 00:25:39,840 currently using, which will be a local 730 00:25:37,840 --> 00:25:42,159 MacOSS environment. So you might want to 731 00:25:39,840 --> 00:25:44,240 specify- target to specify an output 732 00:25:42,159 --> 00:25:46,559 directory. So only iOS content is in 733 00:25:44,240 --> 00:25:49,120 that directory. Also at the moment at 734 00:25:46,559 --> 00:25:51,520 least d-platform isn't transitive. If 735 00:25:49,120 --> 00:25:53,600 you install an iOS version of my package 736 00:25:51,520 --> 00:25:55,760 and my package needs to install other 737 00:25:53,600 --> 00:25:57,679 binary requirements, pip will 738 00:25:55,760 --> 00:25:59,919 unfortunately install the macOS version 739 00:25:57,679 --> 00:26:02,159 of those downstream requirements. So you 740 00:25:59,919 --> 00:26:04,480 may need to use- noep to prevent that 741 00:26:02,159 --> 00:26:06,159 dependency training from happening. If 742 00:26:04,480 --> 00:26:08,320 you're a UV user, well, support 743 00:26:06,159 --> 00:26:09,919 literally landed last week for iOS and 744 00:26:08,320 --> 00:26:12,320 Android as platform tags. Consult your 745 00:26:09,919 --> 00:26:14,640 UV documentation for details. or if 746 00:26:12,320 --> 00:26:16,000 you're a briefcase user, uh just add the 747 00:26:14,640 --> 00:26:17,679 package to your apps requires and you're 748 00:26:16,000 --> 00:26:19,760 done. All all that stuff is managed for 749 00:26:17,679 --> 00:26:21,120 you. What's next when it comes to 750 00:26:19,760 --> 00:26:22,640 packaging and mobile packaging in 751 00:26:21,120 --> 00:26:24,559 particular? Well, one big issue we 752 00:26:22,640 --> 00:26:26,720 haven't tackled yet are Python packages 753 00:26:24,559 --> 00:26:28,320 that are built using Rust. Rust is a 754 00:26:26,720 --> 00:26:30,400 compiler tool chain unto itself and it 755 00:26:28,320 --> 00:26:32,080 has support for iOS and Android, but we 756 00:26:30,400 --> 00:26:34,159 need to work out what modifications to 757 00:26:32,080 --> 00:26:36,080 the Rust Pep 517 build backends like 758 00:26:34,159 --> 00:26:38,080 matchin and setup tools rust are 759 00:26:36,080 --> 00:26:39,279 required to support those platforms. Uh 760 00:26:38,080 --> 00:26:40,960 we're also going to be looking at ways 761 00:26:39,279 --> 00:26:42,799 to consolidate and standardize the way 762 00:26:40,960 --> 00:26:45,440 that crossplatform builds actually work. 763 00:26:42,799 --> 00:26:47,360 Uh crossplatform builds uh on Android 764 00:26:45,440 --> 00:26:49,120 and iOS have very similar constraints, 765 00:26:47,360 --> 00:26:51,120 but the CI build will implementations 766 00:26:49,120 --> 00:26:52,720 take very slightly different approaches. 767 00:26:51,120 --> 00:26:54,000 However, they share enough similarities 768 00:26:52,720 --> 00:26:55,679 that there is an opportunity to 769 00:26:54,000 --> 00:26:57,600 consolidate those similarities into a 770 00:26:55,679 --> 00:26:58,880 shared codebase. I've already started 771 00:26:57,600 --> 00:27:00,880 working on this with a project called 772 00:26:58,880 --> 00:27:02,240 Crossbuild. Uh Crossbuild is a work on 773 00:27:00,880 --> 00:27:03,679 progress. I've published cross build in 774 00:27:02,240 --> 00:27:05,360 a sort of pre-alpha state so that I have 775 00:27:03,679 --> 00:27:07,120 something to discuss uh in the coming 776 00:27:05,360 --> 00:27:09,840 week at the CPython core team summit in 777 00:27:07,120 --> 00:27:12,559 the UK. Uh that might lead to a new pep 778 00:27:09,840 --> 00:27:14,880 enhancing pep 517 to add crossplatform 779 00:27:12,559 --> 00:27:16,159 support. Uh but you can already use 780 00:27:14,880 --> 00:27:18,080 cross build to build a virtual 781 00:27:16,159 --> 00:27:20,559 environment that can fake being iOS 782 00:27:18,080 --> 00:27:22,240 Android or in scripton and pip install 783 00:27:20,559 --> 00:27:23,520 just works in that environment 784 00:27:22,240 --> 00:27:26,240 installing platform or target 785 00:27:23,520 --> 00:27:28,000 appropriate uh platforms uh packages and 786 00:27:26,240 --> 00:27:30,960 all their dependencies including all the 787 00:27:28,000 --> 00:27:32,480 uh the transitive issues. So, our call 788 00:27:30,960 --> 00:27:34,400 to action, we need your help. Malcolm 789 00:27:32,480 --> 00:27:36,240 and I are only two people and there is a 790 00:27:34,400 --> 00:27:38,000 limit to how many projects on Pipi where 791 00:27:36,240 --> 00:27:41,120 can we can port if we're the only people 792 00:27:38,000 --> 00:27:43,840 doing it. But the tools now exist. You, 793 00:27:41,120 --> 00:27:45,520 yes, you can support add support for 794 00:27:43,840 --> 00:27:47,360 mobile platforms to the Python packages 795 00:27:45,520 --> 00:27:49,520 that you use. If you visit 796 00:27:47,360 --> 00:27:52,480 beware.org/mobile wheels, you will see 797 00:27:49,520 --> 00:27:54,159 the 360 most popular packages on Pippi 798 00:27:52,480 --> 00:27:56,000 by download categorized as to whether 799 00:27:54,159 --> 00:27:57,679 they are mobile ready. Pick a package 800 00:27:56,000 --> 00:27:59,840 that's in yellow. See if it uses CI 801 00:27:57,679 --> 00:28:02,799 build. if it does add an iOS and Android 802 00:27:59,840 --> 00:28:04,799 configuration as a pull request. Now, 803 00:28:02,799 --> 00:28:05,760 ordinarily this would be where I say if 804 00:28:04,799 --> 00:28:07,039 you're looking for something to sprint 805 00:28:05,760 --> 00:28:08,720 on, this is one way to contribute and 806 00:28:07,039 --> 00:28:10,000 I'll be around to help out. But 807 00:28:08,720 --> 00:28:11,200 unfortunately, I won't be here for the 808 00:28:10,000 --> 00:28:12,720 sprints because I'm on a plane tomorrow 809 00:28:11,200 --> 00:28:14,640 to fly to the UK for the core team 810 00:28:12,720 --> 00:28:17,200 sprint. But it's still a great place to 811 00:28:14,640 --> 00:28:18,640 contribute. See how you go. If you'd 812 00:28:17,200 --> 00:28:20,080 like to know more, the Beware project is 813 00:28:18,640 --> 00:28:21,520 a great place to get started with Python 814 00:28:20,080 --> 00:28:22,960 on mobile platforms. We've also got a 815 00:28:21,520 --> 00:28:24,399 Discord plus discussion forums and 816 00:28:22,960 --> 00:28:26,640 ticket trackers for the projects. 817 00:28:24,399 --> 00:28:29,279 Details on the website beware.org or you 818 00:28:26,640 --> 00:28:31,120 can go to the CI build CI build wheel 819 00:28:29,279 --> 00:28:32,480 documentation directly. It's part of the 820 00:28:31,120 --> 00:28:34,960 resources of the Python packaging 821 00:28:32,480 --> 00:28:38,279 authority or the PIPA. Thank you all 822 00:28:34,960 --> 00:28:38,279 very much.