Skip to content

--source: the tmpbin python -> python2 link breaks in several setups #435

Open

Description

@ekalinin

On Python 3, build_node_from_src looks up python2 and points python at it through a link in tmpbin (master, ecb4b74):

        python2_path = shutil.which('python2')
        if not python2_path:
            raise OSError(
                'Python >=3.0 virtualenv detected, but no python2 '
                'command (required for building node.js) was found'
            )
        logger.debug(' * Temporarily pointing python to %s', python2_path)
        node_tmpbin_dir = join(src_dir, 'tmpbin')
        node_tmpbin_link = join(node_tmpbin_dir, 'python')
        mkdir(node_tmpbin_dir)
        if not os.path.exists(node_tmpbin_link):
            callit(['ln', '-s', python2_path, node_tmpbin_link])
        env['PATH'] = '{}:{}'.format(node_tmpbin_dir,
                                     os.environ.get('PATH', ''))

PR #429 keeps this code for node.js before 13 (the lookup) and before 8.8 (the link). It breaks in these cases:

  1. Only python2.7 is on PATH. A Python 2.7 installed with make altinstall has python2.7 but no python2. nodeenv raises the "no python2 command" error, although node's own ./configure wrapper (6.12+ / 8.5+) tries python2.7 first.
  2. A dangling tmpbin/python from an earlier run. src is kept after a failed build or with --no-clean-src, and --force reuses it. os.path.exists follows the link, so once the old python2 is gone, ln -s fails with File exists.
  3. A stale tmpbin/python from an earlier run. If the old target still exists, the build uses it instead of the python2 found now.
  4. python2 is a pyenv shim. pyenv shims pick the program by their own name (program="${0##*/}", then pyenv exec "$program"), so a link named python to ~/.pyenv/shims/python2 runs pyenv exec python. With e.g. pyenv global 3.12.0 2.7.18 that is Python 3.12.
  5. A relative PATH entry. shutil.which returns a relative path (bin/python2), and ln -s stores it relative to tmpbin/, so the link dangles and ./configure can't find python.

The link is also made by running ln -s through callit, while the rest of the module uses os.symlink.

Reproduce

On master (ecb4b74) on macOS, where /usr/bin has no python2, with fake scripts in place of node's configure, make and python2. Run from a nodeenv checkout:

T=/tmp/y; mkdir -p $T
git show ecb4b74:nodeenv.py > $T/nodeenv.py
PY=$(command -v python3)
mkscript() { mkdir -p "$(dirname "$1")"; printf '#!/bin/sh\n%s\n' "$2" > "$1"; chmod +x "$1"; }
mkscript $T/tools/make 'exit 0'
mkscript $T/py27/python2.7 'echo "python2.7 runs"'
mkscript $T/py2old/python2 'echo "OLD python2 runs"'
mkscript $T/py2new/python2 'echo "NEW python2 runs"'
mkscript $T/shims/python2 'echo "shim dispatches to: ${0##*/}"'
# every case gets a node source dir whose configure runs `python`
newcase() { mkscript $T/$1/src/node-src/configure 'exec python'; }
cat > $T/run.py <<'EOF'
import sys, types
sys.path.insert(0, '/tmp/y')
import nodeenv
work = sys.argv[1]
args = types.SimpleNamespace(load_average=None, jobs='2', without_ssl=False,
    debug=False, profile=False, make_path='/tmp/y/tools/make', verbose=True)
try:
    nodeenv.build_node_from_src(work + '/env', work + '/src', work + '/src/node-src', args)
except OSError as e:
    print('OSError:', e)
EOF
  1. Only python2.7:
newcase c1
env PATH="$T/py27:/usr/bin:/bin" $PY $T/run.py $T/c1
OSError: Python >=3.0 virtualenv detected, but no python2 command (required for building node.js) was found
  1. Dangling link:
newcase c2; mkdir -p $T/c2/src/tmpbin; ln -s $T/gone/python2 $T/c2/src/tmpbin/python
env PATH="$T/py2new:/usr/bin:/bin" $PY $T/run.py $T/c2
ln: /tmp/y/c2/src/tmpbin/python: File exists
OSError: Command ln -s /tmp/y/py2new/python2 /tmp/y/c2/src/tmpbin/python failed with error code 1
  1. Stale link:
newcase c3; mkdir -p $T/c3/src/tmpbin; ln -s $T/py2old/python2 $T/c3/src/tmpbin/python
env PATH="$T/py2new:/usr/bin:/bin" $PY $T/run.py $T/c3
OLD python2 runs
  1. A shim that dispatches on its name like pyenv's:
newcase c4
env PATH="$T/shims:/usr/bin:/bin" $PY $T/run.py $T/c4
shim dispatches to: python
  1. Relative PATH entry:
newcase c5; mkscript $T/c5/bin/python2 'echo "python2 runs"'
(cd $T/c5 && env PATH="bin:/usr/bin:/bin" $PY $T/run.py $T/c5)
readlink $T/c5/src/tmpbin/python
./configure: line 2: exec: python: not found
OSError: Command ./configure --prefix=/tmp/y/c5/env failed with error code 127
bin/python2

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions