Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build/pkgs/pillow: Allow discovery of libjpeg, upgrade to 10.1.0 #36731

Merged
merged 10 commits into from
Nov 30, 2023

Conversation

mkoeppe
Copy link
Member

@mkoeppe mkoeppe commented Nov 18, 2023

As discussed in https://groups.google.com/g/sage-devel/c/A4nY3bh1oPI @egourgoulhon

Here we makelibjpeg a new "recommended" dummy package with spkg-configure script -- same category as pandoc, ffmpeg, imagemagick, texlive, so it appears in the installation instructions in https://deploy-preview-36731--sagemath-tobias.netlify.app/html/en/installation/source#debian-ubuntu-package-installation (in the 3rd command line for each of the shown package systems).

Use of libjpeg can be disabled using ./configure --without-system-libjpeg.

Everything done here would also be necessary if we decided to make libjpeg a normal standard/optional package; that is beyond the scope of this PR.

We also refine the "CI Linux incremental" workflow, which determines what packages to uninstall (so that spkg-configure is tested) or to build (important for optional packages), based on changed files. Failures seen in https://github.com/sagemath/sage/actions/runs/6914890950/job/18813211070?pr=36731#step:4:8 came from the workflow trying to build dummy packages. We suppress these now, as seen in https://github.com/sagemath/sage/actions/runs/6915792754/job/18815135590?pr=36731#step:4:8

Tests at https://github.com/mkoeppe/sage/actions/runs/6911535503 (Linux), https://github.com/mkoeppe/sage/actions/runs/6911535500 (macOS)

📝 Checklist

  • The title is concise, informative, and self-explanatory.
  • The description explains in detail what this PR is about.
  • I have linked a relevant issue or discussion.
  • I have created tests covering the changes.
  • I have updated the documentation accordingly.

⌛ Dependencies

@mkoeppe mkoeppe self-assigned this Nov 18, 2023
@mkoeppe mkoeppe changed the title build/pkgs/pillow/spkg-install.in: Allow discovery of libjpeg build/pkgs/pillow: Allow discovery of libjpeg, upgrade to 10.1.0 Nov 18, 2023
@orlitzky
Copy link
Contributor

Will pillow still build on a system with no libjpeg? Their docs imply that it won't:

Starting with Pillow 3.0.0, libjpeg is required by default, but may be disabled with the --disable-jpeg flag.

Either required or disabled is better than having it be auto-detected, but if it's going to be required we should probably put it in _prereq and not _recommended.

@mkoeppe
Copy link
Member Author

mkoeppe commented Nov 18, 2023

Will pillow still build on a system with no libjpeg? Their docs imply that it won't:

Starting with Pillow 3.0.0, libjpeg is required by default, but may be disabled with the --disable-jpeg flag.

Thanks, I'll check

@mkoeppe
Copy link
Member Author

mkoeppe commented Nov 18, 2023

I wouldn't like to make it a required dummy package - because as Marc @culler pointed out, it's not available on macOS (without homebrew).

@mkoeppe
Copy link
Member Author

mkoeppe commented Nov 18, 2023

@orlitzky
Copy link
Contributor

The risk with it being auto-detected is that if you happen to have libjpeg installed temporarily for some unrelated reason, building sage (even with --disable-libjpeg!) will unconditionally build a copy that requires libjpeg to run. Then when you later remove libjpeg, sage will crash.

@mkoeppe
Copy link
Member Author

mkoeppe commented Nov 18, 2023

Well, that's true for pretty much any use of shared libs from system packages

@orlitzky
Copy link
Contributor

Well, that's true for pretty much any use of shared libs from system packages

Yeah but with a required standard package you can at least avoid the eventual crash with --without-system-foo.

@mkoeppe
Copy link
Member Author

mkoeppe commented Nov 18, 2023

That's a fair point. Not sure if a major point, but a fair point.

@mkoeppe
Copy link
Member Author

mkoeppe commented Nov 18, 2023

We do have a variable PILLOW_CONFIG_SETTINGS that an ephemeral-system-packages-wary user could use for locking down what libraries pillow uses.

@mkoeppe
Copy link
Member Author

mkoeppe commented Nov 18, 2023

And note that Pillow autodetects a bunch of other libraries and we are not forbidding that.

Copy link

Documentation preview for this PR (built with commit 30d5a7e; changes) is ready! 🎉

Copy link
Member

@egourgoulhon egourgoulhon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for implementing this!
LGTM. I've checked it on Ubuntu 22.04, with libjpeg-dev installed as a system package. More precisely, I've built Sage from scratch (starting from a git clone (-> Sage 10.2.rc4) + pull of this branch) and the output of ./configure is

Checking whether SageMath should install SPKG libjpeg...
checking for libjpeg... yes
configure: will use system package and not install SPKG libjpeg
...
libjpeg:                        using system package

Then I've checked that running

from PIL import Image
plot(sin(x)).save("fig.png")
img = Image.open("fig.png")
img

in a Jupyter notebook does not trigger any error (contrary to Sage 10.2.rc4), and that the subsequent code

img.convert("RGB").save("fig.jpg")

properly creates a jpeg image file.
I've also checked that the public notebook
https://nbviewer.org/github/sagemanifolds/SageManifolds/blob/master/Notebooks/SM_black_hole_rendering.ipynb
runs without any error.

@mkoeppe
Copy link
Member Author

mkoeppe commented Nov 20, 2023

Thanks for testing!

@dimpase
Copy link
Member

dimpase commented Nov 20, 2023

The risk with it being auto-detected is that if you happen to have libjpeg installed temporarily for some unrelated reason, building sage (even with --disable-libjpeg!) will unconditionally build a copy that requires libjpeg to run. Then when you later remove libjpeg, sage will crash.

In that sense, ./sage --pip install pillow is safer - it will most probably fetch a binary wheel with everything included.

@culler
Copy link
Contributor

culler commented Nov 20, 2023 via email

@mkoeppe
Copy link
Member Author

mkoeppe commented Nov 20, 2023

In that sense, ./sage --pip install pillow is safer - it will most probably fetch a binary wheel with everything included.

Note that the included libraries in such binary wheels (built using auditwheel or delocate) may cause library conflicts of the same kind that we otherwise avoid using SAGE_SPKG_DEPCHECK files in spkg-configure.m4
Not saying that I'd be overly concerned about this in the particular case of pillow, but it is something that we do not have control over in general.

@culler
Copy link
Contributor

culler commented Nov 20, 2023 via email

@mkoeppe
Copy link
Member Author

mkoeppe commented Nov 20, 2023

That's true about libjpeg (besides that, we know that Pillow is the only package using it!), but the concern extends to the other libraries bundled in the wheel:

$ unzip Pillow-10.1.0-cp311-cp311-macosx_10_10_x86_64.whl
[...]
  inflating: PIL/.dylibs/liblcms2.2.dylib  
  inflating: PIL/.dylibs/libpng16.16.dylib  
  inflating: PIL/.dylibs/libxcb.1.1.0.dylib  
  inflating: PIL/.dylibs/libbrotlidec.1.1.0.dylib  
  inflating: PIL/.dylibs/libfreetype.6.dylib  
  inflating: PIL/.dylibs/libXau.6.0.0.dylib  
  inflating: PIL/.dylibs/libbrotlicommon.1.1.0.dylib  
  inflating: PIL/.dylibs/libharfbuzz.0.dylib  
  inflating: PIL/.dylibs/libsharpyuv.0.dylib  
  inflating: PIL/.dylibs/libz.1.3.dylib  
  inflating: PIL/.dylibs/libwebp.7.dylib  
  inflating: PIL/.dylibs/libwebpmux.3.dylib  
  inflating: PIL/.dylibs/libopenjp2.2.5.0.dylib  
  inflating: PIL/.dylibs/libwebpdemux.2.dylib  
  inflating: PIL/.dylibs/liblzma.5.dylib  
  inflating: PIL/.dylibs/libtiff.6.dylib  
[...]

@orlitzky
Copy link
Contributor

Note that the included libraries in such binary wheels (built using auditwheel or delocate) may cause library conflicts of the same kind that we otherwise avoid using SAGE_SPKG_DEPCHECK files in spkg-configure.m4 Not saying that I'd be overly concerned about this in the particular case of pillow, but it is something that we do not have control over in general.

I think libjpeg is actually pretty high-risk. It's a common system library and lots of math software needs to work with JPEG graphics for e.g. handwriting recognition. You'd have to be able to promise (a) that no other sage dependency would ever want to link to libjpeg, and (b) that the user himself wouldn't want to link a sage program to libjpeg.

