summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaweł Tomulik <ptomulik@meil.pw.edu.pl>2018-10-10 22:33:56 (GMT)
committerPaweł Tomulik <ptomulik@meil.pw.edu.pl>2018-11-10 09:24:35 (GMT)
commit08863caeef77c0a579f774bd831e289124dd65e0 (patch)
tree25bfe89478e59744b5b16f3ee90f272388b4d934
parentbccc8cfc087cf8ee25a55eb76673a17e7e3a06d0 (diff)
downloadSCons-08863caeef77c0a579f774bd831e289124dd65e0.zip
SCons-08863caeef77c0a579f774bd831e289124dd65e0.tar.gz
SCons-08863caeef77c0a579f774bd831e289124dd65e0.tar.bz2
initial support for virtualenv
-rw-r--r--doc/man/scons.xml12
-rw-r--r--doc/user/misc.xml49
-rw-r--r--src/CHANGES.txt8
-rw-r--r--src/engine/SCons/Platform/PlatformTests.py8
-rw-r--r--src/engine/SCons/Platform/VE.py136
-rw-r--r--src/engine/SCons/Platform/VETests.py299
-rw-r--r--src/engine/SCons/Platform/__init__.py4
-rw-r--r--src/engine/SCons/Platform/posix.py5
-rw-r--r--src/engine/SCons/Platform/win32.py7
-rw-r--r--src/engine/SCons/Script/Main.py8
-rw-r--r--src/engine/SCons/Script/SConsOptions.py14
-rw-r--r--src/engine/SCons/Script/__init__.py2
-rw-r--r--test/virtualenv/activated/option/enable-virtualenv.py91
-rw-r--r--test/virtualenv/activated/option/ignore-virtualenv.py90
-rw-r--r--test/virtualenv/activated/virtualenv_activated_python.py96
-rw-r--r--test/virtualenv/activated/virtualenv_detect_virtualenv.py58
-rw-r--r--test/virtualenv/always/virtualenv_global_function.py68
-rw-r--r--test/virtualenv/regularenv/virtualenv_detect_regularenv.py57
-rw-r--r--test/virtualenv/unactivated/virtualenv_unactivated_python.py95
19 files changed, 1101 insertions, 6 deletions
diff --git a/doc/man/scons.xml b/doc/man/scons.xml
index abbed4f..9b48f69 100644
--- a/doc/man/scons.xml
+++ b/doc/man/scons.xml
@@ -997,6 +997,12 @@ the mechanisms in the specified order.</para>
</listitem>
</varlistentry>
<varlistentry>
+ <term>--enable-virtualenv</term>
+ <listitem>
+<para>Import virtualenv-related variables to SCons.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term>-f<emphasis> file</emphasis>, --file=<emphasis>file</emphasis>, --makefile=<emphasis>file</emphasis>, --sconstruct=<emphasis>file</emphasis></term>
<listitem>
<para>Use
@@ -1051,6 +1057,12 @@ are used, the directories are searched in the order specified.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>--ignore-virtualenv</term>
+ <listitem>
+<para>Suppress importing virtualenv-related variables to SCons.</para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term>--implicit-cache</term>
<listitem>
diff --git a/doc/user/misc.xml b/doc/user/misc.xml
index e390b7a..a5092d0 100644
--- a/doc/user/misc.xml
+++ b/doc/user/misc.xml
@@ -625,4 +625,53 @@ env.Command('directory_build_info',
</section>
+ <section>
+ <title>Virtual environments (virtualenvs)</title>
+
+ <para>
+
+ Virtualenv is a tool to create isolated Python environments.
+ A python application (such as SCons) may be executed within
+ an activated virtualenv. The activation of virtualenv modifies
+ current environment by defining some virtualenv-specific variables
+ and modifying search PATH, such that executables installed within
+ virtualenv's home directory are preferred over the ones installed
+ outside of it.
+
+ </para>
+
+ <para>
+
+ Normally, SCons uses hard-coded PATH when searching for external
+ executables, so it always picks-up executables from these pre-defined
+ locations. This applies also to python interpreter, which is invoked
+ by some custom SCons tools or test suites. This means, when running
+ SCons in a virtualenv, an eventual invocation of python interpreter from
+ SCons script will most probably jump out of virtualenv and execute
+ python executable found in hard-coded SCons PATH, not the one which is
+ executing SCons. Some users may consider this as an inconsistency.
+
+ </para>
+
+ <para>
+ This issue may be overcame by using <literal>--enable-virtualenv</literal>
+ option. The option automatically imports virtualenv-related environment
+ variables to all created construction environment <literal>env['ENV']</literal>,
+ and modifies SCons PATH appropriately to prefer virtualenv's executables.
+ Setting environment variable <literal>SCONS_ENABLE_VIRTUALENV=1</literal>
+ will have same effect. If virtualenv support is enabled system-vide
+ by the environment variable, it may be suppressed with
+ <literal>--ignore-virtualenv</literal> option.
+ </para>
+
+ <para>
+ Inside of SConscript, a global function <literal>Virtualenv</literal> is
+ available. It returns a path to virtualenv's home directory, or
+ <literal>None</literal> if SCons is not running from virtualenv. Note,
+ that this function returns a path even if SCons is run from an
+ unactivated virtualenv.
+ </para>
+
+ </section>
+
</chapter>
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index bd1e56a..d8eb1c8 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -104,6 +104,14 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE
- In the testing framework, module TestCommon, fixed must_contain(),
must_not_contain(), and related methods of TestCommon class to work with
substrings located at zero offset.
+ - Added virtualenv support. A new function Virtualenv() determines whether
+ SCons runs in a virtualenv. The search PATH may also be extended to
+ prefer executables from the current virtualenv over the ones provided by
+ base environment. New option --enable-virtualenv provided to import some
+ virtualenv-related variables to SCons and extend every env['ENV']['PATH']
+ automatically. New option --ignore-virtualenv disables this. Two
+ environment variables, SCONS_ENABLE_VIRTUALENV and
+ SCONS_IGNORE_VIRTUALENV are supported for the same purpose.
From Richard West:
- Add SConstruct.py, Sconstruct.py, sconstruct.py to the search path for the root SConstruct file.
diff --git a/src/engine/SCons/Platform/PlatformTests.py b/src/engine/SCons/Platform/PlatformTests.py
index 6f720ec..3f42eae 100644
--- a/src/engine/SCons/Platform/PlatformTests.py
+++ b/src/engine/SCons/Platform/PlatformTests.py
@@ -36,7 +36,7 @@ import SCons.Action
class Environment(collections.UserDict):
def Detect(self, cmd):
return cmd
-
+
def AppendENVPath(self, key, value):
pass
@@ -174,9 +174,9 @@ class TempFileMungeTestCase(unittest.TestCase):
SCons.Action.print_actions = 0
# Create an instance of object derived class to allow setattrb
class Node(object) :
- class Attrs(object):
+ class Attrs(object):
pass
- def __init__(self):
+ def __init__(self):
self.attributes = self.Attrs()
target = [Node()]
cmd = t(target, None, env, 0)
@@ -203,7 +203,7 @@ class PlatformEscapeTestCase(unittest.TestCase):
if __name__ == "__main__":
unittest.main()
-
+
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Platform/VE.py b/src/engine/SCons/Platform/VE.py
new file mode 100644
index 0000000..f7aa80c
--- /dev/null
+++ b/src/engine/SCons/Platform/VE.py
@@ -0,0 +1,136 @@
+"""SCons.Platform.VE
+
+Support for virtualenv.
+"""
+
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os
+import sys
+import SCons.Util
+
+
+def _get_bool_envvar(name, default=False):
+ try:
+ var = os.environ[name]
+ except KeyError:
+ return default
+ try:
+ return bool(int(var))
+ except ValueError:
+ if str(var).lower() in ('true', 'yes', 'y', 'on'):
+ return True
+ elif str(var).lower() in ('false', 'no', 'n', 'off'):
+ return False
+ else:
+ return default
+
+
+virtualenv_enabled_by_default = False
+
+
+def _enable_virtualenv_default():
+ return _get_bool_envvar('SCONS_ENABLE_VIRTUALENV', virtualenv_enabled_by_default)
+
+
+def _ignore_virtualenv_default():
+ return _get_bool_envvar('SCONS_IGNORE_VIRTUALENV', False)
+
+
+enable_virtualenv = _enable_virtualenv_default()
+ignore_virtualenv = _ignore_virtualenv_default()
+virtualenv_variables = ['VIRTUAL_ENV', 'PIPENV_ACTIVE']
+
+
+def _running_in_virtualenv():
+ """Returns True, if scons is executed within a virtualenv"""
+ # see https://stackoverflow.com/a/42580137
+ return (hasattr(sys, 'real_prefix') or
+ (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix))
+
+
+def _is_path_in(path, base):
+ """Returns true, if **path** is located under the **base** directory."""
+ if not path or not base: # empty path may happen, base too
+ return False
+ rp = os.path.relpath(path, base)
+ return ((not rp.startswith(os.path.pardir)) and (not rp == os.path.curdir))
+
+
+def _inject_venv_variables(env):
+ if 'ENV' not in env:
+ env['ENV'] = {}
+ ENV = env['ENV']
+ for name in virtualenv_variables:
+ try:
+ ENV[name] = os.environ[name]
+ except KeyError:
+ pass
+
+def _inject_venv_path(env, path_list=None):
+ """Modify environment such that SCons will take into account its virtualenv
+ when running external tools."""
+ if path_list is None:
+ path_list = os.getenv('PATH')
+ env.PrependENVPath('PATH', select_paths_in_venv(path_list))
+
+
+def select_paths_in_venv(path_list):
+ """Returns a list of paths from **path_list** which are under virtualenv's
+ home directory."""
+ if SCons.Util.is_String(path_list):
+ path_list = path_list.split(os.path.pathsep)
+ # Find in path_list the paths under the virtualenv's home
+ return [path for path in path_list if IsInVirtualenv(path)]
+
+
+def ImportVirtualenv(env):
+ """Copies virtualenv-related environment variables from OS environment
+ to ``env['ENV']`` and prepends virtualenv's PATH to ``env['ENV']['PATH']``.
+ """
+ _inject_venv_variables(env)
+ _inject_venv_path(env)
+
+
+def Virtualenv():
+ """Returns path to the virtualenv home if scons is executing within a
+ virtualenv or None, if not."""
+ if _running_in_virtualenv():
+ return sys.prefix
+ return None
+
+
+def IsInVirtualenv(path):
+ """Returns True, if **path** is under virtualenv's home directory. If not,
+ or if we don't use virtualenv, returns False."""
+ return _is_path_in(path, Virtualenv())
+
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Platform/VETests.py b/src/engine/SCons/Platform/VETests.py
new file mode 100644
index 0000000..8fd94af
--- /dev/null
+++ b/src/engine/SCons/Platform/VETests.py
@@ -0,0 +1,299 @@
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import SCons.compat
+
+import collections
+import unittest
+import os
+import sys
+
+import SCons.Platform.VE
+import SCons.Util
+
+class Environment(collections.UserDict):
+ def Detect(self, cmd):
+ return cmd
+
+ def AppendENVPath(self, key, value):
+ if SCons.Util.is_List(value):
+ value = os.path.pathsep.join(value)
+ if 'ENV' not in self:
+ self['ENV'] = {}
+ current = self['ENV'].get(key)
+ if not current:
+ self['ENV'][key] = value
+ else:
+ self['ENV'][key] = os.path.pathsep.join([current, value])
+
+ def PrependENVPath(self, key, value):
+ if SCons.Util.is_List(value):
+ value = os.path.pathsep.join(value)
+ if 'ENV' not in self:
+ self['ENV'] = {}
+ current = self['ENV'].get(key)
+ if not current:
+ self['ENV'][key] = value
+ else:
+ self['ENV'][key] = os.path.pathsep.join([value, current])
+
+class SysPrefixes(object):
+ """Used to temporarily mock sys.prefix, sys.real_prefix and sys.base_prefix"""
+ def __init__(self, prefix, real_prefix=None, base_prefix=None):
+ self._prefix = prefix
+ self._real_prefix = real_prefix
+ self._base_prefix = base_prefix
+
+ def start(self):
+ self._store()
+ sys.prefix = self._prefix
+ if self._real_prefix is None:
+ if hasattr(sys, 'real_prefix'):
+ del sys.real_prefix
+ else:
+ sys.real_prefix = self._real_prefix
+ if self._base_prefix is None:
+ if hasattr(sys, 'base_prefix'):
+ del sys.base_prefix
+ else:
+ sys.base_prefix = self._base_prefix
+
+ def stop(self):
+ self._restore()
+
+ def __enter__(self):
+ self.start()
+ attrs = ('prefix', 'real_prefix', 'base_prefix')
+ return {k: getattr(sys, k) for k in attrs if hasattr(sys, k)}
+
+ def __exit__(self, *args):
+ self.stop()
+
+ def _store(self):
+ s = dict()
+ if hasattr(sys, 'real_prefix'):
+ s['real_prefix'] = sys.real_prefix
+ if hasattr(sys, 'base_prefix'):
+ s['base_prefix'] = sys.base_prefix
+ s['prefix'] = sys.prefix
+ self._stored = s
+
+ def _restore(self):
+ s = self._stored
+ if 'real_prefix' in s:
+ sys.real_prefix = s['real_prefix']
+ if 'base_prefix' in s:
+ sys.base_prefix = s['base_prefix']
+ if 'prefix' in s:
+ sys.prefix = s['prefix']
+ del self._stored
+
+class OsEnviron(object):
+ """Used to temporarily mock os.environ"""
+ def __init__(self, environ):
+ self._environ = environ
+
+ def start(self):
+ self._stored = os.environ
+ os.environ = self._environ
+
+ def stop(self):
+ os.environ = self._stored
+ del self._stored
+
+ def __enter__(self):
+ self.start()
+ return os.environ
+
+ def __exit__(self, *args):
+ self.stop()
+
+def _p(p):
+ """Converts path string **p** from posix format to os-specific format."""
+ drive = []
+ if p.startswith('/') and sys.platform == 'win32':
+ drive = ['C:']
+ pieces = p.split('/')
+ return os.path.sep.join(drive + pieces)
+
+class _get_bool_envvar_TestCase(unittest.TestCase):
+ def test_missing(self):
+ with OsEnviron(dict()):
+ var = SCons.Platform.VE._get_bool_envvar('FOO')
+ assert var is False, "var should be False, not %s" % repr(var)
+ with OsEnviron({'FOO': '1'}):
+ var = SCons.Platform.VE._get_bool_envvar('BAR')
+ assert var is False, "var should be False, not %s" % repr(var)
+
+ def test_true(self):
+ for foo in [ 'TRUE', 'True', 'true',
+ 'YES', 'Yes', 'yes',
+ 'Y', 'y',
+ 'ON', 'On', 'on',
+ '1', '20', '-1']:
+ with OsEnviron({'FOO': foo}):
+ var = SCons.Platform.VE._get_bool_envvar('FOO')
+ assert var is True, 'var should be True, not %s' % repr(var)
+
+ def test_false(self):
+ for foo in [ 'FALSE', 'False', 'false',
+ 'NO', 'No', 'no',
+ 'N', 'n',
+ 'OFF', 'Off', 'off',
+ '0']:
+ with OsEnviron({'FOO': foo}):
+ var = SCons.Platform.VE._get_bool_envvar('FOO', True)
+ assert var is False, 'var should be True, not %s' % repr(var)
+
+ def test_default(self):
+ with OsEnviron({'FOO': 'other'}):
+ var = SCons.Platform.VE._get_bool_envvar('FOO', True)
+ assert var is True, 'var should be True, not %s' % repr(var)
+ var = SCons.Platform.VE._get_bool_envvar('FOO', False)
+ assert var is False, 'var should be False, not %s' % repr(var)
+
+
+class _is_path_in_TestCase(unittest.TestCase):
+ def test_false(self):
+ for args in [ ('',''),
+ ('', _p('/foo/bar')),
+ (_p('/foo/bar'), ''),
+ (_p('/foo/bar'), _p('/foo/bar')),
+ (_p('/foo/bar'), _p('/foo/bar/geez')),
+ (_p('/'), _p('/foo')),
+ (_p('foo'), _p('foo/bar')) ]:
+ assert SCons.Platform.VE._is_path_in(*args) is False, "_is_path_in(%r, %r) should be False" % args
+
+ def test__true(self):
+ for args in [ (_p('/foo'), _p('/')),
+ (_p('/foo/bar'), _p('/foo')),
+ (_p('/foo/bar/geez'), _p('/foo/bar')),
+ (_p('/foo//bar//geez'), _p('/foo/bar')),
+ (_p('/foo/bar/geez'), _p('/foo//bar')),
+ (_p('/foo/bar/geez'), _p('//foo//bar')) ]:
+ assert SCons.Platform.VE._is_path_in(*args) is True, "_is_path_in(%r, %r) should be True" % args
+
+class IsInVirtualenvTestCase(unittest.TestCase):
+ def test_false(self):
+ # "without wirtualenv" - always false
+ with SysPrefixes(_p('/prefix')):
+ for p in [ _p(''),
+ _p('/foo'),
+ _p('/prefix'),
+ _p('/prefix/foo') ]:
+ assert SCons.Platform.VE.IsInVirtualenv(p) is False, "IsInVirtualenv(%r) should be False" % p
+
+ # "with virtualenv"
+ with SysPrefixes(_p('/virtualenv/prefix'), real_prefix=_p('/real/prefix')):
+ for p in [ _p(''),
+ _p('/real/prefix/foo'),
+ _p('/virtualenv/prefix'),
+ _p('/virtualenv/prefix/bar/..'),
+ _p('/virtualenv/prefix/bar/../../bleah'),
+ _p('/virtualenv/bleah') ]:
+ assert SCons.Platform.VE.IsInVirtualenv(p) is False, "IsInVirtualenv(%r) should be False" % p
+
+ # "with venv"
+ with SysPrefixes(_p('/virtualenv/prefix'), base_prefix=_p('/base/prefix')):
+ for p in [ _p(''),
+ _p('/base/prefix/foo'),
+ _p('/virtualenv/prefix'),
+ _p('/virtualenv/prefix/bar/..'),
+ _p('/virtualenv/prefix/bar/../../bleah'),
+ _p('/virtualenv/bleah') ]:
+ assert SCons.Platform.VE.IsInVirtualenv(p) is False, "IsInVirtualenv(%r) should be False" % p
+
+ def test_true(self):
+ # "with virtualenv"
+ with SysPrefixes(_p('/virtualenv/prefix'), real_prefix=_p('/real/prefix')):
+ for p in [ _p('/virtualenv/prefix/foo'),
+ _p('/virtualenv/prefix/foo/bar') ]:
+ assert SCons.Platform.VE.IsInVirtualenv(p) is True, "IsInVirtualenv(%r) should be True" % p
+
+ # "with venv"
+ with SysPrefixes(_p('/virtualenv/prefix'), base_prefix=_p('/base/prefix')):
+ for p in [ _p('/virtualenv/prefix/foo'),
+ _p('/virtualenv/prefix/foo/bar') ]:
+ assert SCons.Platform.VE.IsInVirtualenv(p) is True, "IsInVirtualenv(%r) should be True" % p
+
+class _inject_venv_pathTestCase(unittest.TestCase):
+ def path_list(self):
+ return [
+ _p('/virtualenv/prefix/bin'),
+ _p('/virtualenv/prefix'),
+ _p('/virtualenv/prefix/../bar'),
+ _p('/home/user/.local/bin'),
+ _p('/usr/bin'),
+ _p('/opt/bin')
+ ]
+ def test_with_path_string(self):
+ env = Environment()
+ path_string = os.path.pathsep.join(self.path_list())
+ with SysPrefixes(_p('/virtualenv/prefix'), real_prefix=_p('/real/prefix')):
+ SCons.Platform.VE._inject_venv_path(env, path_string)
+ assert env['ENV']['PATH'] == _p('/virtualenv/prefix/bin'), env['ENV']['PATH']
+
+ def test_with_path_list(self):
+ env = Environment()
+ with SysPrefixes(_p('/virtualenv/prefix'), real_prefix=_p('/real/prefix')):
+ SCons.Platform.VE._inject_venv_path(env, self.path_list())
+ assert env['ENV']['PATH'] == _p('/virtualenv/prefix/bin'), env['ENV']['PATH']
+
+class VirtualenvTestCase(unittest.TestCase):
+ def test_none(self):
+ def _msg(given):
+ return "Virtualenv() should be None, not %s" % repr(given)
+
+ with SysPrefixes(_p('/prefix')):
+ ve = SCons.Platform.VE.Virtualenv()
+ assert ve is None , _msg(ve)
+ with SysPrefixes(_p('/base/prefix'), base_prefix=_p('/base/prefix')):
+ ve = SCons.Platform.VE.Virtualenv()
+ assert ve is None, _msg(ve)
+
+ def test_not_none(self):
+ def _msg(expected, given):
+ return "Virtualenv() should == %r, not %s" % (_p(expected), repr(given))
+
+ with SysPrefixes(_p('/virtualenv/prefix'), real_prefix=_p('/real/prefix')):
+ ve = SCons.Platform.VE.Virtualenv()
+ assert ve == _p('/virtualenv/prefix'), _msg('/virtualenv/prefix', ve)
+ with SysPrefixes(_p('/same/prefix'), real_prefix=_p('/same/prefix')):
+ ve = SCons.Platform.VE.Virtualenv()
+ assert ve == _p('/same/prefix'), _msg('/same/prefix', ve)
+ with SysPrefixes(_p('/virtualenv/prefix'), base_prefix=_p('/base/prefix')):
+ ve = SCons.Platform.VE.Virtualenv()
+ assert ve == _p('/virtualenv/prefix'), _msg('/virtualenv/prefix', ve)
+
+
+if __name__ == "__main__":
+ unittest.main()
+
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/src/engine/SCons/Platform/__init__.py b/src/engine/SCons/Platform/__init__.py
index 1654d0a..8784d5e 100644
--- a/src/engine/SCons/Platform/__init__.py
+++ b/src/engine/SCons/Platform/__init__.py
@@ -87,6 +87,7 @@ def platform_default():
else:
return sys.platform
+
def platform_module(name = platform_default()):
"""Return the imported module for the platform.
@@ -117,11 +118,13 @@ def platform_module(name = platform_default()):
setattr(SCons.Platform, name, mod)
return sys.modules[full_name]
+
def DefaultToolList(platform, env):
"""Select a default tool list for the specified platform.
"""
return SCons.Tool.tool_list(platform, env)
+
class PlatformSpec(object):
def __init__(self, name, generate):
self.name = name
@@ -133,6 +136,7 @@ class PlatformSpec(object):
def __str__(self):
return self.name
+
class TempFileMunge(object):
"""A callable class. You can set an Environment variable to this,
then call it with a string argument, then it will perform temporary
diff --git a/src/engine/SCons/Platform/posix.py b/src/engine/SCons/Platform/posix.py
index ae2ad1a..fb57521 100644
--- a/src/engine/SCons/Platform/posix.py
+++ b/src/engine/SCons/Platform/posix.py
@@ -41,6 +41,8 @@ import select
import SCons.Util
from SCons.Platform import TempFileMunge
+from SCons.Platform.VE import ImportVirtualenv
+from SCons.Platform.VE import ignore_virtualenv, enable_virtualenv
exitvalmap = {
2 : 127,
@@ -119,6 +121,9 @@ def generate(env):
# Must be able to have GCC and DMD work in the same build, so:
env['__DRPATH'] = '$_DRPATH'
+ if enable_virtualenv and not ignore_virtualenv:
+ ImportVirtualenv(env)
+
# Local Variables:
# tab-width:4
# indent-tabs-mode:nil
diff --git a/src/engine/SCons/Platform/win32.py b/src/engine/SCons/Platform/win32.py
index ea2fd3f..0cd8399 100644
--- a/src/engine/SCons/Platform/win32.py
+++ b/src/engine/SCons/Platform/win32.py
@@ -39,6 +39,8 @@ import tempfile
from SCons.Platform.posix import exitvalmap
from SCons.Platform import TempFileMunge
+from SCons.Platform.VE import ImportVirtualenv
+from SCons.Platform.VE import ignore_virtualenv, enable_virtualenv
import SCons.Util
try:
@@ -436,7 +438,7 @@ def generate(env):
if v:
env['ENV']['COMSPEC'] = v
- env.AppendENVPath('PATH', get_system_root() + '\System32')
+ env.AppendENVPath('PATH', get_system_root() + '\\System32')
env['ENV']['PATHEXT'] = '.COM;.EXE;.BAT;.CMD'
env['OBJPREFIX'] = ''
@@ -462,6 +464,9 @@ def generate(env):
env['HOST_OS'] = 'win32'
env['HOST_ARCH'] = get_architecture().arch
+ if enable_virtualenv and not ignore_virtualenv:
+ ImportVirtualenv(env)
+
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py
index 6516a15..daa6be8 100644
--- a/src/engine/SCons/Script/Main.py
+++ b/src/engine/SCons/Script/Main.py
@@ -59,6 +59,7 @@ import SCons.Job
import SCons.Node
import SCons.Node.FS
import SCons.Platform
+import SCons.Platform.VE
import SCons.SConf
import SCons.Script
import SCons.Taskmaster
@@ -863,6 +864,13 @@ def _main(parser):
for warning_type, message in delayed_warnings:
SCons.Warnings.warn(warning_type, message)
+ if not SCons.Platform.VE.virtualenv_enabled_by_default:
+ if options.enable_virtualenv:
+ SCons.Platform.VE.enable_virtualenv = True
+
+ if options.ignore_virtualenv:
+ SCons.Platform.VE.ignore_virtualenv = True
+
if options.diskcheck:
SCons.Node.FS.set_diskcheck(options.diskcheck)
diff --git a/src/engine/SCons/Script/SConsOptions.py b/src/engine/SCons/Script/SConsOptions.py
index 60d456e..a18c6b9 100644
--- a/src/engine/SCons/Script/SConsOptions.py
+++ b/src/engine/SCons/Script/SConsOptions.py
@@ -38,6 +38,7 @@ except ImportError:
_ = gettext
import SCons.Node.FS
+import SCons.Platform.VE
import SCons.Warnings
OptionValueError = optparse.OptionValueError
@@ -706,6 +707,12 @@ def Parser(version):
action="callback", callback=opt_duplicate,
help=opt_duplicate_help)
+ if not SCons.Platform.VE.virtualenv_enabled_by_default:
+ op.add_option('--enable-virtualenv',
+ dest="enable_virtualenv",
+ action="store_true",
+ help="Import certain virtualenv variables to SCons")
+
op.add_option('-f', '--file', '--makefile', '--sconstruct',
nargs=1, type="string",
dest="file", default=[],
@@ -733,6 +740,11 @@ def Parser(version):
help="Search DIR for imported Python modules.",
metavar="DIR")
+ op.add_option('--ignore-virtualenv',
+ dest="ignore_virtualenv",
+ action="store_true",
+ help="Do not import virtualenv variables to SCons")
+
op.add_option('--implicit-cache',
dest='implicit_cache', default=False,
action="store_true",
@@ -906,6 +918,7 @@ def Parser(version):
action="append",
help="Search REPOSITORY for source and target files.")
+
# Options from Make and Cons classic that we do not yet support,
# but which we may support someday and whose (potential) meanings
# we don't want to change. These all get a "the -X option is not
@@ -978,7 +991,6 @@ def Parser(version):
action="callback", callback=opt_not_yet,
# help="Warn when an undefined variable is referenced."
help=SUPPRESS_HELP)
-
return op
# Local Variables:
diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py
index 90bc311..ee3a191 100644
--- a/src/engine/SCons/Script/__init__.py
+++ b/src/engine/SCons/Script/__init__.py
@@ -82,6 +82,7 @@ import SCons.Builder
import SCons.Environment
import SCons.Node.FS
import SCons.Platform
+import SCons.Platform.VE
import SCons.Scanner
import SCons.SConf
import SCons.Subst
@@ -149,6 +150,7 @@ Environment = SCons.Environment.Environment
#OptParser = SCons.SConsOptions.OptParser
FindPathDirs = SCons.Scanner.FindPathDirs
Platform = SCons.Platform.Platform
+Virtualenv = SCons.Platform.VE.Virtualenv
Return = _SConscript.Return
Scanner = SCons.Scanner.Base
Tool = SCons.Tool.Tool
diff --git a/test/virtualenv/activated/option/enable-virtualenv.py b/test/virtualenv/activated/option/enable-virtualenv.py
new file mode 100644
index 0000000..dc3fa76
--- /dev/null
+++ b/test/virtualenv/activated/option/enable-virtualenv.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Ensure that the --enable-virtualenv flag works.
+"""
+
+import TestSCons
+import SCons.Platform.VE
+import sys
+import os
+import re
+
+test = TestSCons.TestSCons()
+
+if SCons.Platform.VE.virtualenv_enabled_by_default:
+ test.skip_test("Virtualenv support enabled by default, the option --enable-virtualenv is unavailable, skipping\n")
+
+if not SCons.Platform.VE.Virtualenv():
+ test.skip_test("No virtualenv detected, skipping\n")
+
+if not SCons.Platform.VE.select_paths_in_venv(os.getenv('PATH','')):
+ test.skip_test("Virtualenv detected but looks like unactivated, skipping\n")
+
+test.write('SConstruct', """
+import sys
+import SCons.Platform.VE
+env = DefaultEnvironment(tools=[])
+print("sys.executable: %r" % sys.executable)
+print("env.WhereIs('python'): %r" % env.WhereIs('python'))
+""")
+
+test.run(['-Q', '--enable-virtualenv'])
+s = test.stdout()
+m = re.search(r"""^sys\.executable:\s*(?P<py>["'][^"']+["'])\s*$""", s, re.MULTILINE)
+if not m:
+ test.fail_test(message="""\
+can't determine sys.executable from stdout:
+========= STDOUT =========
+%s
+==========================
+""" % s)
+
+interpreter = eval(m.group('py'))
+
+m = re.search(r"""^\s*env.WhereIs\('python'\):\s*(?P<py>["']?[^"']+["']?)\s*$""", s, re.MULTILINE)
+if not m:
+ test.fail_test(message="""
+can't determine env.WhereIs('python') from stdout:
+========= STDOUT =========
+%s
+==========================
+""" % s)
+
+python = eval(m.group('py'))
+
+test.fail_test(not SCons.Platform.VE.IsInVirtualenv(interpreter),
+ message="sys.executable points outside of virtualenv")
+test.fail_test(not SCons.Platform.VE.IsInVirtualenv(python),
+ message="env.WhereIs('python') points to virtualenv")
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/virtualenv/activated/option/ignore-virtualenv.py b/test/virtualenv/activated/option/ignore-virtualenv.py
new file mode 100644
index 0000000..05b462d
--- /dev/null
+++ b/test/virtualenv/activated/option/ignore-virtualenv.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Ensure that the --ignore-virtualenv flag works.
+"""
+
+import TestSCons
+import SCons.Platform.VE
+import sys
+import os
+import re
+
+test = TestSCons.TestSCons()
+
+if not SCons.Platform.VE.Virtualenv():
+ test.skip_test("No virtualenv detected, skipping\n")
+
+if not SCons.Platform.VE.select_paths_in_venv(os.getenv('PATH','')):
+ test.skip_test("Virtualenv detected but looks like unactivated, skipping\n")
+
+test.write('SConstruct', """
+import sys
+import SCons.Platform.VE
+env = DefaultEnvironment(tools=[])
+print("sys.executable: %s" % repr(sys.executable))
+print("env.WhereIs('python'): %s" % repr(env.WhereIs('python')))
+""")
+
+os.environ['SCONS_ENABLE_VIRTUALENV'] = '1'
+
+test.run(['-Q', '--ignore-virtualenv'])
+s = test.stdout()
+m = re.search(r"""^sys\.executable:\s*(?P<py>["']?[^"']+["']?)\s*$""", s, re.MULTILINE)
+if not m:
+ test.fail_test(message="""\
+can't determine sys.executable from stdout:
+========= STDOUT =========
+%s
+==========================
+""" % s)
+
+interpreter = eval(m.group('py'))
+
+m = re.search(r"""^\s*env.WhereIs\('python'\):\s*(?P<py>["']?[^"']+["']?)\s*$""", s, re.MULTILINE)
+if not m:
+ test.fail_test(message="""
+can't determine env.WhereIs('python') from stdout:
+========= STDOUT =========
+%s
+==========================
+""" % s)
+
+python = eval(m.group('py'))
+
+test.fail_test(not SCons.Platform.VE.IsInVirtualenv(interpreter),
+ message="sys.executable points outside of virtualenv")
+test.fail_test(SCons.Platform.VE.IsInVirtualenv(python),
+ message="env.WhereIs('python') points to virtualenv")
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/virtualenv/activated/virtualenv_activated_python.py b/test/virtualenv/activated/virtualenv_activated_python.py
new file mode 100644
index 0000000..3f591d6
--- /dev/null
+++ b/test/virtualenv/activated/virtualenv_activated_python.py
@@ -0,0 +1,96 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Check which python executable is running scons and which python executable
+would be used by scons, when we run under activated virtualenv (i.e. PATH
+contains the virtualenv's bin path). This test is skipped when ran in regular
+environment or in unactivated virtualenv.
+"""
+
+import TestSCons
+import SCons.Platform.VE
+import sys
+import os
+import re
+
+test = TestSCons.TestSCons()
+
+if not SCons.Platform.VE.Virtualenv():
+ test.skip_test("No virtualenv detected, skipping\n")
+
+if not SCons.Platform.VE.select_paths_in_venv(os.getenv('PATH')):
+ test.skip_test("Virtualenv detected but looks like unactivated, skipping\n")
+
+
+test.write('SConstruct', """
+import sys
+env = DefaultEnvironment(tools=[])
+print("sys.executable: %s" % repr(sys.executable))
+print("env.WhereIs('python'): %s" % repr(env.WhereIs('python')))
+""")
+
+if SCons.Platform.VE.virtualenv_enabled_by_default:
+ test.run(['-Q'])
+else:
+ test.run(['-Q', '--enable-virtualenv'])
+
+s = test.stdout()
+m = re.search(r"""^sys\.executable:\s*(?P<py>["']?[^"']+["']?)\s*$""", s, re.MULTILINE)
+if not m:
+ test.fail_test(message="""\
+can't determine sys.executable from stdout:
+========= STDOUT =========
+%s
+==========================
+""" % s)
+
+interpreter = eval(m.group('py'))
+
+m = re.search(r"""^\s*env\.WhereIs\('python'\):\s*(?P<py>["'][^"']+["'])\s*$""", s, re.MULTILINE)
+if not m:
+ test.fail_test(message="""
+can't determine env.WhereIs('python') from stdout:
+========= STDOUT =========
+%s
+==========================
+""" % s)
+
+python = eval(m.group('py'))
+
+# runing in activated virtualenv (after "activate") - PATH includes virtualenv's bin directory
+test.fail_test(not SCons.Platform.VE.IsInVirtualenv(interpreter),
+ message="sys.executable points outside of virtualenv")
+test.fail_test(not SCons.Platform.VE.IsInVirtualenv(python),
+ message="env.WhereIs('python') points outside of virtualenv")
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/virtualenv/activated/virtualenv_detect_virtualenv.py b/test/virtualenv/activated/virtualenv_detect_virtualenv.py
new file mode 100644
index 0000000..0eba385
--- /dev/null
+++ b/test/virtualenv/activated/virtualenv_detect_virtualenv.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Check if SCons.Platform.VE.Virtualenv() works in SConscripts.
+"""
+
+import TestSCons
+import SCons.Platform.VE
+import sys
+
+test = TestSCons.TestSCons()
+
+ve = SCons.Platform.VE.Virtualenv()
+if not ve:
+ test.skip_test("Virtualenv is not active, skipping\n")
+
+test.write('SConstruct', """
+print("virtualenv: %r" % Virtualenv())
+""")
+
+if SCons.Platform.VE.virtualenv_enabled_by_default:
+ test.run(['-Q'])
+else:
+ test.run(['-Q', '--enable-virtualenv'])
+
+test.must_contain_all_lines(test.stdout(), ['virtualenv: %r' % ve])
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/virtualenv/always/virtualenv_global_function.py b/test/virtualenv/always/virtualenv_global_function.py
new file mode 100644
index 0000000..18f93f8
--- /dev/null
+++ b/test/virtualenv/always/virtualenv_global_function.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Check which python executable is running scons and which python executable
+would be used by scons, when we run under activated virtualenv (i.e. PATH
+contains the virtualenv's bin path).
+"""
+
+import TestSCons
+import SCons.Platform.VE
+import re
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+print("Virtualenv(): %r" % Virtualenv())
+""")
+
+test.run(['-Q'])
+
+s = test.stdout()
+m = re.search(r"^Virtualenv\(\):\s*(?P<ve>.+\S)\s*$", s, re.MULTILINE)
+if not m:
+ test.fail_test(message="""\
+can't determine Virtualenv() result from stdout:
+========= STDOUT =========
+%s
+==========================
+""" % s)
+
+scons_ve = m.group('ve')
+our_ve = "%r" % SCons.Platform.VE.Virtualenv()
+
+# runing in activated virtualenv (after "activate") - PATH includes virtualenv's bin directory
+test.fail_test(scons_ve != our_ve,
+ message="Virtualenv() from SCons != Virtualenv() from caller script (%r != %r)" % (scons_ve, our_ve))
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/virtualenv/regularenv/virtualenv_detect_regularenv.py b/test/virtualenv/regularenv/virtualenv_detect_regularenv.py
new file mode 100644
index 0000000..e5dbc61
--- /dev/null
+++ b/test/virtualenv/regularenv/virtualenv_detect_regularenv.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Check if SCons.Platform.VE.Virtualenv() works in SConscript.
+"""
+
+import TestSCons
+import SCons.Platform.VE
+import sys
+
+test = TestSCons.TestSCons()
+
+if SCons.Platform.VE.Virtualenv():
+ test.skip_test("Virtualenv is active, skipping\n")
+
+test.write('SConstruct', """
+print("virtualenv: %r" % Virtualenv())
+""")
+
+if SCons.Platform.VE.virtualenv_enabled_by_default:
+ test.run(['-Q'])
+else:
+ test.run(['-Q', '--enable-virtualenv'])
+
+test.must_contain_all_lines(test.stdout(), ['virtualenv: %r' % None])
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/virtualenv/unactivated/virtualenv_unactivated_python.py b/test/virtualenv/unactivated/virtualenv_unactivated_python.py
new file mode 100644
index 0000000..a087318
--- /dev/null
+++ b/test/virtualenv/unactivated/virtualenv_unactivated_python.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Check which python executable is running scons and which python executable
+would be used by scons, when we run under an unactivated virtualenv (i.e. PATH
+does not contain virtualenv's bin path). This test is skipped if ran in
+a regular environment or in an activated virtualenv.
+"""
+
+import TestSCons
+import SCons.Platform.VE
+import sys
+import os
+import re
+
+test = TestSCons.TestSCons()
+
+if not SCons.Platform.VE.Virtualenv():
+ test.skip_test("No virtualenv detected, skipping\n")
+
+if SCons.Platform.VE.select_paths_in_venv(os.getenv('PATH')):
+ test.skip_test("Virtualenv detected and it looks like activated, skipping\n")
+
+test.write('SConstruct', """
+import sys
+env = DefaultEnvironment(tools=[])
+print("sys.executable: %s" % repr(sys.executable))
+print("env.WhereIs('python'): %s" % repr(env.WhereIs('python')))
+""")
+
+if SCons.Platform.VE.virtualenv_enabled_by_default:
+ test.run(['-Q'])
+else:
+ test.run(['-Q', '--enable-virtualenv'])
+
+s = test.stdout()
+m = re.search(r"""^sys\.executable:\s*(?P<py>["']?[^\"']+["']?)\s*$""", s, re.MULTILINE)
+if not m:
+ test.fail_test(message="""\
+can't determine sys.executable from stdout:
+========= STDOUT =========
+%s
+==========================
+""" % s)
+
+interpreter = eval(m.group('py'))
+
+m = re.search(r"""^\s*env\.WhereIs\('python'\):\s*(?P<py>["']?[^"']+[\"']?)\s*$""", s, re.MULTILINE)
+if not m:
+ test.fail_test(message="""
+can't determine env.WhereIs('python') from stdout:
+========= STDOUT =========
+%s
+==========================
+""" % s)
+
+python = eval(m.group('py'))
+
+# running without activating virtualenv (by just /path/to/virtualenv/bin/python runtest.py ...).
+test.fail_test(not SCons.Platform.VE.IsInVirtualenv(interpreter),
+ message="sys.executable points outside of virtualenv")
+test.fail_test(SCons.Platform.VE.IsInVirtualenv(python),
+ message="env.WhereIs('python') points to virtualenv")
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4: