Automattic / node-canvas

Node canvas is a Cairo backed Canvas implementation for NodeJS.
10.13k stars 1.17k forks source link

Switch from MSYS 2 MinGW64 to MSYS2 UCRT64 #2155

Closed kayahr closed 1 year ago

kayahr commented 1 year ago

Issue or Feature

The pre-built Windows binary of node-canvas currently uses DLLs from MSYS2 MinGW64 which uses the old MSVCRT runtime. node-canvas would be more portable and more compatible to other native libraries like Sharp when it would use the universal runtime instead.

See https://learn.microsoft.com/en-us/cpp/porting/upgrade-your-code-to-the-universal-crt?view=msvc-170 and https://www.msys2.org/docs/environments/#msvcrt-vs-ucrt

Switching is pretty much straight forward:

I guess a pull request makes no sense here because your build environment for the pre-built windows binaries seems to be manually installed.

vito-vanhecke commented 1 year ago

How do you go about doing this exactly? We require both node-canvas and Sharp, but we are not familiar with c++?

kayahr commented 1 year ago

@vito-vanhecke I wrote "Switching is pretty much straight forward" but this was meant for the node-canvas authors. For a node-canvas user (especially when you are not familiar with C++ build stuff on Windows) it is not that simple I'm afraid.

chearon commented 1 year ago

@kayahr a pull request would be much welcome, I think to the prebuilds branch (@zbjornson has been doing them lately)

vito-vanhecke commented 1 year ago

Are there plans for these prebuilds? We do require both sharp and node-canvas...

vito-vanhecke commented 1 year ago

@zbjornson Are you planning these prebuilds? Or is there a workaround? Not using sharp, not using canvas?

suniltandan commented 1 year ago

I don't think that compiling with UCRT64 will help here. I compiled canvas with @kayahr 's instruction. Requiring both of them does not produce any error, however using them will give the following error:

(process:3040): GLib-GObject-WARNING **: 17:35:30.162: cannot register existing type 'PangoFontMap'
(process:3040): GLib-GObject-WARNING **: 17:35:30.162: cannot add private field to invalid (non-instantiatable) type '<invalid>'
(process:3040): GLib-GObject-WARNING **: 17:35:30.162: cannot register existing type 'GListModel'
(process:3040): GLib-GObject-CRITICAL **: 17:35:30.162: g_type_interface_add_prerequisite: assertion 'G_TYPE_IS_INTERFACE (interface_type)' failed
(process:3040): GLib-CRITICAL **: 17:35:30.162: g_once_init_leave: assertion 'result != 0' failed
(process:3040): GLib-GObject-CRITICAL **: 17:35:30.162: g_type_add_interface_static: assertion 'G_TYPE_IS_INSTANTIATABLE (instance_type)' failed
(process:3040): GLib-CRITICAL **: 17:35:30.162: g_once_init_leave: assertion 'result != 0' failed
(process:3040): GLib-GObject-CRITICAL **: 17:35:30.162: g_type_register_static: assertion 'parent_type > 0' failed
(process:3040): GLib-CRITICAL **: 17:35:30.162: g_once_init_leave: assertion 'result != 0' failed
(process:3040): GLib-GObject-CRITICAL **: 17:35:30.162: g_type_register_static: assertion 'parent_type > 0' failed
(process:3040): GLib-GObject-WARNING **: 17:35:30.162: cannot register existing type 'PangoCairoFontMap'

Neither sharp nor node-canvas seem to catch this error, i.e the promise is never reject nor resolved. The process is then just "stuck".

Not sure what the next steps should be debug this.

I've seen this issue here as well: https://github.com/lovell/sharp/issues/2531. Changing the require order does not seem to help at all.

lovell commented 1 year ago

@suniltandan It's possible this may relate to https://github.com/libvips/libvips/issues/3275 - the forthcoming sharp v0.32.0 will include the fix if so.

acalcutt commented 1 year ago

I am having this issue with https://github.com/maptiler/tileserver-gl . We just got maplibre-native binaries that work with windows, but there is a conflict between sharp and canvas that prevents it from working on windows. sharp 0.32.0 was just released, but does not appear to fix this issue like suggested in the post above.

We have found we can downgrade sharp to 0.30.7 and it works, but I am told the newer versions fail because of the issue mentioned here.

lovell commented 1 year ago

@acalcutt Are you compiling your own canvas binaries from source using the instructions provided? If so, do you see the exact sameg_type_interface_add_prerequisite: assertion 'G_TYPE_IS_INTERFACE (interface_type)' failed error?

acalcutt commented 1 year ago

We are just using the binary provided by the canvas 2.11.0 npm package, so we are not building our own copy. We are also using the sharp 0.32.0 npm package.

If sharp is imported before canvas, the error looks like this image

If canvas is imported before sharp, the error looks like this image

If canvas is imported before sharp and we supply custom made UCRT64 node-canvas binary, made like https://github.com/Automattic/node-canvas/pull/2219, it runs image

tdcosta100 commented 1 year ago

One thing it's worthy to mention is: sharp uses the official libvips Windows binaries located here. These are cross-compiled using MXE, and they seem to be the source of most trouble we are having with canvas. I tried to build both canvas and sharp with MSYS2 libraries (and using the MSYS2 libvips libraries), and they worked very well. Maybe in the future we can find a build configuration (maybe cross compile canvas too?) that is more compatible with these binaries than the MSYS2 build (but building it with MSYS2 UCRT64 increases the compatibility a lot).

