coldtype / coldtype

cross-platform display typography in python
https://coldtype.xyz
Apache License 2.0
293 stars 22 forks source link

viewer dependencies (freetype-py & pyglfw) do not include arm binaries for m1 macs #70

Closed Mark2Mark closed 2 years ago

Mark2Mark commented 2 years ago

Thanks so much for looking into #27 @stenson! I really appreciate all your help.

On a follow up on that: I could now get a few steps further, however I had to upgrade pip after it threw a ton of new errors at me. These are for later reference, perhaps this could be added to the install instructions.

either this pip install --upgrade pip setuptools wheel or into the venv: {...}/venv/bin/python3.9 -m pip install --upgrade pip not sure, I used my absolute path with was proposed by the terminal error. It seems that the PIP inside of the venv always needs to be updated. I tried again after I updated my local pip, and it threw the error about an outdated pip again.

then I do the pip install "coldtype[viewer]" and can’t see any errors in the log.

However, if trying to continue with coldtype demo

I get a bunch of errors again

Traceback (most recent call last):
  File "/{MY_PATH}/Coldtype/venv/lib/python3.9/site-packages/freetype/raw.py", line 49, in <module>
    _lib = ctypes.CDLL(filename)
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/ctypes/__init__.py", line 374, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: dlopen(libfreetype.dylib, 6): image not found

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/{MY_PATH}/Coldtype/venv/bin/coldtype", line 5, in <module>
    from coldtype.renderer import main
  File "/{MY_PATH}/Coldtype/venv/lib/python3.9/site-packages/coldtype/__init__.py", line 4, in <module>
    from coldtype.text import *
  File "/{MY_PATH}/Coldtype/venv/lib/python3.9/site-packages/coldtype/text/__init__.py", line 3, in <module>
    import coldtype.text.composer
  File "/{MY_PATH}/Coldtype/venv/lib/python3.9/site-packages/coldtype/text/composer.py", line 7, in <module>
    from coldtype.text.reader import Style, StyledString, FittableMixin, Font, normalize_font_path, SegmentedString,  _PenClass, _PensClass
  File "/{MY_PATH}/Coldtype/venv/lib/python3.9/site-packages/coldtype/text/reader.py", line 42, in <module>
    from coldtype.fontgoggles.font.otfFont import OTFFont
  File "/{MY_PATH}/Coldtype/venv/lib/python3.9/site-packages/coldtype/fontgoggles/font/otfFont.py", line 7, in <module>
    from ..misc.ftFont import FTFont
  File "/{MY_PATH}/Coldtype/venv/lib/python3.9/site-packages/coldtype/fontgoggles/misc/ftFont.py", line 5, in <module>
    import freetype
  File "/{MY_PATH}/Coldtype/venv/lib/python3.9/site-packages/freetype/__init__.py", line 24, in <module>
    from freetype.raw import *
  File "/{MY_PATH}/Coldtype/venv/lib/python3.9/site-packages/freetype/raw.py", line 52, in <module>
    raise RuntimeError('Freetype library not found')
RuntimeError: Freetype library not found

freetype is installed, running pip install freetype-py claims Requirement already satisfied: freetype-py in ./venv/lib/python3.9/site-packages (2.2.0). It also shows in the list pip list -l as freetype-py 2.2.0

Originally posted by @Mark2Mark in https://github.com/goodhertz/coldtype/issues/27#issuecomment-945078202

Mark2Mark commented 2 years ago

I am wondering if it might be caused by the ARM (M1 Mac)?

While trying to find a fix, I copied a libfreetype.6.dylib (the one and only libfreetype.dylib I could find on my mac) into the venv freetype folder and renamed it to libfreetype.dylib (as this is what the raw.py searches for.

Then my error is

...
{...}/venv/lib/python3.9/site-packages/freetype/libfreetype.dylib, 6): no suitable image found.  Did find:
    {...}/venv/lib/python3.9/site-packages/freetype/libfreetype.dylib: mach-o, but wrong architecture
    {...}/venv/lib/python3.9/site-packages/freetype/libfreetype.dylib: mach-o, but wrong architecture