@mkoeppe
Copy link
Member Author

mkoeppe commented Nov 20, 2023

@culler Of course, in preparing your macOS app, you can simply test that the resulting binary works. But for the unattended from-source builds of Sage on user systems, relying on the published wheels from PyPI introduces a lot of potential for mysterious failures.

@culler
Copy link
Contributor

culler commented Nov 20, 2023 via email

@mkoeppe
Copy link
Member Author

mkoeppe commented Nov 20, 2023

I don't actually think that any other parts of sage would be able to find the dynamic libraries packaged with Pillow.

I agree with this; but I think the failure mechanism is through possible global symbol clashes of different versions of the same library.

@orlitzky
Copy link
Contributor

I'd have to try it to be sure. The issue isn't with finding the bundled libraries but rather with the conflicting symbol names. All of this stuff eventually gets linked into the same executable, so if two libraries (even transitively) provide the same symbols, you can get a conflict if they're not binary-compatible.

For example, the bundled pillow .so and the one on my system:

$ nm -gD /usr/lib/python3.11/site-packages/PIL/_imaging.cpython-311-x86_64-linux-gnu.so  | grep jpeg
                 U jpeg_CreateCompress@LIBJPEG_6.2
                 U jpeg_CreateDecompress@LIBJPEG_6.2
                 U jpeg_add_quant_table@LIBJPEG_6.2
00000000000375f0 T jpeg_buffer_dest
0000000000037090 T jpeg_buffer_src
                 U jpeg_destroy_compress@LIBJPEG_6.2
                 U jpeg_destroy_decompress@LIBJPEG_6.2
                 U jpeg_finish_compress@LIBJPEG_6.2
                 U jpeg_finish_decompress@LIBJPEG_6.2
                 U jpeg_read_header@LIBJPEG_6.2
                 U jpeg_read_scanlines@LIBJPEG_6.2
                 U jpeg_resync_to_restart@LIBJPEG_6.2
                 U jpeg_set_defaults@LIBJPEG_6.2
                 U jpeg_set_quality@LIBJPEG_6.2
                 U jpeg_simple_progression@LIBJPEG_6.2
                 U jpeg_start_compress@LIBJPEG_6.2
                 U jpeg_start_decompress@LIBJPEG_6.2
                 U jpeg_std_error@LIBJPEG_6.2
                 U jpeg_suppress_tables@LIBJPEG_6.2
                 U jpeg_write_marker@LIBJPEG_6.2
                 U jpeg_write_scanlines@LIBJPEG_6.2
0000000000064ee0 D libjpeg_turbo_version

$ nm -gD PIL/_imaging.pypy310-pp73-x86_64-linux-gnu.so | grep jpeg
                 U jpeg_CreateCompress@LIBJPEG_6.2
                 U jpeg_CreateDecompress@LIBJPEG_6.2
                 U jpeg_add_quant_table@LIBJPEG_6.2
000000000003b1f0 T jpeg_buffer_dest
000000000003ac60 T jpeg_buffer_src
                 U jpeg_destroy_compress@LIBJPEG_6.2
                 U jpeg_destroy_decompress@LIBJPEG_6.2
                 U jpeg_finish_compress@LIBJPEG_6.2
                 U jpeg_finish_decompress@LIBJPEG_6.2
                 U jpeg_read_header@LIBJPEG_6.2
                 U jpeg_read_scanlines@LIBJPEG_6.2
                 U jpeg_resync_to_restart@LIBJPEG_6.2
                 U jpeg_set_defaults@LIBJPEG_6.2
                 U jpeg_set_quality@LIBJPEG_6.2
                 U jpeg_simple_progression@LIBJPEG_6.2
                 U jpeg_start_compress@LIBJPEG_6.2
                 U jpeg_start_decompress@LIBJPEG_6.2
                 U jpeg_std_error@LIBJPEG_6.2
                 U jpeg_suppress_tables@LIBJPEG_6.2
                 U jpeg_write_marker@LIBJPEG_6.2
                 U jpeg_write_scanlines@LIBJPEG_6.2
000000000006b5e0 D libjpeg_turbo_version

In this case they agree, but... I'd have to try it. (This is why there are different wheels for different libc)

