Tags: Node.js »»»» Node.js Installation »»»» Ubuntu
Node.js 14.x was just released, as was Ubuntu 20.04. Testing my application on that combination failed because native code npm packages fail because python is not found. WTF? I followed the instructions and installed build-essential which supposedly brings in every compiler tool we would need. But ... fortunately there is a simple solution.
Remember that when Node.js installs a native-code package, a build tool (node-gyp) is used. That build tool relies on Python. For Node.js 14.x, the team transitioned the requirements such that node-gyp
must be executed with Python3, because the Python team has end-of-life'd Python2.
As we'll see, some of the packages in the npm repository are affected by being unable to install because there is no python
command. By default Ubuntu installs Python 3, but it follows the Python community requirements of installing it as python3
. Therefore a script trying to run python
will fail because there is no python
command.
The actual bug here is the script which tries to run python
. The Python community has required for several years now that Python be installed as python2
and python3
, and therefore scripts must accommodate either one. Therefore an npm package that fails to install because python
is not found is in error.
I don't know where it's clearly stated the installation requirements to not only install Node.js, but to support installing native code modules. The npm documentation doesn't document the requirements. The document on building Node.js from source talks about Python 2 being eschewed in favor of Python 3, but suggests that Python 2 is used if it is found. The NodeSource page on installing on Linux from their package repository says to do only this:
apt-get install -y build-essential
Further, when you install Node.js on Ubuntu from the Nodesource package repository, you're told this:
## Run `sudo apt-get install -y nodejs` to install Node.js 13.x and npm
## You may also need development tools to build native addons:
sudo apt-get install gcc g++ make
Hence, that results in Python 3 being the only Python release installed. If we instead install build-essential
, Python 2 does get installed alongside Python 3.
I have installed - using Multipass - an Ubuntu 20.4 instance. To set up Node.js, I ran these commands:
1 curl -sL https://deb.nodesource.com/setup_13.x | sudo -E bash -
2 sudo apt-get update
3 sudo apt-get upgrade -y
4 sudo apt-get install -y nodejs build-essential
You'll note this is installing Node.js 13.x (13.13 to be exact) but the same result happens if I install Node.js 14.x instead.
Installing build-essential
is useful because it installs all compiler tools, including Python. Supposedly.
$ ls /usr/bin/python*
/usr/bin/python2 /usr/bin/python2.7 /usr/bin/python3 /usr/bin/python3.8
We have python2
and python3
installed, but no command named python
. Indeed, if I execute python, I get this:
$ python
Command 'python' not found, did you mean:
command 'python3' from deb python3
command 'python' from deb python-is-python3
There is no python command installed, and a couple suggestions are made. Ubuntu kindly makes suggestions like this to help guide us in which packages to install.
$ python3
Python 3.8.2 (default, Mar 13 2020, 10:14:16)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
Because Python 3 is installed, we can run it using the python3
command. Bottom line is that on Ubuntu 20.04 there is not a command named python
, only python2
and python3
.
As we'll see, for some native code packages in the npm repository, because there is no python
command. But.. that's by intention rather than a bug.
But, wait, the Python community wants us to use python2
and python3
commands
This issue will affect any other tool that expects to run the python
command. But it seems that my lack of knowledge about Python has led me astray. I thought that the python
command must be updated to equate to Python 3 because Python 3 is now the default. But looking at the Ubuntu Wiki, and then at
PEP-0394, I see the situation is more nuanced than that.
Namely, the Python community has been in a transition process for many years. There are some Python scripts that only support Python 2, some that only support Python 3, and some that support either. It's a problem if the python
command executes Python 3, because Python-2-only scripts will fail to run.
In the name of backwards compatibility, Unix/Linux/etc distributions are required to install Python 2 as python2
and to install Python 3 as python3
.
Therefore, Ubuntu is following the requirements laid out by the Python community. The lack of a python
command is by design rather than by mistake.
Back to installing npm packages
Let's try installing some native code packages and see what happens. To do so, we set up a test directory, and install packages that require native code installation.
5 mkdir t
6 cd t
7 npm init
8 node --version
9 npm install bcrypt --save
10 python
11 npm install sqlite3 --save
Installing bcrypt works fine. But installing sqlite3 fails with this:
$ npm install sqlite3 --save
> sqlite3@4.2.0 install /home/ubuntu/t/node_modules/sqlite3
> node-pre-gyp install --fallback-to-build
node-pre-gyp WARN Using needle for node-pre-gyp https download
node-pre-gyp WARN Pre-built binaries not installable for sqlite3@4.2.0 and node@13.13.0 (node-v79 ABI, glibc) (falling back to source compile with node-gyp)
node-pre-gyp WARN Hit error Remote end closed socket abruptly.
node-pre-gyp WARN Pre-built binaries not installable for sqlite3@4.2.0 and node@13.13.0 (node-v79 ABI, glibc) (falling back to source compile with node-gyp)
node-pre-gyp WARN Hit error bad download
make: Entering directory '/home/ubuntu/t/node_modules/sqlite3/build'
ACTION deps_sqlite3_gyp_action_before_build_target_unpack_sqlite_dep Release/obj/gen/sqlite-autoconf-3310100/sqlite3.c
make: Entering directory '/home/ubuntu/t/node_modules/sqlite3/build'
ACTION deps_sqlite3_gyp_action_before_build_target_unpack_sqlite_dep Release/obj/gen/sqlite-autoconf-3310100/sqlite3.c
/bin/sh: 1: python: not found
make: *** [deps/action_before_build.target.mk:13: Release/obj/gen/sqlite-autoconf-3310100/sqlite3.c] Error 127
make: Leaving directory '/home/ubuntu/t/node_modules/sqlite3/build'
There was more output, but the key is there - python: not found.
For libxslt the installation fails, but for another reason having to do with source incompatibilities.
Some native code packages, like sharp, provide prebuilt binaries and do not require compilation on the target system. In the case of SQLite3, that team does the same, but is apparently not supporting Ubuntu 20.04 with prebuilt binaries. Likewise, the SQLite3 team does not yet support Node.js 14.x with prebuilt binaries.
I cannot think of other native code packages but we have found one native code library that fails to install because the python
command does not exist.
The solution - that breaks the Python community recommendations
There is a very simple solution that is alluded to above. Namely:
$ sudo apt-get install python-is-python3
When we ran the python
command earlier, the message suggested we might find that command in the python-is-python3
package. So we install that package and now the python
command works:
$ python
Python 3.8.2 (default, Mar 13 2020, 10:14:16)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
And we can now successfully install SQLite3:
$ npm install sqlite3 --save
> bcrypt@4.0.1 install /home/ubuntu/t/node_modules/bcrypt
> node-pre-gyp install --fallback-to-build
node-pre-gyp WARN Using needle for node-pre-gyp https download
node-pre-gyp WARN Pre-built binaries not installable for bcrypt@4.0.1 and node@13.13.0 (node-v79 ABI, glibc) (falling back to source compile with node-gyp)
node-pre-gyp WARN Hit error Remote end closed socket abruptly.
make: Entering directory '/home/ubuntu/t/node_modules/bcrypt/build'
CC(target) Release/obj.target/nothing/../node-addon-api/src/nothing.o
AR(target) Release/obj.target/../node-addon-api/src/nothing.a
COPY Release/nothing.a
CXX(target) Release/obj.target/bcrypt_lib/src/blowfish.o
CXX(target) Release/obj.target/bcrypt_lib/src/bcrypt.o
Well, "success" is a relative term since there is a zillion warning messages printed because of deprecated this's and that's. But it no longer fails on the missing python
command.
But of course any Python script that is only compatible with Python 2 will now fail unless we remember to run the python2
command.
To do this magic, a symbolic link is add to /usr/bin
:
$ ls -l /usr/bin/python
lrwxrwxrwx 1 root root 7 Apr 15 03:45 /usr/bin/python -> python3
I found strong recommendations to not do this because obviously Python2 scripts will fail to run.
Installing the python-is-python3
package lets us run a script that expects to find a python
command. It sets up a condition that is incompatible with Python community requirements. We're supposed to transition to using python2
for Python 2 scripts, and python3
for Python 3 scripts, and to eschew the python
command. But sometimes pragmatism wins out over such clearly stated technical requirements.