Mark2Mark commented 2 years ago

Seems to be ARM related

I reinstalled freetype with brew, and located it in /opt/homebrew/Cellar/freetype/2.11.0/lib/libfreetype.6.dylib

libfreetype.dylib.zip

Then I copied that file to the venv {...}/venv/lib/python3.9/site-packages/freetype/libfreetype.dylib and renamed it to libfreetype.dylib

Now I don’t get any errors anymore on coldtype demo

But now it is stuck on ... watching venv/lib/python3.9/site-packages/coldtype/demo/demo.py for changes ... while no window appears as expected. No python app in the Dock.

When I open the demo.py and apply some changes to it, they are properly tracked: >>> resave: venv/lib/python3.9/site-packages/coldtype/demo/demo.py

Is there a way to open the viewer?

stenson commented 2 years ago

Hi Mark — sorry for the all the trouble here! Unfortunately I haven't seen any of these errors before and I don't have access to an M1 computer, although I know of at least one M1 computer that has successfully run an older version of coldtype, so I'm not sure what the issue there would be. Could definitely be M1, though, since the prebuilt binaries that some python libraries come with might not be built properly for M1, which is what the freetype-py issue sounds like (libfreetype.dylib: mach-o, but wrong architecture) except that I can't find anything online about people freetype-py doesn't work on M1 which seems a little odd.

What OS version are you running?

And if you are comfortable modifying the coldtype source, if you locate the coldtype/renderer/__init__.py file in your venv, could you try modifying the initialize_gui_and_server function to have an additional print at the beginning, so it'd look something like this:

    def initialize_gui_and_server(self):
        self.winmans.add_viewers()

        print("GLSK >>>", self.winmans.glsk)

The glsk there is the viewer (a GLFW window), so if that's None then it's not being created, which could happen if the skia-python or glfw libraries aren't being installed properly.

Mark2Mark commented 2 years ago

Hi @stenson Thanks so much for your quick reply.

I think we’re pretty close, as the M1 issue was apparently semi-solved by me dumping the latest dylib in there myself. (I think you could bundle the newest freetype as it ships from here (that’s the one I uploaded as a zip in my comment above). There it’s mentioned to be compatible with Silicon and Big Sur.

I was happy to add your debug print, however it prints GLSK >>> None

Can I somehow install skia-python and or glfw in a way that coldtype can use it?

Thanks so so much for all your help, I am still so excited to get it running. Never saw it on my screen yet :D (and then clicked the wrong button that closed the issue :D)

stenson commented 2 years ago

Nice! Yeah that's helpful. I've just been reading about M1 & python and it seems like lots of people are using the rosetta translation environment for x86 programs, particularly for python libraries that rely on c extensions (like freetype, glfw, and skia). So I guess that could be one alterate route you could take (run the virtualenv entirely in a rosetta-emulated terminal), though I don't have any first hand experience with that.

According to pypi, skia-python does have an arm variant, so I think you should be fine there, though to verify it's installed properly, you can just run an interactive python prompt (python), and then just type in import skia — if that works you should be all good.

GLFW, on the other hand, doesn't seem to have an arm build on pypi yet, so I think that's probably the sticking point. If you run a python interactive prompt and just run import glfw what happens?

Mark2Mark commented 2 years ago

Thanks!

import skia does work

import glfw thows:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "{...}/venv/lib/python3.9/site-packages/glfw/__init__.py", line 43, in <module>
    raise ImportError("Failed to load GLFW3 shared library.")
ImportError: Failed to load GLFW3 shared library.

Now I was building a GLFW with the commands as state here and got a folder with a build that might work (I can run the example files in {...}/glfw-3.3.2/build/examples). Do you know which files I could use from that build to exchange with the ones somewhere in {...}/venv/lib/python3.9/site-packages/glfw?

stenson commented 2 years ago

Thanks for trying that out! That makes sense — do you know which version of glfw you have installed in the venv? It looks like it's a recent issue on the glfw repo itself (https://github.com/FlorianRhiem/pyGLFW/issues/57), so you might just need to do a pip install glfw==2.3.0

Mark2Mark commented 2 years ago

Thanks so much.

I couldn’t find the version, but I ran this pip insall and it seems it was already that version: Requirement already satisfied: glfw==2.3.0 in ./venv/lib/python3.9/site-packages (2.3.0)

stenson commented 2 years ago

Hmm, yeah I guess that should have the fix then. Do you know where glfw is installed to in your homebrew?

Mark2Mark commented 2 years ago

I don’t have it installed in homebrew (generally I try to not use homebrew anyway, just got it for a necessary web dev project).

But I did that build as mentioned here

Now I was building a GLFW with the commands as state here and got a folder with a build that might work (I can run the example files in {...}/glfw-3.3.2/build/examples). which just sits in a folder. If I knew if I could pick a few files out of that and dump it into the venv …

Ah, so sory for that …

stenson commented 2 years ago

Yeah, on a pre M1 mac (or on an M1 mac using rosetta) I don't think homebrew is actually necessary at all for a working coldtype installation, it's just that these particular packages (freetype-py and glfw) don't have a setup yet for the dedicated m1 architecture (arm), which I think is understandable given how limited the m1 ecosystem still is — so ideally, none of this would be necessary, but is for the moment until those projects have prebuilt arm wheels available on pypi.

I think if the python glfw can find the homebrew-installed glfw c dylib (libglfw.3.dylib I think is what it would be called), then you theoretically would be all set, which I think is the idea behind that pyGLFW issue linked to above. Could you try the same thing you did for freetype, moving the homebrew-installed dylib into the venv? i.e. venv/lib/python3.9/site-packages/glfw/libglfw.3.dylib

(Apologies if I'm misunderstanding the glfw issue, and thanks for sticking with the install!)

Mark2Mark commented 2 years ago

I did the file copying now with the Homebrew one and got a tiny bit futher with a next error now. Then I found the same-named libglfw in the arm build (again see see my quoted part above) and it shows the same error.

But I got the feeling we’re closer

❯ coldtype demo
... watching venv/lib/python3.9/site-packages/coldtype/demo/demo.py for changes ...
GLSK >>> <coldtype.renderer.winman.glfwskia.WinmanGLFWSkia object at 0x110427850>
[1]    62709 segmentation fault  coldtype demo

After the error, an NSAlert pops up, telling me that "Python quit unexpectedly."

Thank you so much for sticking with me, I know how annoying it is to debug such things, especially remotely. I really appreciate all your help!


Next thing I did to start debugging was adding print(_glfw) in line 45 of {...}/venv/lib/python3.9/site-packages/glfw/__init__.py

That shows me, that the dylib is actually not called from this folder, but from the homebrew location as it prints <CDLL '/opt/homebrew/Cellar/glfw/3.3.4/lib/libglfw.3.3.dylib', handle 131e614e0 at 0x106cf2e50>

So I removed the dylib from the coldtype designated venv/lib/python3.9/site-packages/glfw/ and tried again.

No change, but then I copied the dylib from that self built glfw into that homebrew folder, and then, the entire error log is:

❯ coldtype demo
<CDLL '/opt/homebrew/Cellar/glfw/3.3.4/lib/libglfw.3.3.dylib', handle 131e614e0 at 0x106cf2e50>
... watching venv/lib/python3.9/site-packages/coldtype/demo/demo.py for changes ...
/{...}/venv/lib/python3.9/site-packages/glfw/__init__.py:835: GLFWError: (65544) b'Cocoa: Failed to find service port for display'
  warnings.warn(message, GLFWError)
GLSK >>> <coldtype.renderer.winman.glfwskia.WinmanGLFWSkia object at 0x1072e2d00>
[1]    99809 segmentation fault  coldtype demo

(note the GLFWError: (65544) b'Cocoa: Failed to find service port for display', which after some googling appears again to be some sort of M1 issue.) I am almost desparate, but will try digging. Once we got it, I am happy to summarize the M1 required steps for other users.


Now I saw that there is an even newer version of glfw that might adress that, I tried again to build a M1 compatible version:

❯ https://github.com/glfw/glfw/releases/download/3.3.4/glfw-3.3.4.zip > glfw-3.3.4.zip
❯ unzip glfw-3.3.4.zip  
❯ cd glfw-3.3.4/ 
❯ mkdir build
❯ cd build
❯ export MACOSX_DEPLOYMENT_TARGET=11.4
❯ cmake -D CMAKE_BUILD_TYPE=Release -D GLFW_NATIVE_API=1 -D CMAKE_OSX_ARCHITECTURES="x86_64;arm64" -D ❯ BUILD_SHARED_LIBS=ON -D CMAKE_C_COMPILER=clang ../
❯ make

And then I am back to the initial Error with the Segmentation fault, just not the GLFWError anymore.

When I pip install glfw, I can run the example python file from here. It opens a black window. This also works even when I run the example code as a py file from the virtual environment. So I assume the glfw itself seems to work.

❯ cd {...}/Coldtype
❯ source venv/bin/activate
❯ python glfw_test.py

So I am stuck at

❯ coldtype demo
... watching venv/lib/python3.9/site-packages/coldtype/demo/demo.py for changes ...
GLSK >>> <coldtype.renderer.winman.glfwskia.WinmanGLFWSkia object at 0x1137e6d00>
[1]    16591 segmentation fault  coldtype demo
stenson commented 2 years ago

Hi Mark — thanks for digging into this! Unfortunately my home internet's been out the last couple days so I haven't been able to look into it myself, will do some more digging as soon as I get internet back tho!

Could very well just be that I'm setting some glfw window value incorrectly for m1 in the glfwskia.py file, but very hard to guess at which one it might be without being able to repro the issues locally (), since the technique would just be to put in print statements and see how far it gets before the seg fault — could definitely do that in a video screenshare at some point if you're amenable to the idea (but again i don't have enough internet at the moment to do that successfully — will post back here again once i'm fully back online)

Mark2Mark commented 2 years ago

Hi Rob, happy to hear from you. That sounds great. I am also happy to dump some prints in places that you suspect. I was about to do that anyway, but I might need a little direction where to start. Otherwise we can also do the screen thingy, however I am in CE(S)T, so syncing times might be tricky.

So long, good luck with the internet! I’m gonna poke the glfwskia.py in the meantime :D


Found it and got it running.

stenson commented 2 years ago

Good to hear! That makes sense, that same code has been very finicky on Windows as well. You could also try doing a pin and False: there to just short-circuit the window-pin functionality completely and have the window always appear at the top-left corner, which should mean it's fully visible (hopefully)

Does the coldtype demo work now with all this setup?

jo-lang commented 2 years ago

hello and sorry to burst into this there might be tiny chance I could help track down the issue. I am using coldtype on a M1 MB Air, OS 11.5.2, python3.9.6.

I just tried to install coldtype as demonstrated in the how to video and it works without any issues. That might be due to a lot of libraries being cached.

Here is the report of the versions of installed libraries.

Successfully installed PyOpenGL-3.1.5 SimpleWebSocketServer-0.1.1 appdirs-1.4.4 booleanOperations-0.9.0 cffsubr-0.2.8 coldtype-0.7.3 cu2qu-1.6.7.post1 defcon-0.9.0 easing-functions-1.0.4 fontMath-0.8.1 fontParts-0.9.11 fontPens-0.2.4 fonttools-4.27.1 freetype-py-2.2.0 fs-2.4.13 glfw-2.3.0 lxml-4.6.3 mido-1.2.10 more-itertools-8.10.0 numpy-1.21.2 pybind11-2.8.0 pyclipper-1.3.0 python-bidi-0.4.2 pytz-2021.3 six-1.16.0 skia-pathops-0.7.1 skia-python-87.3 timecode-1.3.1 ufo2ft-2.25.1 uharfbuzz-0.18.0 watchdog-1.0.2
Mark2Mark commented 2 years ago

Ha, I thought I need the current monitor rect, so I installed pyobjc and asked for my screen

pip install -U PyObjC

from AppKit import NSScreen
nsScreenRect = NSScreen.mainScreen().frame()
x, y, width, height = nsScreenRect.origin.x, nsScreenRect.origin.y, nsScreenRect.size.width, nsScreenRect.size.height
work_rect = Rect((int(x), int(y)), (int(width), int(height)))

That also prevents the error, but the window appears again more than half offscreen.

However pin and False: totally did it.

YES it is working now. Amazing. Love to see it finally alive 🧟😍

Mark2Mark commented 2 years ago

Thank you all for your help. Unfortunately now I don’t know if it was just this one little line pin and False:. I will try to start from scratch tomorrow and see if that already solves it. Otherwise I’ll go through the steps and make a list here of M1 requirements. It could be that the homebrew and/or self-built dylib was also necesary.

stenson commented 2 years ago

@Mark2Mark great to hear it's working! My guess is that pin and False might be a big part of it, although wouldn't solve the needing-to-homebrew-install for freetype & glfw

@jo-lang thanks for chiming in! very helpful to get another m1 perspective in here — did you have rosetta enabled when you built the virtualenv?

jo-lang commented 2 years ago

@stenson rosetta was installed at the time of the virtualevn build but I cannot tell if it actually was used. unfortunately it is not that straight forward to uninstall rosetta for a quick test.

Mark2Mark commented 2 years ago

Before turning the gathered info about dependencies into a proper list, i just quickly dump more things here:

TODO: Turn into issues:

stenson commented 2 years ago

thanks for the info @jo-lang!

@Mark2Mark any chance you'd be willing to try installing coldtype on your m1 machine using an intel version of an older python? (Or maybe you have a python3.7 or a python3.8 already installed on your computer?) That could be one way to make sure your computer is opting into x86 binaries, which I think is what's happening for @jo-lang and is why there are no issues there with the default install, i.e. if you run something like python3.8 -m venv venv and then activate and use that environment to install coldtype, it should make an intel-based venv for you that forces python to install the x86 versions of the coldtype dependencies. If you don't have an older version, you could try https://www.python.org/downloads/release/python-389/

Other good news is I now have internet & an m1 computer on the way, so should be able to test things myself pretty soon.

Mark2Mark commented 2 years ago

Hey @stenson

First of all: Thank 👏 you 👏 so 👏 much 👏 Coldtype is freaking incredible! Simply nothing less than an absolutely outstanding masterpiece. I finally did some tiny babysteps yesterday, and it just blows my mind every single time I see something new (like the exact frame-base and re-rendering of the same frame when on pause, or the multiplex rendering 🤯, and then the midi integration, omg, I think I am hyperventilating (in a good way). I wanted something like this for so long. You are a true hero and genious! Thank you so much!

I hope I can contribute to this amazing project. Feel free to throw ideas at me, or mention me in new issues when you got ideas for further implementations. Worth a try.

Now about this particular issue: Sorry for the mess here, I’ll clean this up during the weekend. And then I am happy to give your approach a shot with the python 3.8.

Lastly, congratulations on the incoming m1. Will it be PRO or MAX? I bet either way you can hardly wait :D

stenson commented 2 years ago

Glad to hear you're enjoying it, @Mark2Mark! Will definitely keep you in mind for future features/implementations — the code itself is still a little convoluted under the hood, but plenty to do in there.

FlorianRhiem commented 2 years ago

I've just released version 2.4.0 of the package glfw, along with wheels for macOS with arm64, so the pyglfw half of this issue should be fixed.

I'm using the binaries provided by GLFW3 and do not have an M1 macbook myself, so if there are any issues with the wheels, please let me know!

stenson commented 2 years ago

@FlorianRhiem thanks for letting us know! working great on my m1 laptop with an arm python3.9