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

nodejs: fix cross compilation for armv7l #220204

Closed
wants to merge 1 commit into from

Conversation

Cynerd
Copy link
Contributor

@Cynerd Cynerd commented Mar 8, 2023

Description of changes

The cross build for armv7l requires build to be on i686 (32bit in general). This ensures that and thus fixes build for armv7l.

I am not happy with this solution but I am unable to figure out how to fix cross compilation for amv7l in better way than this.

Things done
  • Built on platform(s)
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • For non-Linux: Is sandbox = true set in nix.conf? (See Nix manual)
  • Tested, as applicable:
  • Tested compilation of all packages that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage
  • Tested basic functionality of all binary files (usually in ./result/bin/)
  • 23.05 Release Notes (or backporting 22.11 Release notes)
    • (Package updates) Added a release notes entry if the change is major or breaking
    • (Module updates) Added a release notes entry if the change is significant
    • (Module addition) Added a release notes entry if adding a new NixOS module
  • Fits CONTRIBUTING.md.

@ofborg ofborg bot added the 6.topic: cross-compilation Building packages on a different platform than they will be used on label Mar 8, 2023
@ofborg ofborg bot requested review from cko, marsam, gilligan and cillianderoiste March 8, 2023 20:14
Copy link
Member

@alyssais alyssais left a comment

Choose a reason for hiding this comment

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

If we truly need to force a 32-bit build system, I think a cleaner way to do it would be to do it in the callPackage invocation. Then we could just do something like

nodejs = (if stdenv.buildPlatform.isx86_64 && stdenv.is32bit then pkgsi686Linux else pkgs).callPackage …

which is nice because then there's one less package set involved in the actual expression, and it prevents adding 64-bit dependencies as well by mistake.

Either way, this needs a comment explaining what's going on, with a link to documentation or reference to the source code so people can understand what's going on, and where to check to see if it can be removed.

@Cynerd
Copy link
Contributor Author

Cynerd commented Mar 28, 2023

If we truly need to force a 32-bit build system, I think a cleaner way to do it would be to do it in the callPackage invocation. Then we could just do something like

I tweaked it a bit. It is probably reasonable requirement.

Either way, this needs a comment explaining what's going on, with a link to documentation or reference to the source code so people can understand what's going on, and where to check to see if it can be removed.

I added comment but I am unable to find that this is actually required in the documentation. At the same time build really fails with missing 32bit stubs and thus it for sure tries to use 32bit build somewhere inside. The upstream builds it on Ubuntu and thus they might expect that 32bit libraries are just available while that is not the case with nixpkgs.

@ofborg ofborg bot added 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin 10.rebuild-linux: 0 This PR does not cause any packages to rebuild on Linux and removed 10.rebuild-darwin: 11-100 10.rebuild-linux: 11-100 labels Mar 28, 2023
@Cynerd Cynerd requested a review from alyssais April 25, 2023 12:55
Copy link
Member

@alyssais alyssais left a comment

Choose a reason for hiding this comment

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

LGTM but I'd really like there to be an upstream issue about this. They shouldn't be passing -m32 to the native compiler just because they're cross compiling for 32 bit.

@lilyinstarlight
Copy link
Member

I only just now saw this, but I do wonder if it may also be fixed by #238377?

@Cynerd
Copy link
Contributor Author

Cynerd commented Jun 30, 2023

@lilyinstarlight I just tested it, and it doesn't help my issue. The issue here is with cross-compilation from amd64 to 32-bit systems. Node requires bitness to match it seems, and for that reason, we need to build it not in an amd64 environment but instead in an x86 one.

I suspect that, especially on new Macs that do not have 32-bit arm support (if I remember correctly) it won't even be possible to cross-compile Node to armv7l.

@Cynerd
Copy link
Contributor Author

Cynerd commented Jun 30, 2023

LGTM but I'd really like there to be an upstream issue about this. They shouldn't be passing -m32 to the native compiler just because they're cross compiling for 32 bit.

I create an issue upstream but I am not expecting much nodejs/node#48612. This is a common issue I saw with other bytecode compilers (for example luajit).

@tie
Copy link
Member

tie commented Jul 8, 2023

FWIW I think this is the relevant code that sets -m32 when cross-compiling in your case.
https://github.com/nodejs/node/blob/3205b1936a5c3c21b0c8c568a57b0d2bada366b1/tools/v8_gypfiles/toolchain.gypi#L583-L601

host_cxx_is_biarch is defined at https://github.com/nodejs/node/blob/3205b1936a5c3c21b0c8c568a57b0d2bada366b1/tools/v8_gypfiles/toolchain.gypi#L107-L118

That is, it assumes that x86(-64) (and some other systems) have bi-arch capable toolchains (probably for a reason, see below, but maybe not, this is technical decision is really weird).

With the following patch, we successfully get a meaningful error (instead of vague compilation failure due to the missing headers) that leads us to https://github.com/nodejs/node/blob/3205b1936a5c3c21b0c8c568a57b0d2bada366b1/deps/v8/include/v8config.h#L863-L892 (or deps/v8/src/base/build_config.h for v18)

diff --git a/tools/v8_gypfiles/toolchain.gypi b/tools/v8_gypfiles/toolchain.gypi
index 26c6866fa9..658ebd6e86 100644
--- a/tools/v8_gypfiles/toolchain.gypi
+++ b/tools/v8_gypfiles/toolchain.gypi
@@ -109,7 +109,7 @@
       host_arch=="s390x" or \
       clang==1', {
       'variables': {
-        'host_cxx_is_biarch%': 1,
+        'host_cxx_is_biarch%': 0,
        },
      }, {
       'variables': {

Target architecture arm is only supported on arm and ia32 host

So this seems like an issue with v8 rather than Node.js. At this point I don’t really want to dive deeper into this rabbit hole 🥲

pkgs/top-level/all-packages.nix Outdated Show resolved Hide resolved
@lilyinstarlight lilyinstarlight added the 2.status: merge conflict This PR has merge conflicts with the target branch label Jul 8, 2023
@lilyinstarlight
Copy link
Member

@Cynerd Could you rebase and resolve merge conflicts on this?

If this is what we have to do, I'm mostly alright with this, but it is rather unfortunate

@ofborg ofborg bot removed the 2.status: merge conflict This PR has merge conflicts with the target branch label Jul 10, 2023
@ofborg ofborg bot added 10.rebuild-darwin: 501+ 10.rebuild-darwin: 501-1000 10.rebuild-linux: 501+ 10.rebuild-linux: 1001-2500 and removed 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin 10.rebuild-linux: 0 This PR does not cause any packages to rebuild on Linux labels Jun 28, 2024
@tie
Copy link
Member

tie commented Jun 30, 2024

Yes, should be targeting staging given the number of rebuilds.

See https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#rebasing-between-branches-ie-from-master-to-staging

@Cynerd Cynerd changed the base branch from master to staging July 1, 2024 07:59
@Cynerd
Copy link
Contributor Author

Cynerd commented Jul 2, 2024

Rebase and retargeting is done (I am not sure if GitHub sends an email about it).

Copy link
Member

Choose a reason for hiding this comment

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

Are there any changes from the buildroot patch? Can we use fetchpatch2 to fetch the patch?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No changes in the end. I will switch it to fetchpatch.

@@ -126,7 +127,8 @@ let

pos = builtins.unsafeGetAttrPos "version" args;

inherit patches;
patches = patches
++ (lib.optional isCrossBitness ./add-qemu-wrapper-support.patch);
Copy link
Member

Choose a reason for hiding this comment

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

I’m not really a fan of conditional patches. Can we apply the patch unconditionally (and replace MAYBE_WRAPPER with an empty string if we are not using an emulator)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok.. in my eyes, it makes it a bit more complex, but ok. There is also an option to just always use a wrapper because, for the native build, it will work as well.

sed -i "s%@MAYBE_WRAPPER@%'<(PRODUCT_DIR)/v8-qemu-wrapper',%g" node.gyp
sed -i "s%@MAYBE_WRAPPER@%'<(PRODUCT_DIR)/v8-qemu-wrapper',%g" tools/v8_gypfiles/v8.gyp
mkdir -p out/Release
cat > out/Release/v8-qemu-wrapper << "EOF"
Copy link
Member

@tie tie Jul 2, 2024

Choose a reason for hiding this comment

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

Hm, I think a symlink should be enough? IIRC emulator contains additional arguments iff canExecute. Is it the case here?

if pkgs.stdenv.hostPlatform.canExecute final
then "${pkgs.runtimeShell} -c '\"$@\"' --"
else if final.isWindows
then "${wine}/bin/wine${optionalString (final.parsed.cpu.bits == 64) "64"}"
else if final.isLinux && pkgs.stdenv.hostPlatform.isLinux && final.qemuArch != null
then "${qemu-user}/bin/qemu-${final.qemuArch}"
else if final.isWasi
then "${pkgs.wasmtime}/bin/wasmtime"
else if final.isMmix
then "${pkgs.mmixware}/bin/mmix"
else null;

Copy link
Member

Choose a reason for hiding this comment

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

Ah, OK.

nix-repl> (lib.systems.elaborate "x86_64-linux").canExecute (lib.systems.elaborate "i686-linux")   
true

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah for i686 it needs to be a script.

Copy link
Member

Choose a reason for hiding this comment

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

FWIW I’ve opened PR #324071 to make an emulator a simple executable path in native case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

With that it should be possible to use only just symlink 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can we proceed with this PR and update it just once #324071 is merged. It should be later only a minor change in postPath. What do you think?

CC_host = "cc";
CXX_host = "c++";
CC_host = if isCrossBitness then "${stdenv.cc.targetPrefix}cc" else "cc";
CXX_host = if isCrossBitness then "${stdenv.cc.targetPrefix}c++" else "c++";
Copy link
Member

Choose a reason for hiding this comment

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

Are we using $NIX_CC instead of $NIX_CC_FOR_BUILD in this case? If we don’t need C compiler for build platform, perhaps it makes sense to omit depsBuildBuild entirely if isCrossBitness?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes it should be possible to just drop depsBuildBuild in case of isCrossBitness.

Would it even make sense to always use this magic for cross-compilation and not just when bitness differed? The issue with isCrossBitness is that as far as I can see, this is only extraction for the Nix-supported platforms, but as you linked, v8 actually supports only limited build platforms not actually matching bitness.

Copy link
Member

Choose a reason for hiding this comment

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

Would it even make sense to always use this magic for cross-compilation and not just when bitness differed?

Yes, sounds reasonable to me. Always using an emulator instead of cross-compiling should make Node.js build faster, see also #321847.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is now converted to build always with the patch and with wrapper.

I tested it on amd64 and cross build from amd64 to armv7.

@aduh95
Copy link
Contributor

aduh95 commented Jul 5, 2024

This needs a rebase.

@wegank wegank added the 2.status: merge conflict This PR has merge conflicts with the target branch label Jul 6, 2024
@ofborg ofborg bot removed the 2.status: merge conflict This PR has merge conflicts with the target branch label Jul 11, 2024
@Cynerd Cynerd requested a review from tie July 11, 2024 15:58
The v8 library supports only limited number of build platforms based on
the host architecture. The rule there is the bitness (the mixing of
32bit and 64bit architectures seems to be in most cases disallowed).
Thus this adds usage of the emulator of the host platform and builds
tools for that.
patches = patches ++ [
(fetchpatch2 {
name = "add-qemu-wrapper-support.patch";
url = "https://github.com/buildroot/buildroot/raw/ecf060a2ff93626fb5fbe1c52b8d42b6ee0fbb5b/package/nodejs/nodejs-src/0001-add-qemu-wrapper-support.patch";
Copy link
Member

Choose a reason for hiding this comment

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

GitHub repository is a mirror, the official Git repository is at https://gitlab.com/buildroot.org/buildroot.

Suggested change
url = "https://github.com/buildroot/buildroot/raw/ecf060a2ff93626fb5fbe1c52b8d42b6ee0fbb5b/package/nodejs/nodejs-src/0001-add-qemu-wrapper-support.patch";
url = "https://gitlab.com/buildroot.org/buildroot/-/raw/ecf060a2ff93626fb5fbe1c52b8d42b6ee0fbb5b/package/nodejs/nodejs-src/0001-add-qemu-wrapper-support.patch";

@@ -180,14 +195,14 @@ let
pushd out/Release/obj.target
find . -path "./torque_*/**/*.o" -or -path "./v8*/**/*.o" | sort -u >files
${if stdenv.buildPlatform.isGnu then ''
ar -cqs $libv8/lib/libv8.a @files
$AR -cqs $libv8/lib/libv8.a @files
Copy link
Member

Choose a reason for hiding this comment

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

Hm, I’m not sure why this line is conditional. I thought Nixpkgs bintools wrapper handled response files, but maybe that’s not the case here for some reason? I’ll have to double-check that.

@tie
Copy link
Member

tie commented Jul 15, 2024

@Cynerd, I’ve made a few minor adjustments based on this PR and re-implemented buildroot patch in 42fa83e (0001-nodejs-fix-cross-compilation-for-armv7l.patch) so that the emulator trick is upstreamable. What do you think?

@Cynerd
Copy link
Contributor Author

Cynerd commented Jul 18, 2024

@Cynerd, I’ve made a few minor adjustments based on this PR and re-implemented buildroot patch in 42fa83e (0001-nodejs-fix-cross-compilation-for-armv7l.patch) so that the emulator trick is upstreamable. What do you think?

I am ok with that. I tried to compile it and ti seems to work so I would go with your solution.

@tie
Copy link
Member

tie commented Jul 24, 2024

Note for reviewers: #327653 contains commits from this PR (with slight modifications, see above) and goes further on top of these changes to use ninja for build.

@Cynerd, would it be OK to close this PR (since you’ve approved ninja PR)?

@Cynerd
Copy link
Contributor Author

Cynerd commented Jul 25, 2024

@tie Yes. This can be closed.

@Cynerd Cynerd closed this Jul 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

6 participants