Trouble packaging python application - missing file during build

Hi all,

Yesterday I began trying to package the basedpyright language server.

To start, I used nix-init in the root of my local nixpkgs fork. I tested the build, and it made up a package python3.pkgs.nodejs-bin that didn’t exist.

So I went and tried packaging that, and on the surface it seems to have worked. The derivation for this package is in my fork here.

Running nix- build .#python3Packages.nodejs-bin followed by ls -la, I get:

result -> /nix/store/ydymmshwzhxlkhdbxjypnb49ivwrkigl-python3.11-nodejs-bin-unstable-2022-11-10

To test that the python package was working correctly, I created a quick devshell based off my nixpkgs fork (is there a quicker way I could have tested this?)

{
  description = "nodejs-bin python test";

  inputs = {
    nixpkgs.url = "github:vinnymeller/nixpkgs?ref=init-basedpyright";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs = {
    self,
    flake-utils,
    nixpkgs,
  }:
    flake-utils.lib.eachDefaultSystem (system: let
      pkgs = nixpkgs.legacyPackages.${system};
      python-pkgs = pkgs.python3.withPackages (ps: with ps; [nodejs-bin ipython]);
    in {
      devShells.default = pkgs.mkShell {
        buildInputs = [python-pkgs];
      };
    });
}

and just popped open ipython and tested that I could run from nodejs import node, npm, npx and run some sample commands. that worked fine. cool.

Now back to basedpyright, the derivation is here.

Now let’s nix build .#basedpyright:

Sourcing python-remove-tests-dir-hook
Sourcing python-catch-conflicts-hook.sh
Sourcing python-remove-bin-bytecode-hook.sh
Sourcing pypa-build-hook
Using pypaBuildPhase
Sourcing python-runtime-deps-check-hook
Using pythonRuntimeDepsCheckHook
Sourcing pypa-install-hook
Using pypaInstallPhase
Sourcing python-imports-check-hook.sh
Using pythonImportsCheckPhase
Sourcing python-namespaces-hook
Sourcing python-catch-conflicts-hook.sh
@nix { "action": "setPhase", "phase": "unpackPhase" }
Running phase: unpackPhase
unpacking source archive /nix/store/d0m3gk3inn3rq55b52bhwxyls7sgvidh-source
source root is source
setting SOURCE_DATE_EPOCH to timestamp 315619200 of file source/tsconfig.json
@nix { "action": "setPhase", "phase": "patchPhase" }
Running phase: patchPhase
@nix { "action": "setPhase", "phase": "updateAutotoolsGnuConfigScriptsPhase" }
Running phase: updateAutotoolsGnuConfigScriptsPhase
@nix { "action": "setPhase", "phase": "configurePhase" }
Running phase: configurePhase
no configure script, doing nothing
@nix { "action": "setPhase", "phase": "buildPhase" }
Running phase: buildPhase
Executing pypaBuildPhase
Setting PDM_BUILD_SCM_VERSION to 1.9.1
Creating a wheel...
Getting build dependencies for wheel...
Traceback (most recent call last):
  File "/nix/store/pz11drilfxdg0kcvmhnfaby3kysjh2fs-python3.11-pyproject-hooks-1.0.0/lib/python3.11/site-packages/pyproject_hooks/_in_process/_in_process.py", line 353, in <module>
    main()
  File "/nix/store/pz11drilfxdg0kcvmhnfaby3kysjh2fs-python3.11-pyproject-hooks-1.0.0/lib/python3.11/site-packages/pyproject_hooks/_in_process/_in_process.py", line 335, in main
    json_out['return_val'] = hook(**hook_input['kwargs'])
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/nix/store/pz11drilfxdg0kcvmhnfaby3kysjh2fs-python3.11-pyproject-hooks-1.0.0/lib/python3.11/site-packages/pyproject_hooks/_in_process/_in_process.py", line 118, in get_requires_for_build_wheel
    return hook(config_settings)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/nix/store/16v4rdrm61p8vahvwy0qyk9hajm3ys52-python3.11-pdm-backend-2.1.8/lib/python3.11/site-packages/pdm/backend/__init__.py", line 22, in get_requires_for_build_wheel
    with WheelBuilder(Path.cwd(), config_settings) as builder:
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/nix/store/16v4rdrm61p8vahvwy0qyk9hajm3ys52-python3.11-pdm-backend-2.1.8/lib/python3.11/site-packages/pdm/backend/wheel.py", line 78, in __init__
    super().__init__(location, config_settings)
  File "/nix/store/16v4rdrm61p8vahvwy0qyk9hajm3ys52-python3.11-pdm-backend-2.1.8/lib/python3.11/site-packages/pdm/backend/base.py", line 101, in __init__
    self._hooks = list(self.get_hooks())
                  ^^^^^^^^^^^^^^^^^^^^^^
  File "/nix/store/16v4rdrm61p8vahvwy0qyk9hajm3ys52-python3.11-pdm-backend-2.1.8/lib/python3.11/site-packages/pdm/backend/base.py", line 126, in get_hooks
    BuildHookInterface, import_module_at_path(self.location / local_hook)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/nix/store/16v4rdrm61p8vahvwy0qyk9hajm3ys52-python3.11-pdm-backend-2.1.8/lib/python3.11/site-packages/pdm/backend/utils.py", line 220, in import_module_at_path
    spec.loader.exec_module(module)  # type: ignore
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap_external>", line 940, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/build/source/pdm_build.py", line 20, in <module>
    _ = run(["ci"], check=True)
        ^^^^^^^^^^^^^^^^^^^^^^^
  File "/nix/store/ydymmshwzhxlkhdbxjypnb49ivwrkigl-python3.11-nodejs-bin-unstable-2022-11-10/lib/python3.11/site-packages/nodejs/npm.py", line 18, in run
    return node.run([
           ^^^^^^^^^^
  File "/nix/store/ydymmshwzhxlkhdbxjypnb49ivwrkigl-python3.11-nodejs-bin-unstable-2022-11-10/lib/python3.11/site-packages/nodejs/node.py", line 19, in run
    return subprocess.run([
           ^^^^^^^^^^^^^^^^
  File "/nix/store/gd3shnza1i50zn8zs04fa729ribr88m9-python3-3.11.8/lib/python3.11/subprocess.py", line 548, in run
    with Popen(*popenargs, **kwargs) as process:
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/nix/store/gd3shnza1i50zn8zs04fa729ribr88m9-python3-3.11.8/lib/python3.11/subprocess.py", line 1026, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
  File "/nix/store/gd3shnza1i50zn8zs04fa729ribr88m9-python3-3.11.8/lib/python3.11/subprocess.py", line 1953, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: '/nix/store/ydymmshwzhxlkhdbxjypnb49ivwrkigl-python3.11-nodejs-bin-unstable-2022-11-10/lib/python3.11/site-packages/nodejs/bin/node'

ERROR Backend subprocess exited when trying to invoke get_requires_for_build_wheel

Obviously at the bottom I see No such file or directory: '/nix/store/ydymmshwzhxlkhdbxjypnb49ivwrkigl-python3.11-nodejs-bin-unstable-2022-11-10/lib/python3.11/site-packages/nodejs/bin/node'

So let’s check again

nix build .#python3Packages.nodejs-bin
cd result/lib/python3.11/site-packages/nodejs/bin
realpath node

/nix/store/ydymmshwzhxlkhdbxjypnb49ivwrkigl-python3.11-nodejs-bin-unstable-2022-11-10/lib/python3.11/site-packages/nodejs/bin/node

To try and investigate further, I used this nix-build-phases.bash script.

Following through the script:

  • unpackPhase creates source/ directory with the contents of the basedpyright repo
  • patchPhase, updateAutotoolsGnuConfigScriptsPhase, and configurePhase don’t do anything obviously visible. Actually, it tells me there is no configure script so it did nothing.
  • buildPhase is where I get the error from above about the missing file from the python3.pkgs.nodejs-bin package

I get some additional context here:

  File "/home/vinny/dev/oss/nixpkgs/master/dev/source/pdm_build.py", line 20, in <module>
    _ = run(["ci"], check=True)
        ^^^^^^^^^^^^^^^^^^^^^^^
  File "/nix/store/ydymmshwzhxlkhdbxjypnb49ivwrkigl-python3.11-nodejs-bin-unstable-2022-11-10/lib/python3.11/site-packages/nodejs/npm.py", line 18, in run
    return node.run([
           ^^^^^^^^^^
  File "/nix/store/ydymmshwzhxlkhdbxjypnb49ivwrkigl-python3.11-nodejs-bin-unstable-2022-11-10/lib/python3.11/site-packages/nodejs/node.py", line 19, in run
    return subprocess.run([
           ^^^^^^^^^^^^^^^^
  File "/nix/store/gd3shnza1i50zn8zs04fa729ribr88m9-python3-3.11.8/lib/python3.11/subprocess.py", line 571, in run
    raise CalledProcessError(retcode, process.args,
subprocess.CalledProcessError: Command ['/nix/store/ydymmshwzhxlkhdbxjypnb49ivwrkigl-python3.11-nodejs-bin-unstable-2022-11-10/lib/python3.11/site-packages/nodejs/bin/node', '/nix/store/ydymmshwzhxlkhdbxjypnb49ivwrkigl-python3.11-nodejs-bin-unstable-2022-11-10/lib/python3.11/site-packages/nodejs/lib/node_modules/npm/bin/npm-cli.js', 'ci'] returned non-zero exit status 127.

ERROR Backend subprocess exited when trying to invoke get_requires_for_build_wheel
Finished creating a wheel...
Finished executing pypaBuildPhase

This is where I’m stuck. I just don’t get why the path /nix/store/ydymmshwzhxlkhdbxjypnb49ivwrkigl-python3.11-nodejs-bin-unstable-2022-11-10/lib/python3.11/site-packages/nodejs/bin/node isn’t available during this build and I guess I’m having trouble figuring out what to check next.

I feel like I’m missing something dumb and it’ll run with a simple change once I get it, but I don’t know where to look anymore. I’ve tried a bunch of random things with hooks that I won’t bother listing to save space.

If this looks familiar to you, would greatly appreciate any thoughts. I’ll be searching here/wiki/reddit in the meantime!

Thanks

This is often a red herring on Nix. Pre-packaged binaries will usually contain FHS paths which don’t exist in the sandbox or on NixOS. So the “file not found” isn’t because the binarie doesn’t exist, but rather some depedency.

The generally, the easy fix is to do

nativeBuildInputs = [ autoPatchElfHook ];

That should nixify any binaries located in our outputs.

Thanks for your response @jonringer ! While my problem isn’t solved it gave me a good starting point to read & try things :slight_smile:

I went through the build process manually in a development environment and manually ran the command causing issues in the source directory:

/nix/store/k0paxay1w330gvib1csvl5l2kr6cdniw-python3.11-nodejs-bin-unstable-2022-11-10/lib/python3.11/site-packages/nodejs/bin/node /nix/store/k0paxay1w330gvib1csvl5l2kr6cdniw-python3.11-nodejs-bin-unstable-2022-11-10/lib/python3.11/site-packages/nodejs/lib/node_modules/npm/bin/npm-cli.js ci

And I guess I should have looked closer at what the command actually says. I see why you said it’s a red-herring now: this is just calling npm ci through the python package to install the dependencies, which calls another script during the process:

npm ERR! code 127
npm ERR! path /home/vinny/dev/oss/nixpkgs/master/dev/source/node_modules/@parcel/watcher
npm ERR! command failed
npm ERR! command sh -c node-gyp-build
npm ERR! /usr/bin/env: ‘node’: No such file or directory

And obviously this thing it just pulled in isn’t patched and has a standard FHS path.

The overall process seems to be:

  • During basedpyright’s build process, it uses the Python nodejs-bin package to call, effectively, npm ci to install the node dependencies from package-lock.json
  • During the above, a script that npm itself pulled in attempts to execute, but fails because it references a standard FHS path

I’m imagining I’d have to do something like:

  • package the node dependencies separately so they can get patched properly(?)
  • use this as a dependency in basedpyright
  • override the build step to not run npm ci during basedpyright’s build

Does that sound right? Is there a way to handle the node dependencies as an intermediate step, or will I have to create a standalone package for the dependencies or something?

1 Like

Have you seen GitHub - LoganWalls/basedpyright-nix: A nix flake that packages basedpyright? Maybe this can give you some pointers.