kayahr commented 1 year ago

@tdcosta100 This is the whole point of this issue. Both sharp and canvas are compiled with MSYS2 but they use a different C Runtime. Sharp uses the newer and more universal UCRT (Used by MSYS2 UCRT64) while the pre-built canvas binary uses the very old MSVCRT (Used by MSYS2 Mingw64). These runtimes are not compatible and therefor the two libraries cannot be used together. This can be fixed by canvas by switching to UCRT as well as I described in this issue. I think it is not a good solution to convince the sharp developers to downgrade their build to the old MSVCRT because UCRT is the modern way to go on Windows.

tdcosta100 commented 1 year ago

My point is switching to MSYS UCRT64 make it compatible, but doesn't fix all errors, like @acalcutt demonstrated. But doing this allows at least the coexistence of the two libraries, which was impossible with MSVCRT. I think it's possible to find a build scheme that makes it fully compatible, just don't know how yet (possibly using MXE too, but maybe there's a solution using MSYS2).

kayahr commented 1 year ago

I actually don't see a demonstrated problem. I understood that Acalcutt tried running canvas and sharp with the pre-built binaries and it didn't work but with a custom canvas build using UCRT64 it worked. That is also my experience.

tdcosta100 commented 1 year ago

Okay then, you can try loading canvas after sharp and tell me if even with MSYS2 UCRT64 build it works. To me it only worked when I built sharp also with MSYS2 UCRT64. But if I load canvas before sharp, then it works.

acalcutt commented 1 year ago

Like @tdcosta100 mentioned, there is still an error when sharp is imported before canvas even with the change to UCRT64 , it looks like the first error image

This is somewhat a problem for us, since we need to use the oposite order in linux due to https://github.com/lovell/sharp/issues/371 and imports are hard to change the order of since they need to be top level.

chearon commented 1 year ago

Hey all, closing this because I just released 2.11.2 which uses UCRT64. I'm not sure I understand the remaining problems with sharp, but open to anything that works. The only upcoming changes to prebuilds is that we're going to use NAPI and possibly make some of the libraries static.

acalcutt commented 1 year ago

Thanks for adding this :-)

octo-kumo commented 1 year ago

For anyone in the future (or the past), I solved the conflict by using sharp@0.31.3 with canvas@2.11.2.

jacopomaroli commented 1 year ago

There are two issues at play here.
1) including/using both (solved) 2) including/using both AND render an svg with text Please run the following example in a cmd terminal (debugging in vscode won't show errors)

install latest deps:

"canvas": "2.11.2",
"sharp": "0.32.3"
const canvas = require('canvas')
const sharp = require('sharp')

async function test() {
  const c = canvas.createCanvas(200, 200)
  // 1. comment next line to make it work
  const ctx = c.getContext('2d')

  const collectionNameText = Buffer.from(`
    <?xml version="1.0" encoding="UTF-8"?>
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
      <!-- 2. empty next tag to make it work -->
      <text>hello</text>
    </svg>
  `.trim())

  const buf = await sharp(collectionNameText)
    .toBuffer() // Process will hang here
  console.log(buf)
}

test()

observe errors:

(process:22100): GLib-GObject-CRITICAL **: 21:44:50.733: cannot register existing type 'GInitable'

(process:22100): GLib-GObject-CRITICAL **: 21:44:50.739: g_type_interface_add_prerequisite: assertion 'G_TYPE_IS_INTERFACE (interface_type)' failed

(process:22100): GLib-CRITICAL **: 21:44:50.739: g_once_init_leave: assertion 'result != 0' failed

(process:22100): GLib-GObject-CRITICAL **: 21:44:50.742: cannot register existing type 'PangoFontMap'

(process:22100): GLib-GObject-CRITICAL **: 21:44:50.742: cannot add private field to invalid (non-instantiatable) type '<invalid>'

(process:22100): GLib-GObject-CRITICAL **: 21:44:50.743: cannot register existing type 'GListModel'

(process:22100): GLib-GObject-CRITICAL **: 21:44:50.743: g_type_interface_add_prerequisite: assertion 'G_TYPE_IS_INTERFACE (interface_type)' failed

(process:22100): GLib-CRITICAL **: 21:44:50.743: g_once_init_leave: assertion 'result != 0' failed

(process:22100): GLib-GObject-CRITICAL **: 21:44:50.744: g_type_add_interface_static: assertion 'G_TYPE_IS_INSTANTIATABLE (instance_type)' failed

(process:22100): GLib-CRITICAL **: 21:44:50.744: g_once_init_leave: assertion 'result != 0' failed

(process:22100): GLib-GObject-CRITICAL **: 21:44:50.745: g_type_register_static: assertion 'parent_type > 0' failed

(process:22100): GLib-GObject-CRITICAL **: 21:44:50.745: cannot add private field to invalid (non-instantiatable) type '<invalid>'

Now note the two comments in the above code: following the instructions of either of the two will make it run with no errors. This was already mentioned here https://github.com/lovell/sharp/issues/2531 and it's likely the issue mentioned above https://github.com/Automattic/node-canvas/issues/2155#issuecomment-1422424184