Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/changelog/2637.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add ``PKG_CONFIG_PATH`` environment variable support to all activation scripts (Bash, Batch, PowerShell, Fish, C Shell, Nushell, and Python). The virtualenv's ``lib/pkgconfig`` directory is now automatically prepended to ``PKG_CONFIG_PATH`` on activation and restored on deactivation, enabling packages that use ``pkg-config`` during build/install to find their configuration files - by :user:`rahuldevikar`.
9 changes: 9 additions & 0 deletions src/virtualenv/activation/bash/activate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ deactivate () {
export TK_LIBRARY
unset _OLD_VIRTUAL_TK_LIBRARY
fi
if ! [ -z "${_OLD_PKG_CONFIG_PATH+_}" ]; then
PKG_CONFIG_PATH="$_OLD_PKG_CONFIG_PATH"
export PKG_CONFIG_PATH
unset _OLD_PKG_CONFIG_PATH
fi

# The hash command must be called to get it to forget past
# commands. Without forgetting past commands the $PATH changes
Expand Down Expand Up @@ -74,6 +79,10 @@ _OLD_VIRTUAL_PATH="$PATH"
PATH="$VIRTUAL_ENV/"__BIN_NAME__":$PATH"
export PATH

_OLD_PKG_CONFIG_PATH="${PKG_CONFIG_PATH}"
PKG_CONFIG_PATH="${VIRTUAL_ENV}/lib/pkgconfig:${PKG_CONFIG_PATH}"
export PKG_CONFIG_PATH

if [ "x"__VIRTUAL_PROMPT__ != x ] ; then
VIRTUAL_ENV_PROMPT=__VIRTUAL_PROMPT__
else
Expand Down
3 changes: 3 additions & 0 deletions src/virtualenv/activation/batch/activate.bat
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
@if defined TK_LIBRARY @set "_OLD_VIRTUAL_TK_LIBRARY=%TK_LIBRARY%"
@if NOT "__TK_LIBRARY__"=="" @set "TK_LIBRARY=__TK_LIBRARY__"

@if defined PKG_CONFIG_PATH @set "_OLD_PKG_CONFIG_PATH=%PKG_CONFIG_PATH%"
@set "PKG_CONFIG_PATH=%VIRTUAL_ENV%\lib\pkgconfig;%PKG_CONFIG_PATH%"

@REM if defined _OLD_VIRTUAL_PATH (
@if not defined _OLD_VIRTUAL_PATH @goto ENDIFVPATH1
@set "PATH=%_OLD_VIRTUAL_PATH%"
Expand Down
4 changes: 4 additions & 0 deletions src/virtualenv/activation/batch/deactivate.bat
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
@if not defined _OLD_VIRTUAL_TK_LIBRARY @set TK_LIBRARY=
@set _OLD_VIRTUAL_TK_LIBRARY=

@if defined _OLD_PKG_CONFIG_PATH @set "PKG_CONFIG_PATH=%_OLD_PKG_CONFIG_PATH%"
@if not defined _OLD_PKG_CONFIG_PATH @set PKG_CONFIG_PATH=
@set _OLD_PKG_CONFIG_PATH=

@if not defined _OLD_VIRTUAL_PATH @goto ENDIFVPATH
@set "PATH=%_OLD_VIRTUAL_PATH%"
@set _OLD_VIRTUAL_PATH=
Expand Down
9 changes: 8 additions & 1 deletion src/virtualenv/activation/cshell/activate.csh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
set newline='\
'

alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH:q" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_TCL_LIBRARY != 0 && setenv TCL_LIBRARY "$_OLD_VIRTUAL_TCL_LIBRARY:q" && unset _OLD_VIRTUAL_TCL_LIBRARY || unsetenv TCL_LIBRARY; test $?_OLD_VIRTUAL_TK_LIBRARY != 0 && setenv TK_LIBRARY "$_OLD_VIRTUAL_TK_LIBRARY:q" && unset _OLD_VIRTUAL_TK_LIBRARY || unsetenv TK_LIBRARY; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT:q" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate && unalias pydoc'
alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH:q" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_TCL_LIBRARY != 0 && setenv TCL_LIBRARY "$_OLD_VIRTUAL_TCL_LIBRARY:q" && unset _OLD_VIRTUAL_TCL_LIBRARY || unsetenv TCL_LIBRARY; test $?_OLD_VIRTUAL_TK_LIBRARY != 0 && setenv TK_LIBRARY "$_OLD_VIRTUAL_TK_LIBRARY:q" && unset _OLD_VIRTUAL_TK_LIBRARY || unsetenv TK_LIBRARY; test $?_OLD_PKG_CONFIG_PATH != 0 && setenv PKG_CONFIG_PATH "$_OLD_PKG_CONFIG_PATH:q" && unset _OLD_PKG_CONFIG_PATH || unsetenv PKG_CONFIG_PATH; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT:q" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate && unalias pydoc'

# Unset irrelevant variables.
deactivate nondestructive
Expand All @@ -15,6 +15,13 @@ setenv VIRTUAL_ENV __VIRTUAL_ENV__
set _OLD_VIRTUAL_PATH="$PATH:q"
setenv PATH "$VIRTUAL_ENV:q/"__BIN_NAME__":$PATH:q"

if ($?PKG_CONFIG_PATH) then
set _OLD_PKG_CONFIG_PATH="$PKG_CONFIG_PATH"
setenv PKG_CONFIG_PATH "${VIRTUAL_ENV}/lib/pkgconfig:${PKG_CONFIG_PATH}"
else
setenv PKG_CONFIG_PATH "${VIRTUAL_ENV}/lib/pkgconfig"
endif

if (__TCL_LIBRARY__ != "") then
if ($?TCL_LIBRARY) then
set _OLD_VIRTUAL_TCL_LIBRARY="$TCL_LIBRARY"
Expand Down
8 changes: 8 additions & 0 deletions src/virtualenv/activation/fish/activate.fish
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ function deactivate -d 'Exit virtualenv mode and return to the normal environmen
end
end

if test -n "$_OLD_PKG_CONFIG_PATH"
set -gx PKG_CONFIG_PATH "$_OLD_PKG_CONFIG_PATH"
set -e _OLD_PKG_CONFIG_PATH
end

if test -n "$_OLD_VIRTUAL_PYTHONHOME"
set -gx PYTHONHOME "$_OLD_VIRTUAL_PYTHONHOME"
set -e _OLD_VIRTUAL_PYTHONHOME
Expand Down Expand Up @@ -77,6 +82,9 @@ deactivate nondestructive

set -gx VIRTUAL_ENV __VIRTUAL_ENV__

set -gx _OLD_PKG_CONFIG_PATH "$PKG_CONFIG_PATH"
set -gx PKG_CONFIG_PATH "$VIRTUAL_ENV/lib/pkgconfig:$PKG_CONFIG_PATH"

# https://github.com/fish-shell/fish-shell/issues/436 altered PATH handling
if test (string sub -s 1 -l 1 $FISH_VERSION) -lt 3
set -gx _OLD_VIRTUAL_PATH (_bashify_path $PATH)
Expand Down
4 changes: 3 additions & 1 deletion src/virtualenv/activation/nushell/activate.nu
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ export-env {
} else {
__VIRTUAL_PROMPT__
}
let new_env = { $path_name: $new_path VIRTUAL_ENV: $virtual_env VIRTUAL_ENV_PROMPT: $virtual_env_prompt }
let old_pkg_config_path = if (has-env 'PKG_CONFIG_PATH') { $env.PKG_CONFIG_PATH } else { '' }
let new_pkg_config_path = $'($virtual_env)/lib/pkgconfig:($old_pkg_config_path)'
let new_env = { $path_name: $new_path VIRTUAL_ENV: $virtual_env VIRTUAL_ENV_PROMPT: $virtual_env_prompt PKG_CONFIG_PATH: $new_pkg_config_path }
if (has-env 'TCL_LIBRARY') {
let $new_env = $new_env | insert TCL_LIBRARY __TCL_LIBRARY__
}
Expand Down
10 changes: 10 additions & 0 deletions src/virtualenv/activation/powershell/activate.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ function global:deactivate([switch] $NonDestructive) {
}
}

if (Test-Path variable:_OLD_PKG_CONFIG_PATH) {
$env:PKG_CONFIG_PATH = $variable:_OLD_PKG_CONFIG_PATH
Remove-Variable "_OLD_PKG_CONFIG_PATH" -Scope global
}

if (Test-Path function:_old_virtual_prompt) {
$function:prompt = $function:_old_virtual_prompt
Remove-Item function:\_old_virtual_prompt
Expand Down Expand Up @@ -78,6 +83,11 @@ if (__TK_LIBRARY__ -ne "") {

New-Variable -Scope global -Name _OLD_VIRTUAL_PATH -Value $env:PATH

if (Test-Path env:PKG_CONFIG_PATH) {
New-Variable -Scope global -Name _OLD_PKG_CONFIG_PATH -Value $env:PKG_CONFIG_PATH
}
$env:PKG_CONFIG_PATH = "$env:VIRTUAL_ENV\lib\pkgconfig;$env:PKG_CONFIG_PATH"

$env:PATH = "$env:VIRTUAL_ENV/" + __BIN_NAME__ + __PATH_SEP__ + $env:PATH
if (!$env:VIRTUAL_ENV_DISABLE_PROMPT) {
function global:_old_virtual_prompt {
Expand Down
8 changes: 8 additions & 0 deletions src/virtualenv/activation/python/activate_this.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@
os.environ["VIRTUAL_ENV"] = base # virtual env is right above bin directory
os.environ["VIRTUAL_ENV_PROMPT"] = __VIRTUAL_PROMPT__ or os.path.basename(base)

# Set PKG_CONFIG_PATH to include the virtualenv's pkgconfig directory
pkg_config_path = os.path.join(base, "lib", "pkgconfig")
existing_pkg_config_path = os.environ.get("PKG_CONFIG_PATH", "")
if existing_pkg_config_path:
os.environ["PKG_CONFIG_PATH"] = os.pathsep.join([pkg_config_path, existing_pkg_config_path])
else:
os.environ["PKG_CONFIG_PATH"] = pkg_config_path

# add the virtual environments libraries to the host python import mechanism
prev_length = len(sys.path)
for lib in __LIB_FOLDERS__.split(os.pathsep):
Expand Down
7 changes: 7 additions & 0 deletions tests/unit/activation/test_bash.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ def __init__(self, dest):
# The teardown logic is always present in deactivate()
assert "unset _OLD_VIRTUAL_TCL_LIBRARY" in content
assert "unset _OLD_VIRTUAL_TK_LIBRARY" in content
assert "unset _OLD_PKG_CONFIG_PATH" in content

# PKG_CONFIG_PATH is always set
assert '_OLD_PKG_CONFIG_PATH="${PKG_CONFIG_PATH}"' in content
assert 'PKG_CONFIG_PATH="${VIRTUAL_ENV}/lib/pkgconfig:${PKG_CONFIG_PATH}"' in content
assert "export PKG_CONFIG_PATH" in content
assert 'PKG_CONFIG_PATH="$_OLD_PKG_CONFIG_PATH"' in content

if present:
assert 'if [ /path/to/tcl != "" ]; then' in content
Expand Down
7 changes: 7 additions & 0 deletions tests/unit/activation/test_batch.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ def __init__(self, dest):
deactivate_content = (creator.bin_dir / "deactivate.bat").read_text(encoding="utf-8")

# THEN
# PKG_CONFIG_PATH is always set
assert '@if defined PKG_CONFIG_PATH @set "_OLD_PKG_CONFIG_PATH=%PKG_CONFIG_PATH%"' in activate_content
assert '@set "PKG_CONFIG_PATH=%VIRTUAL_ENV%\\lib\\pkgconfig;%PKG_CONFIG_PATH%"' in activate_content
assert '@if defined _OLD_PKG_CONFIG_PATH @set "PKG_CONFIG_PATH=%_OLD_PKG_CONFIG_PATH%"' in deactivate_content
assert "@if not defined _OLD_PKG_CONFIG_PATH @set PKG_CONFIG_PATH=" in deactivate_content
assert "@set _OLD_PKG_CONFIG_PATH=" in deactivate_content

if present:
assert '@if NOT "C:\\tcl"=="" @set "TCL_LIBRARY=C:\\tcl"' in activate_content
assert '@if NOT "C:\\tk"=="" @set "TK_LIBRARY=C:\\tk"' in activate_content
Expand Down
8 changes: 8 additions & 0 deletions tests/unit/activation/test_csh.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ def __init__(self, dest):
activator.generate(creator)
content = (creator.bin_dir / "activate.csh").read_text(encoding="utf-8")

# PKG_CONFIG_PATH is always set
assert "test $?_OLD_PKG_CONFIG_PATH != 0" in content
assert 'set _OLD_PKG_CONFIG_PATH="$PKG_CONFIG_PATH"' in content
assert 'setenv PKG_CONFIG_PATH "${VIRTUAL_ENV}/lib/pkgconfig:${PKG_CONFIG_PATH}"' in content
assert 'setenv PKG_CONFIG_PATH "${VIRTUAL_ENV}/lib/pkgconfig"' in content
assert 'setenv PKG_CONFIG_PATH "$_OLD_PKG_CONFIG_PATH:q"' in content
assert "unset _OLD_PKG_CONFIG_PATH" in content

if present:
assert "test $?_OLD_VIRTUAL_TCL_LIBRARY != 0" in content
assert "test $?_OLD_VIRTUAL_TK_LIBRARY != 0" in content
Expand Down
5 changes: 5 additions & 0 deletions tests/unit/activation/test_fish.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ def __init__(self, dest):
content = (creator.bin_dir / "activate.fish").read_text(encoding="utf-8")

# THEN
# PKG_CONFIG_PATH is always set
assert 'set -gx _OLD_PKG_CONFIG_PATH "$PKG_CONFIG_PATH"' in content
assert 'set -gx PKG_CONFIG_PATH "$VIRTUAL_ENV/lib/pkgconfig:$PKG_CONFIG_PATH"' in content
assert "set -e _OLD_PKG_CONFIG_PATH" in content

if present:
assert "set -gx TCL_LIBRARY '/path/to/tcl'" in content
assert "set -gx TK_LIBRARY '/path/to/tk'" in content
Expand Down
5 changes: 5 additions & 0 deletions tests/unit/activation/test_nushell.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ def __init__(self, dest):
content = (creator.bin_dir / "activate.nu").read_text(encoding="utf-8")

# THEN
# PKG_CONFIG_PATH is always set
assert "let old_pkg_config_path = if (has-env 'PKG_CONFIG_PATH')" in content
assert "let new_pkg_config_path = " in content
assert "PKG_CONFIG_PATH: $new_pkg_config_path" in content

expected_tcl = f"let $new_env = $new_env | insert TCL_LIBRARY {quoted_tcl_path}"
expected_tk = f"let $new_env = $new_env | insert TK_LIBRARY {quoted_tk_path}"

Expand Down
7 changes: 7 additions & 0 deletions tests/unit/activation/test_powershell.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ def __init__(self, dest):
content = (creator.bin_dir / "activate.ps1").read_text(encoding="utf-8-sig")

# THEN
# PKG_CONFIG_PATH is always set
assert "New-Variable -Scope global -Name _OLD_PKG_CONFIG_PATH" in content
assert '$env:PKG_CONFIG_PATH = "$env:VIRTUAL_ENV\\lib\\pkgconfig;$env:PKG_CONFIG_PATH"' in content
assert "if (Test-Path variable:_OLD_PKG_CONFIG_PATH)" in content
assert "$env:PKG_CONFIG_PATH = $variable:_OLD_PKG_CONFIG_PATH" in content
assert 'Remove-Variable "_OLD_PKG_CONFIG_PATH" -Scope global' in content

if present:
assert "if ('C:\\tcl' -ne \"\")" in content
assert "$env:TCL_LIBRARY = 'C:\\tcl'" in content
Expand Down
34 changes: 34 additions & 0 deletions tests/unit/activation/test_python_activator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,47 @@

import os
import sys
from argparse import Namespace
from ast import literal_eval
from textwrap import dedent

from virtualenv.activation import PythonActivator
from virtualenv.info import IS_WIN


def test_python_activator_generates_pkg_config_path(tmp_path):
"""Test that activate_this.py sets PKG_CONFIG_PATH."""

class MockInterpreter:
tcl_lib = None
tk_lib = None

class MockCreator:
def __init__(self, dest):
self.dest = dest
self.bin_dir = dest / ("Scripts" if IS_WIN else "bin")
self.bin_dir.mkdir(parents=True)
self.libs = [dest / "Lib" / "site-packages"]
self.env_name = "test-env"
self.interpreter = MockInterpreter()
self.pyenv_cfg = {}

creator = MockCreator(tmp_path)
options = Namespace(prompt=None)
activator = PythonActivator(options)

# Generate the activation script
activator.generate(creator)

# Read the generated script
content = (creator.bin_dir / "activate_this.py").read_text(encoding="utf-8")

# Verify PKG_CONFIG_PATH is set
assert "PKG_CONFIG_PATH" in content
assert "pkg_config_path" in content
assert 'os.path.join(base, "lib", "pkgconfig")' in content


def test_python(raise_on_non_source_class, activation_tester):
class Python(raise_on_non_source_class):
def __init__(self, session) -> None:
Expand Down
Loading