vbraun pushed a commit to vbraun/sage that referenced this pull request Nov 27, 2023
…grade to 10.1.0

    
<!-- ^^^^^
Please provide a concise, informative and self-explanatory title.
Don't put issue numbers in there, do this in the PR body below.
For example, instead of "Fixes sagemath#1234" use "Introduce new method to
calculate 1+1"
-->
<!-- Describe your changes here in detail -->

As discussed in https://groups.google.com/g/sage-devel/c/A4nY3bh1oPI
@egourgoulhon

Here we make`libjpeg` a new "recommended" dummy package with spkg-
configure script -- same category as pandoc, ffmpeg, imagemagick,
texlive, so it appears in the installation instructions in
https://deploy-preview-36731--sagemath-
tobias.netlify.app/html/en/installation/source#debian-ubuntu-package-
installation (in the 3rd command line for each of the shown package
systems).

Use of libjpeg can be disabled using `./configure --without-system-
libjpeg`.

Everything done here would also be necessary if we decided to make
libjpeg a normal standard/optional package; that is beyond the scope of
this PR.

We also refine the "CI Linux incremental" workflow, which determines
what packages to uninstall (so that spkg-configure is tested) or to
build (important for optional packages), based on changed files.
Failures seen in https://github.com/sagemath/sage/actions/runs/691489095
0/job/18813211070?pr=36731#step:4:8 came from the workflow trying to
build dummy packages. We suppress these now, as seen in https://github.c
om/sagemath/sage/actions/runs/6915792754/job/18815135590?pr=36731#step:4
:8

Tests at https://github.com/mkoeppe/sage/actions/runs/6911535503
(Linux), https://github.com/mkoeppe/sage/actions/runs/6911535500 (macOS)

<!-- Why is this change required? What problem does it solve? -->
<!-- If this PR resolves an open issue, please link to it here. For
example "Fixes sagemath#12345". -->
<!-- If your change requires a documentation PR, please link it
appropriately. -->

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->
<!-- If your change requires a documentation PR, please link it
appropriately -->
<!-- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
<!-- Feel free to remove irrelevant items. -->

- [x] The title is concise, informative, and self-explanatory.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [ ] I have created tests covering the changes.
- [ ] I have updated the documentation accordingly.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on
- sagemath#12345: short description why this is a dependency
- sagemath#34567: ...
-->

<!-- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
    
URL: sagemath#36731
Reported by: Matthias Köppe
Reviewer(s): Eric Gourgoulhon
@vbraun
Copy link
Member

vbraun commented Nov 27, 2023

Imho this needs more testing than cramming it in at the last minute

@mkoeppe
Copy link
Member Author

mkoeppe commented Nov 27, 2023

People who use Jupyter seem to consider it a severe regression from the previous release

@mkoeppe
Copy link
Member Author

mkoeppe commented Nov 27, 2023

And this was tested on the full set of platforms, see ticket description.

@egourgoulhon
Copy link
Member

People who use Jupyter seem to consider it a severe regression from the previous release

I confirm...

@orlitzky
Copy link
Contributor

The issue wasn't just that you can't play with jpegs, but that some other functionality of pillow happens to be broken at the moment if you compile it without jpeg support, right?

@orlitzky
Copy link
Contributor

@culler FYI I noticed that libjpeg has a slightly unusual license, that says,

(2) If only executable code is distributed, then the accompanying documentation must state that "this software is based in part on the work of the Independent JPEG Group".

@egourgoulhon
Copy link
Member

The issue wasn't just that you can't play with jpegs, but that some other functionality of pillow happens to be broken at the moment if you compile it without jpeg support, right?

Yes indeed. For instance, as reported in https://groups.google.com/g/sage-devel/c/A4nY3bh1oPI/m/0lxrC67HAgAJ, the following code does not manipulate any jpeg but is broken in Sage 10.2.rc4:

from PIL import Image
plot(sin(x)).save("fig.png")
img = Image.open("fig.png")
img

For some reason, it yields

ValueError: Could not save to JPEG for display

while on the user side, we are not asking for any jpeg.
The same piece of code works nicely in Sage 10.1

@orlitzky
Copy link
Contributor

while on the user side, we are not asking for any jpeg.
The same piece of code works nicely in Sage 10.1

This is probably fixed in pillow-10.1.0: python-pillow/Pillow#7259

If so, maybe an upgrade to pillow is a less intrusive fix?

@vbraun vbraun merged commit 202a245 into sagemath:develop Nov 30, 2023
33 of 60 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants