From 2180ff6d0388162586fff59e066bc1e3e4bb9600 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sun, 26 Aug 2018 22:54:00 -0600 Subject: Stop using custom OrderedDict OrdredDict is in the standard library for all supported Python versions (2.7 and 3.5+) and has improvements over the ActiveState recipe version of OrderedDict we have been using. Switch to importing from collections instead of getting it from SCons.Util (tests already did this). At the same time, reorganize the Util.py imports - import Iterable from collections.abc if possible (it is deprecated to import it from collections, will stop working in 3.8); try getting the User{Dict,List,String} from collections if possible - that is, try the 3.x way first. Signed-off-by: Mats Wichmann --- src/engine/SCons/Action.py | 3 +- src/engine/SCons/Tool/javac.py | 3 +- src/engine/SCons/Util.py | 74 +++++------------------------------------- 3 files changed, 13 insertions(+), 67 deletions(-) diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py index 44ddacd..9a3888b 100644 --- a/src/engine/SCons/Action.py +++ b/src/engine/SCons/Action.py @@ -107,6 +107,7 @@ import sys import subprocess import itertools import inspect +from collections import OrderedDict import SCons.Debug from SCons.Debug import logInstanceCreation @@ -1294,7 +1295,7 @@ class ListAction(ActionBase): return result def get_varlist(self, target, source, env, executor=None): - result = SCons.Util.OrderedDict() + result = OrderedDict() for act in self.list: for var in act.get_varlist(target, source, env, executor): result[var] = True diff --git a/src/engine/SCons/Tool/javac.py b/src/engine/SCons/Tool/javac.py index b26c0b3..72c48f7 100644 --- a/src/engine/SCons/Tool/javac.py +++ b/src/engine/SCons/Tool/javac.py @@ -34,6 +34,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import os import os.path +from collections import OrderedDict import SCons.Action import SCons.Builder @@ -70,7 +71,7 @@ def emit_java_classes(target, source, env): if isinstance(entry, SCons.Node.FS.File): slist.append(entry) elif isinstance(entry, SCons.Node.FS.Dir): - result = SCons.Util.OrderedDict() + result = OrderedDict() dirnode = entry.rdir() def find_java_files(arg, dirpath, filenames): java_files = sorted([n for n in filenames diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py index 52f42d2..f691cd9 100644 --- a/src/engine/SCons/Util.py +++ b/src/engine/SCons/Util.py @@ -37,21 +37,18 @@ import pprint PY3 = sys.version_info[0] == 3 try: + from collections import UserDict, UserList, UserString +except ImportError: from UserDict import UserDict -except ImportError as e: - from collections import UserDict - -try: from UserList import UserList -except ImportError as e: - from collections import UserList - -from collections import Iterable + from UserString import UserString try: - from UserString import UserString -except ImportError as e: - from collections import UserString + from collections.abc import Iterable +except ImportError: + from collections import Iterable + +from collections import OrderedDict # Don't "from types import ..." these because we need to get at the # types module later to look for UnicodeType. @@ -63,7 +60,7 @@ MethodType = types.MethodType FunctionType = types.FunctionType try: - unicode + _ = type(unicode) except NameError: UnicodeType = str else: @@ -1032,59 +1029,6 @@ class CLVar(UserList): def __str__(self): return ' '.join(self.data) -# A dictionary that preserves the order in which items are added. -# Submitted by David Benjamin to ActiveState's Python Cookbook web site: -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747 -# Including fixes/enhancements from the follow-on discussions. -class OrderedDict(UserDict): - def __init__(self, dict = None): - self._keys = [] - UserDict.__init__(self, dict) - - def __delitem__(self, key): - UserDict.__delitem__(self, key) - self._keys.remove(key) - - def __setitem__(self, key, item): - UserDict.__setitem__(self, key, item) - if key not in self._keys: self._keys.append(key) - - def clear(self): - UserDict.clear(self) - self._keys = [] - - def copy(self): - dict = OrderedDict() - dict.update(self) - return dict - - def items(self): - return list(zip(self._keys, list(self.values()))) - - def keys(self): - return self._keys[:] - - def popitem(self): - try: - key = self._keys[-1] - except IndexError: - raise KeyError('dictionary is empty') - - val = self[key] - del self[key] - - return (key, val) - - def setdefault(self, key, failobj = None): - UserDict.setdefault(self, key, failobj) - if key not in self._keys: self._keys.append(key) - - def update(self, dict): - for (key, val) in dict.items(): - self.__setitem__(key, val) - - def values(self): - return list(map(self.get, self._keys)) class Selector(OrderedDict): """A callable ordered dictionary that maps file suffixes to -- cgit v0.12 From 4edd05726a8fad7f192e486a9539c44ea012cd94 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 6 Sep 2018 12:17:56 -0600 Subject: Update CHANGES.txt for OrderedDict change Signed-off-by: Mats Wichmann --- src/CHANGES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 95680dd..6f873b0 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -128,6 +128,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE time.clock deprecated since py3.3, due to remove in 3.8. deprecation warnings from py3.7 were failing a bunch of tests on Windows since they mess up expected stderr. + - Remove obsoleted internal implementaiton of OrderedDict. From Hao Wu - typo in customized decider example in user guide -- cgit v0.12 From bbde3d2bbf2a7e34d369c5be069fde2d5e235670 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 7 Sep 2018 07:37:58 -0600 Subject: Improvements to tarball packager Both gz and bzip2 now skip the test if tar is not found (previously they would not test, but mark the test OK). Since tar is now found on recent Windows 10, but the helper program bzip2 is not, check for bzip2 as well. This is actually a correct test for other systems as well, they have just been very unlikely to not have had bzip2 provisioned so it has not bitten us. Note: on Windows, with cygwin installed, the test may well find bzip2 but c:\Windows\System32\tar.exe does not find it and the test fails anyway. Work needed on this "finds too much" issue (not unique to this test). Signed-off-by: Mats Wichmann --- src/CHANGES.txt | 5 ++++- test/packaging/tar/bz2_packaging.py | 17 +++++++++++------ test/packaging/tar/gz.py | 13 +++++++------ 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 6f7e851..6fa9649 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -129,13 +129,16 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - Document and comment cleanup. - Added new Environment Value X_RPM_EXTRADEFS to supply custom settings to the specfile without adding specific logic for each one to scons. - - One swig test now checks for Python.h instead of failing + - The test for Python.h needed by swig tests is moved to get_python_platform + so it does not have to be repeated in every test; picks up one failure + which did not make the (previously needed) check. - If test opens os.devnull, register with atexit so file opens do not leak. - Fix bugs in Win32 process spawn logic to handle OSError exception correctly. - Use time.perf_counter instead of time.clock if it exists. time.clock deprecated since py3.3, due to remove in 3.8. deprecation warnings from py3.7 were failing a bunch of tests on Windows since they mess up expected stderr. + - tar packaging test fixups From Hao Wu - typo in customized decider example in user guide diff --git a/test/packaging/tar/bz2_packaging.py b/test/packaging/tar/bz2_packaging.py index 1552fd1..2a8b506 100644 --- a/test/packaging/tar/bz2_packaging.py +++ b/test/packaging/tar/bz2_packaging.py @@ -36,18 +36,23 @@ python = TestSCons.python test = TestSCons.TestSCons() tar = test.detect('TAR', 'tar') +if not tar: + test.skip_test('tar not found, skipping test\n') -if tar: - test.subdir('src') +bz2 = test.where_is('bzip2') +if not bz2: + test.skip_test('tar found, but helper bzip2 not found, skipping test\n') - test.write( [ 'src', 'main.c' ], r""" +test.subdir('src') + +test.write([ 'src', 'main.c'], r""" int main( int argc, char* argv[] ) { return 0; } """) - test.write('SConstruct', """ +test.write('SConstruct', """ Program( 'src/main.c' ) env=Environment(tools=['default', 'packaging']) env.Package( PACKAGETYPE = 'src_tarbz2', @@ -56,9 +61,9 @@ env.Package( PACKAGETYPE = 'src_tarbz2', source = [ 'src/main.c', 'SConstruct' ] ) """) - test.run(arguments='', stderr = None) +test.run(arguments='', stderr=None) - test.must_exist( 'src.tar.bz2' ) +test.must_exist('src.tar.bz2') test.pass_test() diff --git a/test/packaging/tar/gz.py b/test/packaging/tar/gz.py index f841c59..05661b7 100644 --- a/test/packaging/tar/gz.py +++ b/test/packaging/tar/gz.py @@ -36,18 +36,19 @@ python = TestSCons.python test = TestSCons.TestSCons() tar = test.detect('TAR', 'tar') +if not tar: + test.skip_test('tar not found, skipping test\n') -if tar: - test.subdir('src') +test.subdir('src') - test.write( [ 'src', 'main.c' ], r""" +test.write(['src', 'main.c'], r""" int main( int argc, char* argv[] ) { return 0; } """) - test.write('SConstruct', """ +test.write('SConstruct', """ Program( 'src/main.c' ) env=Environment(tools=['default', 'packaging']) env.Package( PACKAGETYPE = 'src_targz', @@ -56,9 +57,9 @@ env.Package( PACKAGETYPE = 'src_targz', source = [ 'src/main.c', 'SConstruct' ] ) """) - test.run(arguments='', stderr = None) +test.run(arguments='', stderr=None) - test.must_exist( 'src.tar.gz' ) +test.must_exist('src.tar.gz') test.pass_test() -- cgit v0.12 From 6c16e221611037b41353f18955ef54fdd09f463c Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 3 Sep 2018 08:21:39 -0600 Subject: Update testing docs A little formatting cleanup and some wording changes. Debugging end to end tests got its own section, could use more content probably. Signed-off-by: Mats Wichmann --- src/CHANGES.txt | 1 + testing/framework/test-framework.rst | 524 +++++++++++++++++++++-------------- 2 files changed, 311 insertions(+), 214 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 6f7e851..7d2eb0f 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -136,6 +136,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE time.clock deprecated since py3.3, due to remove in 3.8. deprecation warnings from py3.7 were failing a bunch of tests on Windows since they mess up expected stderr. + - updated the test-framework.rst documentation. From Hao Wu - typo in customized decider example in user guide diff --git a/testing/framework/test-framework.rst b/testing/framework/test-framework.rst index 7082eb9..cb6b8e1 100644 --- a/testing/framework/test-framework.rst +++ b/testing/framework/test-framework.rst @@ -2,14 +2,15 @@ SCons Testing Framework ======================= -SCons uses extensive automated tests to try to ensure quality. The primary goal -is that users should be able to upgrade from version to version without any surprise -changes in behavior. +SCons uses extensive automated tests to ensure quality. The primary goal +is that users be able to upgrade from version to version without +any surprise changes in behavior. -In general, no change goes into SCons unless it has one or more new or modified -tests that demonstrably exercise the bug being fixed or the feature being added. -There are exceptions to this guideline, but they should be just that, ''exceptions''. -When in doubt, make sure it's tested. +In general, no change goes into SCons unless it has one or more new +or modified tests that demonstrably exercise the bug being fixed or +the feature being added. There are exceptions to this guideline, but +they should be just that, ''exceptions''. When in doubt, make sure +it's tested. Test Organization ================= @@ -17,212 +18,193 @@ Test Organization There are three types of SCons tests: *End-to-End Tests* - End-to-end tests of SCons are all Python scripts (``*.py``) underneath - the ``test/`` subdirectory. They use the test infrastructure modules in the - ``testing/framework`` subdirectory. + End-to-end tests of SCons are Python scripts (``*.py``) underneath the + ``test/`` subdirectory. They use the test infrastructure modules in + the ``testing/framework`` subdirectory. They build set up complete + projects and call scons to execute them, checking that the behavior is + as expected. *Unit Tests* Unit tests for individual SCons modules live underneath the ``src/engine/`` subdirectory and are the same base name as the module - with ``Tests.py`` appended--for example, the unit tests for the - ``Builder.py`` module are in the ``BuilderTests.py`` script. + to be tests, with ``Tests`` appended before the ``.py``. For example, + the unit tests for the ``Builder.py`` module are in the + ``BuilderTests.py`` script. Unit tests tend to be based on assertions. *External Tests* - For the support of external Tools (in the form of packages, preferably), the - testing framework got extended, such that it can run in standalone mode. + For the support of external Tools (in the form of packages, preferably), + the testing framework is extended so it can run in standalone mode. You can start it from the top-level folder of your Tool's source tree, - where it then finds all Python scripts (``*.py``) underneath the - local ``test/`` directory. - This implies that Tool tests have to be kept in a folder named ``test``, - like for the SCons core. - + where it then finds all Python scripts (``*.py``) underneath the local + ``test/`` directory. This implies that Tool tests have to be kept in + a folder named ``test``, like for the SCons core. + Contrasting End-to-End and Unit Tests ##################################### -In general, anything that we've put into an end-to-end test script should -be considered a hardened part of the interface (that is, it's something -that a user might do) and should not be broken. Unit tests are now -considered more malleable, more for testing internal interfaces that -can change so long as we don't break users' ``SConscript`` files. (This -wasn't always the case, and there's a lot of meaty code in many of the -unit test scripts that does, in fact, capture external interface +In general, functionality with end-to-end tests +should be considered a hardened part of the public interface (that is, +something that a user might do) and should not be broken. Unit tests +are now considered more malleable, more for testing internal interfaces +that can change so long as we don't break users' ``SConscript`` files. +(This wasn't always the case, and there's a lot of meaty code in many +of the unit test scripts that does, in fact, capture external interface behavior. In general, we should try to move those things to end-to-end scripts as we find them.) -It's more difficult to debug end-to-end tests. You can actually go -straight into the Python debugger on the unit test scripts by using the -``runtest.py --pdb`` option, but the end-to-end tests treat an SCons -invocation as a "black box" and just look for external effects. -Simple ``print`` statements within the SCons code itself often don't help -debug end-to-end because they end up in SCons output that gets compared -against expected output and cause a test failure. Probably the most -effective technique is to use the internal ``SCons.Debug.Trace()`` function, -which prints output to ``/dev/tty`` on Linux/UNIX systems and ``con`` on -Windows systems, so you can see what's going on. - -Naming conventions +End-to-end tests are by their nature harder to debug. +You can drop straight into the Python debugger on the unit test +scripts by using the ``runtest.py --pdb`` option, but the end-to-end +tests treat an SCons invocation as a "black box" and just look for +external effects; simple methods like inserting ``print`` statements +in the SCons code itself can disrupt those external effects. +See `Debugging End-to-End Tests`_ for some more thoughts. + +Naming Conventions ################## -The end-to-end tests, more or less, stick to the following naming conventions: +The end-to-end tests, more or less, stick to the following naming +conventions: -1. All tests end with a .py suffix. +#. All tests end with a .py suffix. -2. In the *General* form we use +#. In the *General* form we use ``Feature.py`` - for the test of a specified feature; try to - keep this description reasonably short + for the test of a specified feature; try to keep this description + reasonably short ``Feature-x.py`` - for the test of a specified feature using - option ``x`` - -3. The *command line option* tests take the form + for the test of a specified feature using option ``x`` +#. The *command line option* tests take the form ``option-x.py`` for a lower-case single-letter option ``option--X.py`` - upper-case single-letter option - (with an extra hyphen, so the file names will - be unique on case-insensitive systems) + upper-case single-letter option (with an extra hyphen, so the + file names will be unique on case-insensitive systems) - ``option--lo.py`` - long option; abbreviate the long - option name to a few characters + ``option--lo.py`` + long option; abbreviate the long option name to a few characters Running Tests ============= -The standard set of SCons tests are run from the top-level source directory -by the ``runtest.py`` script. - -Help is available through the ``-h`` option: +The standard set of SCons tests are run from the top-level source +directory by the ``runtest.py`` script. -:: +Help is available through the ``-h`` option:: $ python runtest.py -h -To simply run all the tests, use the ``-a`` option: - -:: +To simply run all the tests, use the ``-a`` option:: $ python runtest.py -a -By default, ``runtest.py`` prints a count and percentage message for each test -case, along with the name of the test file. -If you need the output to be more silent, have a look at the ``-q``, ``-s`` and -``-k`` options. - -You may specifically list one or more tests to be run: +By default, ``runtest.py`` prints a count and percentage message for each +test case, along with the name of the test file. If you need the output +to be more silent, have a look at the ``-q``, ``-s`` and ``-k`` options. -:: +You may specifically list one or more tests to be run:: $ python runtest.py src/engine/SCons/BuilderTests.py $ python runtest.py test/option-j.py test/Program.py -Folder names are allowed arguments as well, so you can do a - -:: +Folder names are allowed arguments as well, so you can do:: $ python runtest.py test/SWIG to run all SWIG tests only. -You can also use the ``-f`` option to execute just the tests listed in a specified -text file: - -:: +You can also use the ``-f`` option to execute just the tests listed in +a specified text file:: $ cat testlist.txt test/option-j.py test/Program.py $ python runtest.py -f testlist.txt - One test must be listed per line, and any lines that begin with '#' -will be ignored (the intent being to allow you, for example, -to comment out tests that -are currently passing and then uncomment all of the tests in the file -for a final validation run). +will be ignored (the intent being to allow you, for example, to comment +out tests that are currently passing and then uncomment all of the tests +in the file for a final validation run). If more than one test is run, the ``runtest.py`` script prints a summary of how many tests passed, failed, or yielded no result, and lists any unsuccessful tests. The above invocations all test directly the files underneath the ``src/`` -subdirectory, and do not require that a packaging build be performed first. -The ``runtest.py`` script supports additional options to run tests against -unpacked packages in the ``build/test-*/`` subdirectories. +subdirectory, and do not require that a packaging build be performed +first. The ``runtest.py`` script supports additional options to run +tests against unpacked packages in the ``build/test-*/`` subdirectories. -If you are testing a separate Tool outside of the SCons source tree, you have -to call the ``runtest.py`` script in *external* (stand-alone) mode:: +If you are testing a separate Tool outside of the SCons source tree, you +have to call the ``runtest.py`` script in *external* (stand-alone) mode:: $ python ~/scons/runtest.py -e -a -. This ensures that the testing framework doesn't try to access SCons classes -needed for some of the *internal* test cases. +This ensures that the testing framework doesn't try to access SCons +classes needed for some of the *internal* test cases. -Note, that the actual tests are carried out in a temporary folder each, which gets -deleted afterwards. This ensures that your source directories don't get clobbered -with temporary files from the test runs. It also means that you can't simply change -into a folder to "debug things" after a test has gone wrong. For a way around this, -check out the ``PRESERVE`` environment variable. It can be seen in action in +Note, that the actual tests are carried out in a temporary folder each, +which gets deleted afterwards. This ensures that your source directories +don't get clobbered with temporary files from the test runs. It also +means that you can't simply change into a folder to "debug things" after +a test has gone wrong. For a way around this, check out the ``PRESERVE`` +environment variable. It can be seen in action in `How to convert old tests`_ below. Not Running Tests ================= -If you simply want to check which tests would get executed, you can call the -``runtest.py`` script with the ``-l`` option:: +If you simply want to check which tests would get executed, you can call +the ``runtest.py`` script with the ``-l`` option:: $ python runtest.py -l -Then there is also the ``-n`` option, which prints the command line for each -single test, but doesn't actually execute them:: +Then there is also the ``-n`` option, which prints the command line for +each single test, but doesn't actually execute them:: $ python runtest.py -n Finding Tests ============= -When started in *standard* mode - -:: +When started in *standard* mode:: $ python runtest.py -a +``runtest.py`` assumes that it is run from the SCons top-level source +directory. It then dives into the ``src`` and ``test`` folders, where +it tries to find filenames -, ``runtest.py`` assumes that it is run from the SCons top-level source directory. -It then dives into the ``src`` and ``test`` folders, where it tries to find filenames - - ``*Test.py`` - for the ``src`` directory, and - - ``*.py`` - for the ``test`` folder. +``*Test.py`` + for the ``src`` directory -When using fixtures, you may quickly end up in a position where you have supporting -Python script files in a subfolder, but they shouldn't get picked up as test scripts. -In this case you have two options: +``*.py`` + for the ``test`` folder -1. Add a file with the name ``sconstest.skip`` to your subfolder. This lets - ``runtest.py`` skip the contents of the directory completely. -2. Create a file ``.exclude_tests`` in each folder in question, and in it list - line-by-line the files to get excluded from testing. +When using fixtures, you may quickly end up in a position where you have +supporting Python script files in a subfolder, but they shouldn't get +picked up as test scripts. In this case you have two options: -The same rules apply when testing external Tools by using the ``-e`` option. +#. Add a file with the name ``sconstest.skip`` to your subfolder. This + lets ``runtest.py`` skip the contents of the directory completely. +#. Create a file ``.exclude_tests`` in each folder in question, and in + it list line-by-line the files to get excluded from testing. +The same rules apply when testing external Tools by using the ``-e`` +option. -"Hello, world!" SCons Test Script -================================= -To illustrate how the end-to-end test scripts work, -let's walk through a simple "Hello, world!" example: +Example End-to-End Test Script +============================== -:: +To illustrate how the end-to-end test scripts work, let's walk through +a simple "Hello, world!" example:: #!python import TestSCons @@ -249,157 +231,268 @@ let's walk through a simple "Hello, world!" example: test.pass_test() -``import TestSCons`` - Imports the main infrastructure for writing SCons tests. This is normally the only part of the infrastructure that needs importing. Sometimes other Python modules are necessary or helpful, and get imported before this line. +``import TestSCons`` + Imports the main infrastructure for writing SCons tests. This is + normally the only part of the infrastructure that needs importing. + Sometimes other Python modules are necessary or helpful, and get + imported before this line. ``test = TestSCons.TestSCons()`` - This initializes an object for testing. A fair amount happens under the covers when the object is created, including: + This initializes an object for testing. A fair amount happens under + the covers when the object is created, including: - * A temporary directory is created for all the in-line files that will get created. - * The temporary directory's removal is arranged for when the test is finished. - * We ``os.chdir()`` to the temporary directory. + * A temporary directory is created for all the in-line files that will + get created. -``test.write('SConstruct', ...`` - This line creates an ``SConstruct`` file in the temporary directory, to be used as input to the ``scons`` run(s) that we're testing. Note the use of the Python triple-quote syntax for the contents of the ``SConstruct`` file. Because input files for tests are all created from in-line data like this, the tests can sometimes get a little confusing to read, because some of the Python code is found + * The temporary directory's removal is arranged for when + the test is finished. -``test.write('hello.c', ...`` - This lines creates an ``hello.c`` file in the temporary directory. Note that we have to escape the ``\\n`` in the ``"Hello, world!\\n"`` string so that it ends up as a single backslash in the ``hello.c`` file on disk. + * The test does ``os.chdir()`` to the temporary directory. + +``test.write('SConstruct', ...)`` + This line creates an ``SConstruct`` file in the temporary directory, + to be used as input to the ``scons`` run(s) that we're testing. + Note the use of the Python triple-quote syntax for the contents + of the ``SConstruct`` file. Because input files for tests are all + created from in-line data like this, the tests can sometimes get + a little confusing to read, because some of the Python code is found + +``test.write('hello.c', ...)`` + This lines creates an ``hello.c`` file in the temporary directory. + Note that we have to escape the ``\\n`` in the + ``"Hello, world!\\n"`` string so that it ends up as a single + backslash in the ``hello.c`` file on disk. ``test.run()`` - This actually runs SCons. Like the object initialization, things happen under the covers: + This actually runs SCons. Like the object initialization, things + happen under the covers: - * The exit status is verified; the test exits with a failure if the exit status is not zero. - * The error output is examined, and the test exits with a failure if there is any + * The exit status is verified; the test exits with a failure if + the exit status is not zero. + * The error output is examined, and the test exits with a failure + if there is any. ``test.run(program='./hello', stdout="Hello, world!\n")`` - This shows use of the ``TestSCons.run()`` method to execute a program other than ``scons``, in this case the ``hello`` program we just presumably built. The ``stdout=`` keyword argument also tells the ``TestSCons.run()`` method to fail if the program output does not match the expected string ``"Hello, world!\n"``. Like the previous ``test.run()`` line, it will also fail the test if the exit status is non-zero, or there is any error output. + This shows use of the ``TestSCons.run()`` method to execute a program + other than ``scons``, in this case the ``hello`` program we just + presumably built. The ``stdout=`` keyword argument also tells the + ``TestSCons.run()`` method to fail if the program output does not + match the expected string ``"Hello, world!\n"``. Like the previous + ``test.run()`` line, it will also fail the test if the exit status is + non-zero, or there is any error output. ``test.pass_test()`` - This is always the last line in a test script. It prints ``PASSED`` on the screen and makes sure we exit with a ``0`` status to indicate the test passed. As a side effect of destroying the ``test`` object, the created temporary directory will be removed. + This is always the last line in a test script. It prints ``PASSED`` + on the screen and makes sure we exit with a ``0`` status to indicate + the test passed. As a side effect of destroying the ``test`` object, + the created temporary directory will be removed. -Working with fixtures +Working with Fixtures ===================== -In the simple example above, we have seen how to create files in the temporary test directory. -We give a filename to the ``TestSCons.write()`` method, together with its contents, and it gets -written to the test folder right before its start. - -This technique can still be seen throughout most of the end-to-end tests, but there is a better -way. It's much easier to edit, create and maintain real files, instead of copy/pasting -content to/from a Python script. If the test files get longer, the test script -gets longer and is harder to read. - -Against this, we now have the possibility to copy single files or the contents of a -local folder to the test directory. Since we can reuse these files/folders to setup -several tests, we call them *fixtures* in the following. - -Directory fixtures +In the simple example above, the files to set up the test are created +on the fly by the test program. We give a filename to the ``TestSCons.write()`` +method, and a string holding its contents, and it gets written to the test +folder right before starting.. + +This technique can still be seen throughout most of the end-to-end tests, +but there is a better way. To create a test, you need to create the +files that will be used, then when they work reasonably, they need to +be pasted into the script. The process repeats for maintenance. Once +a test gets more complex and/or grows many steps, the test script gets +harder to read. Why not keep the files as is? + +In testing parlance, a fixture is a repeatable test setup. The scons +test harness allows the use of saved files or directories to be used +in that sense: "the fixture for this test is foo", instead of writing +a whole bunch of strings to create files. Since these setups can be +reusable across multiple tests, the *fixture* terminology applies well. + +Directory Fixtures ################## -The function ``dir_fixture(self, srcdir, dstdir=None)`` in the ``TestCmd`` class -copies the contents of the specified folder ``srcdir`` from -the directory of the called test script, to the current -temporary test directory. -The ``srcdir`` name may be a list, in which case the elements are -concatenated with the ``os.path.join()`` method. The ``dstdir`` is -assumed to be under the temporary working directory, it gets -created automatically, if it does not already exist. +The function ``dir_fixture(self, srcdir, dstdir=None)`` in the ``TestCmd`` +class copies the contents of the specified folder ``srcdir`` from +the directory of the called test script to the current temporary test +directory. The ``srcdir`` name may be a list, in which case the elements +are concatenated with the ``os.path.join()`` method. The ``dstdir`` +is assumed to be under the temporary working directory, it gets created +automatically, if it does not already exist. A short syntax example:: test = TestSCons.TestSCons() test.dir_fixture('image') - test.run() + test.run() + +would copy all files and subfolders from the local ``image`` folder, +to the temporary directory for the current test. -would copy all files and subfolders from the local ``image`` folder, to -the temporary directory for the current test. +To see a real example for this in action, refer to the test named +``test/packaging/convenience-functions/convenience-functions.py``. -If you'd like to see a real example for this in action, refer to the test -named ``test/packaging/convenience-functions/convenience-functions.py``. - -File fixtures +File Fixtures ############# Like for directory fixtures, ``file_fixture(self, srcfile, dstfile=None)`` -copies the file ``srcfile`` from the directory of -the called script, to the temporary test directory. -The ``dstfile`` is assumed to be under the temporary working -directory, unless it is an absolute path name. -If ``dstfile`` is specified, its target directory gets created +copies the file ``srcfile`` from the directory of the called script, +to the temporary test directory. The ``dstfile`` is assumed to be +under the temporary working directory, unless it is an absolute path +name. If ``dstfile`` is specified, its target directory gets created automatically if it doesn't already exist. -With a:: +With the following code:: test = TestSCons.TestSCons() test.file_fixture('SConstruct') test.file_fixture(['src','main.cpp'],['src','main.cpp']) - test.run() + test.run() -you would copy the files ``SConstruct`` and ``src/main.cpp`` to the temporary -test folder, prior to running the test itself. +The files ``SConstruct`` and ``src/main.cpp`` are copied to the +temporary test directory. Notice the second ``file_fixture`` line +preserves the path of the original, otherwise ``main.cpp`` +would have landed in the top level of the test directory. -Again, a reference example can be found in the current *default* revision of -SCons, it is ``test/packaging/sandbox-test/sandbox-test.py``. +Again, a reference example can be found in the current revision +of SCons, it is ``test/packaging/sandbox-test/sandbox-test.py``. -For even more examples you should check out one of the external Tools, e.g. the -*Qt4* Tool at https://bitbucket.org/dirkbaechle/scons_qt4. Also visit the SCons -Tools Index at https://github.com/SCons/scons/wiki/ToolsIndex for a complete +For even more examples you should check out +one of the external Tools, e.g. the *Qt4* Tool at +https://bitbucket.org/dirkbaechle/scons_qt4. Also visit the SCons Tools +Index at https://github.com/SCons/scons/wiki/ToolsIndex for a complete list of available Tools, though not all may have tests yet. -How to convert old tests -######################## +How to Convert Old Tests to Use Fixures +####################################### -We now show how to convert a test, still using the ``TestSCons.write()`` method, to -the fixture based approach. For this, we need to get at the files as they -are written to each temporary test folder. +Tests using the inline ``TestSCons.write()`` method can easily be +converted to the fixture based approach. For this, we need to get at the +files as they are written to each temporary test folder. -Luckily, ``runtest.py`` checks for the existence of an environment variable named -``PRESERVE``. If it is set to a non-zero value, the testing framework doesn't delete -the test folder as ususal, but prints its name to the screen. +``runtest.py`` checks for the existence of an environment +variable named ``PRESERVE``. If it is set to a non-zero value, the testing +framework preserves the test folder instead of deleting it, and prints +its name to the screen. -So, you should be able to give the commands +So, you should be able to give the commands:: -:: + $ PRESERVE=1 python runtest.py test/packaging/sandbox-test.py - $ export PRESERVE=1 - $ python runtest.py test/packaging/sandbox-test.py +assuming Linux and a bash-like shell. For a Windows ``cmd`` shell, use +``set PRESERVE=1`` (that will leave it set for the duration of the +``cmd`` session, unless manually deleted). -, assuming Linux and a bash-like shell. - The output should then look something like this:: 1/1 (100.00%) /usr/bin/python -tt test/packaging/sandbox-test.py PASSED Preserved directory /tmp/testcmd.4060.twlYNI -and you see that the test files have been kept in the folder ``/tmp/testcmd.4060.twlYNI``, -where you can now copy them from to your new *fixture* folder. Then, in the test -script you simply remove all the tedious ``TestSCons.write()`` statements and -replace them by a single ``TestSCons.dir_fixture()``. +You can now copy the files from that folder to your new +*fixture* folder. Then, in the test script you simply remove all the +tedious ``TestSCons.write()`` statements and replace them by a single +``TestSCons.dir_fixture()``. + +Finally, don't forget to clean up and remove the temporary test +directory. ``;)`` + +When Not to Use a Fixture +######################### + +Note that some files are not appropriate for use in a fixture as-is: +fixture files should be static. If the creation of the file involves +interpolating data discovered during the run of the test script, +that process should stay in the script. Here is an example of this +kind of usage that does not lend itself to a fixture:: + + import TestSCons + _python_ = TestSCons._python_ + + test.write('SConstruct', """ + cc = Environment().Dictionary('CC') + env = Environment(LINK = r'%(_python_)s mylink.py', + LINKFLAGS = [], + CC = r'%(_python_)s mycc.py', + CXX = cc, + CXXFLAGS = []) + env.Program(target = 'test1', source = 'test1.c') + """ % locals()) + +Here the value of ``_python_`` is picked out of the script's +``locals`` dictionary and interpolated into the string that +will be written to ``SConstruct``. + +The other files created in this test may still be candidates for +use in a fixture, however. + +Debugging End-to-End Tests +========================== + +Most of the end to end tests have expectations for standard output +and error from the test runs. The expectation could be either +that there is nothing on that stream, or that it will contain +very specific text which the test matches against. So adding +``print()`` calls, or ``sys,stderr.write()`` or similar will +emit data that the tests do not expect, and cause further failures. +Say you have three different tests in a script, and the third +one is unexpectedly failing. You add some debug prints to the +part of scons that is involved, and now the first test of the +three starts failing, aborting the test run before it gets +to the third test you were trying to debug. + +Still, there are some techniques to help debugging. + +Probably the most effective technique is to use the internal +``SCons.Debug.Trace()`` function, which prints output to +``/dev/tty`` on Linux/UNIX systems and ``con`` on Windows systems, +so you can see what's going on. + +If you do need to add informational messages in scons code +to debug a problem, you can use logging and send the messages +to a file instead, so they don't interrupt the test expectations. + +Part of the technique discussed in the section +`How to Convert Old Tests to Use Fixures`_ can also be helpful +for debugging purposes. If you have a failing test, try:: + + $ PRESERVE=1 python runtest.py test/failing-test.py + +You can now go to the save directory reported from this run +and invoke the test manually to see what it is doing, without +the presence of the test infrastructure which would otherwise +"swallow" output you may be interested in. In this case, +adding debug prints may be more useful. -Finally, you shouldn't forget to clean up and remove the temporary test directory. ``;)`` Test Infrastructure =================== -The test API is in ``testing/framework/TestSCons.py``. ``TestSCons`` is a subclass of -``TestCommon``, which is a subclass of ``TestCmd``; all those python files are -in ``testing/framework``. Start in ``testing/framework/TestCmd.py`` for the base API definitions, -like how to create files (``test.write()``) and run commands (``test.run()``). +The main test API in the ``TestSCons.py`` class. ``TestSCons`` +is a subclass of ``TestCommon``, which is a subclass of ``TestCmd``. +All those classes are defined in python files of the same name +in ``testing/framework``. Start in +``testing/framework/TestCmd.py`` for the base API definitions, like how +to create files (``test.write()``) and run commands (``test.run()``). -You want to use ``TestSCons`` for the end-to-end tests in ``test``, but ``TestCmd`` -for the unit tests in the ``src`` folder. +Use ``TestSCons`` for the end-to-end tests in ``test``, but use +``TestCmd`` for the unit tests in the ``src`` folder. The match functions work like this: -TestSCons.match_re:: match each line with a RE +``TestSCons.match_re`` + match each line with a RE + * Splits the lines into a list (unless they already are) * splits the REs at newlines (unless already a list) and puts ^..$ around each - * then each RE must match each line. This means there must be as many REs as lines. + * then each RE must match each line. This means there must be as many + REs as lines. + +``TestSCons.match_re_dotall`` + match all the lines against a single RE -TestSCons.match_re_dotall:: match all the lines against a single RE * Joins the lines with newline (unless already a string) - * joins the REs with newline (unless it's a string) and puts ^..$ around the whole thing + * joins the REs with newline (unless it's a string) and puts ``^..$`` + around the whole thing * then whole thing must match with python re.DOTALL. Use them in a test like this:: @@ -410,18 +503,21 @@ or:: test.must_match(..., match=TestSCons.match_re, ...) -Avoiding Tests based on Tool existence +Avoiding Tests Based on Tool Existence ====================================== -Here's an easy sample:: +Here's a simple example:: #!python intelc = test.detect_tool('intelc', prog='icpc') if not intelc: test.skip_test("Could not load 'intelc' Tool; skipping test(s).\n") -See ``testing/framework/TestSCons.py`` for the ``detect_tool`` method. It calls the tool's -``generate()`` method, and then looks for the given prog (tool name by default) in -``env['ENV']['PATH']``. - +See ``testing/framework/TestSCons.py`` for the ``detect_tool`` method. +It calls the tool's ``generate()`` method, and then looks for the given +program (tool name by default) in ``env['ENV']['PATH']``. +The ``where_is`` method can be used to look for programs that +are do not have tool specifications. The existing test code +will have many samples of using either or both of these to detect +if it is worth even proceeding with a test. -- cgit v0.12 From fe27baf677366633c9ec64f2f7b22cb4063adc28 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Tue, 11 Sep 2018 07:24:41 -0600 Subject: Add java 11 jdk 11.0 is almost at GA, and it will be the first edition designated as Long Term Support (LTS), with commercial support until Sept 2023, so we might as well add support to scons. Going forward, is it worth continuing to check for individual versions as being "supported"? Signed-off-by: Mats Wichmann --- src/CHANGES.txt | 2 +- src/engine/SCons/Tool/JavaCommon.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 6f7e851..353d5ab 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -101,7 +101,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE This changes SCons to better comply with normal Python installation practices. From Mats Wichmann: - - Recognize new java 9, 10 (as 9.0 and 10.0) + - Recognize new java 9, 10, 11 (as 9.0 and 10.0, 11.0) - Updated manpage scons.xml to fix a nested list problem - Updated doc terminiology: use prepend instead of append as appropriate - XML validity fixes from SConstruct.py change diff --git a/src/engine/SCons/Tool/JavaCommon.py b/src/engine/SCons/Tool/JavaCommon.py index 8349164..e90e768 100644 --- a/src/engine/SCons/Tool/JavaCommon.py +++ b/src/engine/SCons/Tool/JavaCommon.py @@ -69,7 +69,7 @@ if java_parsing: def __init__(self, version=default_java_version): if not version in ('1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', - '1.8', '5', '6', '9.0', '10.0'): + '1.8', '5', '6', '9.0', '10.0', '11.0'): msg = "Java version %s not supported" % version raise NotImplementedError(msg) @@ -177,7 +177,7 @@ if java_parsing: if self.version in ('1.1', '1.2', '1.3', '1.4'): clazz = self.listClasses[0] self.listOutputs.append('%s$%d' % (clazz, self.nextAnon)) - elif self.version in ('1.5', '1.6', '1.7', '1.8', '5', '6', '9.0', '10.0'): + elif self.version in ('1.5', '1.6', '1.7', '1.8', '5', '6', '9.0', '10.0', '11.0'): self.stackAnonClassBrackets.append(self.brackets) className = [] className.extend(self.listClasses) -- cgit v0.12 From b71404ff2d256460f23c4494932af2ea61d1a271 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 20 Sep 2018 22:35:51 -0400 Subject: Fix test/Execute.py failing on windows with python 2.7.15. It worked fine with python 2.7.14 and below. The output message changed from / to \ and broke the test. --- test/Execute.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/Execute.py b/test/Execute.py index edd746e..c40d0d0 100644 --- a/test/Execute.py +++ b/test/Execute.py @@ -27,7 +27,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" """ Test the Execute() function for executing actions directly. """ - +import sys import TestSCons _python_ = TestSCons._python_ @@ -82,11 +82,12 @@ test.write('k.in', "k.in\n") test.write('l.in', "l.in\n") test.write('m.in', "m.in\n") -import sys if sys.platform == 'win32' and sys.version_info[0] == 2: + # note that nonexistent.in will have a \ on windows with python < 2.7.15 + # and a / on >= 2.7.15 (The third line below) expect = r"""scons: \*\*\* Error 1 scons: \*\*\* Error 2 -scons: \*\*\* nonexistent.in/\*\.\*: (The system cannot find the path specified|Das System kann den angegebenen Pfad nicht finden)""" +scons: \*\*\* nonexistent.in(/|\\)\*\.\*: (The system cannot find the path specified|Das System kann den angegebenen Pfad nicht finden)""" elif sys.platform == 'win32' and sys.version_info[0] == 3: expect = r"""scons: \*\*\* Error 1 scons: \*\*\* Error 2 -- cgit v0.12 From 92830df278cc22505fb1e7984e422dda35ce3503 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 23 Sep 2018 13:03:29 -0700 Subject: Add centos specific likely qt path to moc as it's not in the default PATH defined by SCons --- src/engine/SCons/Tool/qt.py | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/src/engine/SCons/Tool/qt.py b/src/engine/SCons/Tool/qt.py index 77269a8..535f3d7 100644 --- a/src/engine/SCons/Tool/qt.py +++ b/src/engine/SCons/Tool/qt.py @@ -37,6 +37,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import os.path import re +import glob import SCons.Action import SCons.Builder @@ -44,6 +45,8 @@ import SCons.Defaults import SCons.Scanner import SCons.Tool import SCons.Util +import SCons.Tool.cxx +cplusplus = SCons.Tool.cxx class ToolQtWarning(SCons.Warnings.Warning): pass @@ -60,12 +63,33 @@ header_extensions = [".h", ".hxx", ".hpp", ".hh"] if SCons.Util.case_sensitive_suffixes('.h', '.H'): header_extensions.append('.H') -import SCons.Tool.cxx -cplusplus = SCons.Tool.cxx -#cplusplus = __import__('cxx', globals(), locals(), []) - cxx_suffixes = cplusplus.CXXSuffixes + +# +def find_platform_specific_qt_paths(): + """ + If the platform has non-standard paths which it installs QT in,return the likely default path + :return: + """ + + # qt_bin_dirs = [] + qt_bin_dir = None + if os.path.isfile('/etc/redhat-release'): + with open('/etc/redhat-release','r') as rr: + lines = rr.readlines() + distro = lines[0].split()[0] + if distro == 'CentOS': + # Centos installs QT under /usr/{lib,lib64}/qt{4,5,-3.3}/bin + # so we need to handle this differently + # qt_bin_dirs = glob.glob('/usr/lib64/qt*/bin') + qt_bin_dir = '/usr/lib64/qt-3.3/bin' + + return qt_bin_dir + + +QT_BIN_DIR = find_platform_specific_qt_paths() + def checkMocIncluded(target, source, env): moc = target[0] cpp = source[0] @@ -188,13 +212,13 @@ AutomocStatic = _Automoc('StaticObject') def _detect(env): """Not really safe, but fast method to detect the QT library""" - QTDIR = None - if not QTDIR: - QTDIR = env.get('QTDIR',None) + + QTDIR = env.get('QTDIR',None) if not QTDIR: QTDIR = os.environ.get('QTDIR',None) if not QTDIR: - moc = env.WhereIs('moc') + try_paths = os.pathsep.join([env['ENV']['PATH'],QT_BIN_DIR]) + moc = env.WhereIs('moc',try_paths) if moc: QTDIR = os.path.dirname(os.path.dirname(moc)) SCons.Warnings.warn( -- cgit v0.12 From 9189b30403f5de0566902a46278f592498ac1eea Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 23 Sep 2018 14:10:05 -0700 Subject: Handle when we're not on CentOS --- src/engine/SCons/Tool/qt.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/engine/SCons/Tool/qt.py b/src/engine/SCons/Tool/qt.py index 535f3d7..b8cf77a 100644 --- a/src/engine/SCons/Tool/qt.py +++ b/src/engine/SCons/Tool/qt.py @@ -217,8 +217,7 @@ def _detect(env): if not QTDIR: QTDIR = os.environ.get('QTDIR',None) if not QTDIR: - try_paths = os.pathsep.join([env['ENV']['PATH'],QT_BIN_DIR]) - moc = env.WhereIs('moc',try_paths) + moc = env.WhereIs('moc') or env.WhereIs('moc',QT_BIN_DIR) if moc: QTDIR = os.path.dirname(os.path.dirname(moc)) SCons.Warnings.warn( -- cgit v0.12 From 5f09733499c7d1c8cb559a02ea1e6e4fef3825ea Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 23 Sep 2018 15:41:42 -0700 Subject: Add info to CHANGES.txt --- src/CHANGES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index a155da7..2e7c998 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -46,6 +46,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - Fix GH Issue #3141 unicode string in a TryAction() with python 2.7 crashes. - Fixed issue causing stack trace when python Action function contains a unicode string when being run with Python 2.7 + - Add alternate path to QT install for Centos in qt tool: /usr/lib64/qt-3.3/bin From Andrew Featherstone - Removed unused --warn options from the man page and source code. -- cgit v0.12 From 9d8ca555d5e95fde61fc3f43fa0fde632e487279 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 23 Sep 2018 19:28:49 -0400 Subject: Fix Github issue #2580 - hash mark not properly handled in FRAMEWORKPATH --- src/CHANGES.txt | 2 ++ src/engine/SCons/Tool/applelink.py | 3 +- test/LINK/applelink.py | 70 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 test/LINK/applelink.py diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 12df163..1461d62 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -47,6 +47,8 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - Fixed issue causing stack trace when python Action function contains a unicode string when being run with Python 2.7 - Add alternate path to QT install for Centos in qt tool: /usr/lib64/qt-3.3/bin + - Fix GH Issue #2580 - # in FRAMEWORKPATH doesn't get properly expanded. The # is left in the + command line. From Andrew Featherstone - Removed unused --warn options from the man page and source code. diff --git a/src/engine/SCons/Tool/applelink.py b/src/engine/SCons/Tool/applelink.py index c5f4376..65b569a 100644 --- a/src/engine/SCons/Tool/applelink.py +++ b/src/engine/SCons/Tool/applelink.py @@ -45,7 +45,8 @@ def generate(env): link.generate(env) env['FRAMEWORKPATHPREFIX'] = '-F' - env['_FRAMEWORKPATH'] = '${_concat(FRAMEWORKPATHPREFIX, FRAMEWORKPATH, "", __env__)}' + env['_FRAMEWORKPATH'] = '${_concat(FRAMEWORKPATHPREFIX, FRAMEWORKPATH, "", __env__, RDirs)}' + env['_FRAMEWORKS'] = '${_concat("-framework ", FRAMEWORKS, "", __env__)}' env['LINKCOM'] = env['LINKCOM'] + ' $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -dynamiclib') diff --git a/test/LINK/applelink.py b/test/LINK/applelink.py new file mode 100644 index 0000000..0d05d65 --- /dev/null +++ b/test/LINK/applelink.py @@ -0,0 +1,70 @@ +#!/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__" + +import os + +import TestSCons + +_python_ = TestSCons._python_ +_exe = TestSCons._exe + +test = TestSCons.TestSCons() + + +test.write('foo.c', r""" +#include +#include +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + printf("foo.c\n"); + exit (0); +} +""") + +# Test issue # 2580 +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment() + +env.Object( + target = '#foo.o', + source = ['foo.c'], + FRAMEWORKS = ['Ogre'], + FRAMEWORKPATH = ['#frameworks'] +) +""" % locals()) + +test.run(arguments='-Q', stdout='gcc -o foo.o -c -Fframeworks foo.c\n') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: -- cgit v0.12 From 6da2e3926185a44c20bbd3be3564b332e98afd65 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 23 Sep 2018 21:00:54 -0400 Subject: fix test so it should run on any platform --- src/engine/SCons/Defaults.py | 3 +++ src/engine/SCons/Tool/applelink.py | 3 +-- test/LINK/applelink.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py index 69d5c94..87e831d 100644 --- a/src/engine/SCons/Defaults.py +++ b/src/engine/SCons/Defaults.py @@ -342,6 +342,7 @@ Touch = ActionFactory(touch_func, # Internal utility functions + def _concat(prefix, list, suffix, env, f=lambda x: x, target=None, source=None): """ Creates a new list from 'list' by first interpolating each element @@ -358,6 +359,7 @@ def _concat(prefix, list, suffix, env, f=lambda x: x, target=None, source=None): return _concat_ixes(prefix, list, suffix, env) + def _concat_ixes(prefix, list, suffix, env): """ Creates a new list from 'list' by concatenating the 'prefix' and @@ -395,6 +397,7 @@ def _concat_ixes(prefix, list, suffix, env): return result + def _stripixes(prefix, itms, suffix, stripprefixes, stripsuffixes, env, c=None): """ This is a wrapper around _concat()/_concat_ixes() that checks for diff --git a/src/engine/SCons/Tool/applelink.py b/src/engine/SCons/Tool/applelink.py index 65b569a..5a06f9c 100644 --- a/src/engine/SCons/Tool/applelink.py +++ b/src/engine/SCons/Tool/applelink.py @@ -39,6 +39,7 @@ import SCons.Util # the -rpath option, so we use the "link" tool instead of "gnulink". from . import link + def generate(env): """Add Builders and construction variables for applelink to an Environment.""" @@ -68,8 +69,6 @@ def generate(env): env['LDMODULEFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -bundle') env['LDMODULECOM'] = '$LDMODULE -o ${TARGET} $LDMODULEFLAGS $SOURCES $_LIBDIRFLAGS $_LIBFLAGS $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' - - def exists(env): return env['PLATFORM'] == 'darwin' diff --git a/test/LINK/applelink.py b/test/LINK/applelink.py index 0d05d65..b13b36f 100644 --- a/test/LINK/applelink.py +++ b/test/LINK/applelink.py @@ -49,7 +49,7 @@ main(int argc, char *argv[]) # Test issue # 2580 test.write('SConstruct', """ DefaultEnvironment(tools=[]) -env = Environment() +env = Environment(PLATFORM='darwin') env.Object( target = '#foo.o', -- cgit v0.12