diff options
author | William Deegan <bill@baddogconsulting.com> | 2020-01-30 23:03:35 (GMT) |
---|---|---|
committer | William Deegan <bill@baddogconsulting.com> | 2020-01-30 23:03:35 (GMT) |
commit | ff18a25e814dac5e9140c625b84d048962226244 (patch) | |
tree | db78d68aa71babf803ad7e6c70ac4f99f3dfcd41 | |
parent | 0d4c31c7adc62f215e6a73d2efa7c2787bee6c83 (diff) | |
parent | 1e71abf4c79e74adff68eea8eb9b40bfbf1260c3 (diff) | |
download | SCons-ff18a25e814dac5e9140c625b84d048962226244.zip SCons-ff18a25e814dac5e9140c625b84d048962226244.tar.gz SCons-ff18a25e814dac5e9140c625b84d048962226244.tar.bz2 |
Merge branch 'issue-3469' of github.com:bdbaddog/scons into issue-3469
136 files changed, 1585 insertions, 888 deletions
diff --git a/.travis.yml b/.travis.yml index 985a544..b941f4c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,13 +19,6 @@ install: # do the rest of the image setup - ./.travis/install.sh -# pypy is not passing atm, but still report build success for now -# allow coverage to fail, so we can still do testing for all platforms -matrix: - allow_failures: - - python: pypy3 - - stage: Coverage - # run coverage first as its still useful to collect stages: - Coverage @@ -43,6 +36,12 @@ stages: # apparently not guaranteed. jobs: + + # pypy is not passing atm, but still report build success for now + # allow coverage to fail, so we can still do testing for all platforms + allow_failures: + - python: pypy3 + - stage: Coverage include: - &test_job stage: Test @@ -53,29 +52,24 @@ jobs: env: - PYVER=pypy3 - PYTHON=pypy3 - sudo: required - - <<: *test_job python: 3.5 env: - PYVER=35 - PYTHON=3.5 - sudo: required - <<: *test_job python: 3.6 env: - PYVER=36 - PYTHON=3.6 - sudo: required - <<: *test_job python: 3.7 env: - PYVER=37 - PYTHON=3.7 - sudo: required dist: xenial # required for Python >= 3.7 - <<: *test_job @@ -83,7 +77,6 @@ jobs: env: - PYVER=38 - PYTHON=3.8 - sudo: required dist: bionic # required for Python >= 3.8 diff --git a/doc/design/engine.xml b/doc/design/engine.xml index 39289f9..6b8b2a6 100644 --- a/doc/design/engine.xml +++ b/doc/design/engine.xml @@ -1104,13 +1104,13 @@ you set it up with another environment... </para> <programlisting> - env.Dependency(target = 'output1', dependency = 'input_1 input_2') - env.Dependency(target = 'output2', dependency = ['input_1', 'input_2']) - env.Dependency(target = 'output3', dependency = ['white space input']) + env.Depends(target = 'output1', dependency = 'input_1 input_2') + env.Depends(target = 'output2', dependency = ['input_1', 'input_2']) + env.Depends(target = 'output3', dependency = ['white space input']) - env.Dependency(target = 'output_a output_b', dependency = 'input_3') - env.Dependency(target = ['output_c', 'output_d'], dependency = 'input_4') - env.Dependency(target = ['white space output'], dependency = 'input_5') + env.Depends(target = 'output_a output_b', dependency = 'input_3') + env.Depends(target = ['output_c', 'output_d'], dependency = 'input_4') + env.Depends(target = ['white space output'], dependency = 'input_5') </programlisting> <para> @@ -1129,7 +1129,7 @@ you set it up with another environment... </para> <programlisting> - env.Dependency(target = 'archive.tar.gz', dependency = 'SConstruct') + env.Depends(target = 'archive.tar.gz', dependency = 'SConstruct') </programlisting> </section> diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen index 9050832..21e0b1f 100644 --- a/doc/generated/variables.gen +++ b/doc/generated/variables.gen @@ -3448,17 +3448,6 @@ If this is not set, then <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend=" </para> </listitem> </varlistentry> - <varlistentry id="cv-LDMODULEEMITTER"> - <term>LDMODULEEMITTER</term> - <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -Contains the emitter specification for the -<link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="b-LoadableModule"><function>LoadableModule</function></link> builder. -The manpage section "Builder Objects" contains -general information on specifying emitters. -</para> -</listitem> - </varlistentry> <varlistentry id="cv-LDMODULEFLAGS"> <term>LDMODULEFLAGS</term> <listitem> diff --git a/doc/generated/variables.mod b/doc/generated/variables.mod index ba92aa9..ff44a23 100644 --- a/doc/generated/variables.mod +++ b/doc/generated/variables.mod @@ -65,6 +65,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. <!ENTITY cv-CXXVERSION "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$CXXVERSION</envar>"> <!ENTITY cv-DC "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$DC</envar>"> <!ENTITY cv-DCOM "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$DCOM</envar>"> +<!ENTITY cv-DCOMSTR "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$DCOMSTR</envar>"> <!ENTITY cv-DDEBUG "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$DDEBUG</envar>"> <!ENTITY cv-DDEBUGPREFIX "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$DDEBUGPREFIX</envar>"> <!ENTITY cv-DDEBUGSUFFIX "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$DDEBUGSUFFIX</envar>"> @@ -457,6 +458,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. <!ENTITY cv-SHCXXFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHCXXFLAGS</envar>"> <!ENTITY cv-SHDC "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHDC</envar>"> <!ENTITY cv-SHDCOM "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHDCOM</envar>"> +<!ENTITY cv-SHDCOMSTR "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHDCOMSTR</envar>"> <!ENTITY cv-SHDLIBVERSION "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHDLIBVERSION</envar>"> <!ENTITY cv-SHDLIBVERSIONFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHDLIBVERSIONFLAGS</envar>"> <!ENTITY cv-SHDLINK "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHDLINK</envar>"> @@ -706,6 +708,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. <!ENTITY cv-link-CXXVERSION "<link linkend='cv-CXXVERSION' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$CXXVERSION</envar></link>"> <!ENTITY cv-link-DC "<link linkend='cv-DC' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$DC</envar></link>"> <!ENTITY cv-link-DCOM "<link linkend='cv-DCOM' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$DCOM</envar></link>"> +<!ENTITY cv-link-DCOMSTR "<link linkend='cv-DCOMSTR' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$DCOMSTR</envar></link>"> <!ENTITY cv-link-DDEBUG "<link linkend='cv-DDEBUG' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$DDEBUG</envar></link>"> <!ENTITY cv-link-DDEBUGPREFIX "<link linkend='cv-DDEBUGPREFIX' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$DDEBUGPREFIX</envar></link>"> <!ENTITY cv-link-DDEBUGSUFFIX "<link linkend='cv-DDEBUGSUFFIX' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$DDEBUGSUFFIX</envar></link>"> @@ -1098,6 +1101,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. <!ENTITY cv-link-SHCXXFLAGS "<link linkend='cv-SHCXXFLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHCXXFLAGS</envar></link>"> <!ENTITY cv-link-SHDC "<link linkend='cv-SHDC' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHDC</envar></link>"> <!ENTITY cv-link-SHDCOM "<link linkend='cv-SHDCOM' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHDCOM</envar></link>"> +<!ENTITY cv-link-SHDCOMSTR "<link linkend='cv-SHDCOMSTR' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHDCOMSTR</envar></link>"> <!ENTITY cv-link-SHDLIBVERSION "<link linkend='cv-SHDLIBVERSION' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHDLIBVERSION</envar></link>"> <!ENTITY cv-link-SHDLIBVERSIONFLAGS "<link linkend='cv-SHDLIBVERSIONFLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHDLIBVERSIONFLAGS</envar></link>"> <!ENTITY cv-link-SHDLINK "<link linkend='cv-SHDLINK' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHDLINK</envar></link>"> diff --git a/doc/man/scons.xml b/doc/man/scons.xml index bd8d5f0..9dccf11 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -93,88 +93,88 @@ </refsynopsisdiv> -<refsect1 id='description'><title>DESCRIPTION</title> -<para>The +<refsect1 id='description'> +<title>DESCRIPTION</title> +<para> <command>scons</command> -utility builds software (or other files) by determining which -component pieces must be rebuilt and executing the necessary commands to -rebuild them.</para> +orchestrates the construction of software +(and other tangible products such as documentation files) +by determining which +component pieces must be built or rebuilt and invoking the necessary +commands to build them.</para> + +<para>You instruct +<command>scons</command> by writing a configuration file +which specifies the files to be built (<firstterm>targets</firstterm>), +and, if necessary, the rules to build those files. Premade +rules exist for building many common software components +(such as executable programs, object files, libraries), +so that for most software projects, +only the target and input files need be specified.</para> -<para>By default, -<command>scons</command> +<para> +When invoked, <command>scons</command> searches for a file named -<emphasis>SConstruct</emphasis>, -<emphasis>Sconstruct</emphasis>, -<emphasis>sconstruct</emphasis>, -<emphasis>SConstruct.py</emphasis> -<emphasis>Sconstruct.py</emphasis> -or -<emphasis>sconstruct.py</emphasis> -(in that order) in the current directory and reads its -configuration from the first file found. +&SConstruct; +(going on to check alternative names +&Sconstruct;, &sconstruct;, &SConstruct.py; &Sconstruct.py; +and &sconstruct.py; +in that order) in the current directory and reads its +configuration from that file. An alternate file name may be specified via the <option>-f</option> -option.</para> - -<para>The -<emphasis>SConstruct</emphasis> +option. +The &SConstruct; file can specify subsidiary configuration files using the -<emphasis role="bold">SConscript</emphasis>() -function. +&SConscriptFunc; function. By convention, these subsidiary files are named -<emphasis>SConscript</emphasis>, +&SConscript;, although any name may be used. -(Because of this naming convention, -the term "SConscript files" -is sometimes used to refer -generically to all -<command>scons</command> -configuration files, -regardless of actual file name.)</para> - -<para>The configuration files -specify the target files to be built, and -(optionally) the rules to build those targets. Reasonable default -rules exist for building common software components (executable -programs, object files, libraries), so that for most software -projects, only the target and input files need be specified.</para> - -<para>Before reading the -<emphasis>SConstruct</emphasis> -file, +As a result of this naming convention, +the term <firstterm>SConscript files</firstterm> +is often used to refer +generically to the complete set of +configuration files for a project, +including the &SConstruct; file, +regardless of the actual file names or number of files.</para> + +<para>Before reading the SConscript files, <command>scons</command> looks for a directory named -<emphasis>site_scons</emphasis> -in various system directories (see below) and the directory containing the -<emphasis>SConstruct</emphasis> -file; for each of those dirs which exists, -<emphasis>site_scons</emphasis> -is prepended to sys.path, -the file -<emphasis>site_scons/site_init.py</emphasis>, -is evaluated if it exists, -and the directory -<emphasis>site_scons/site_tools</emphasis> -is prepended to the default toolpath if it exists. +<filename>site_scons</filename> +in various system directories and in the directory containing the +&SConstruct; file and prepends the ones it +finds to the Python module search path (<varname>sys.path</varname>), +thus allowing modules in such directories to be imported in +the normal Python way in SConscript files. +For each found site directory, +if it contains a file <filename>site_init.py</filename> +it is evaluated, and if it contains a directory +<filename>site_tools</filename> the path to it +is prepended to the default toolpath. See the -<option>--no-site-dir</option> -and <option>--site-dir</option> -options for more details.</para> +and +<option>--no-site-dir</option> +options for details on default paths and +controlling the site directories. +</para> <para><command>scons</command> reads and executes the SConscript files as Python scripts, so you may use normal Python scripting capabilities (such as flow control, data manipulation, and imported Python libraries) -to handle complicated build situations. -<command>scons</command>, -however, reads and executes all of the SConscript files +to handle complicated build situations.</para> + +<para> +<command>scons</command> +reads and executes all of the SConscript files <emphasis>before</emphasis> it begins building any targets. -To make this obvious, +To make this clear, <command>scons</command> prints the following messages about what it is doing:</para> @@ -189,7 +189,7 @@ $ </literallayout> <para>The status messages -(everything except the line that reads "cp foo.in foo.out") +(lines beginning with the <literal>scons:</literal> tag) may be suppressed using the <option>-Q</option> option.</para> @@ -209,34 +209,28 @@ that you want to use to build your target files are not in standard system locations, <command>scons</command> will not find them unless -you explicitly set the <envar>PATH</envar> +you explicitly set the <command>scons</command> +<envar>PATH</envar> in the internal environment to include those locations. -Whenever you create an -<command>scons</command> -construction environment, +Whenever you create a &consenv;, you can propagate the value of <envar>PATH</envar> from your external environment as follows:</para> <literallayout class="monospaced"> import os -env = Environment(ENV = {'PATH' : os.environ['PATH']}) +env = Environment(ENV={'PATH': os.environ['PATH']}) </literallayout> -<para>Similarly, if the commands use external environment variables -like -<envar>PATH</envar>, -<envar>HOME</envar>, -<envar>JAVA_HOME</envar>, -<envar>LANG</envar>, -<envar>SHELL</envar>, -<envar>TERM</envar>, -etc., -these variables can also be explicitly propagated:</para> +<para>Similarly, if the commands use specific +external environment variables that <command>scons</command> +does not recognize, they can be propagated into +the internal environment:</para> <literallayout class="monospaced"> import os -env = Environment(ENV = {'PATH': os.environ['PATH'], - 'HOME': os.environ['HOME']}) +env = Environment(ENV={'PATH': os.environ['PATH'], + 'ANDROID_HOME': os.environ['ANDROID_HOME'], + 'ANDROID_NDK_HOME': os.environ['ANDROID_NDK_HOME']}) </literallayout> <para>Or you may explicitly propagate the invoking user's @@ -244,17 +238,23 @@ complete external environment:</para> <literallayout class="monospaced"> import os -env = Environment(ENV = os.environ) +env = Environment(ENV=os.environ) </literallayout> <para>This comes at the expense of making your build dependent on the user's environment being set correctly, -but it may be more convenient for many configurations.</para> +but it may be more convenient for many configurations. +It should not cause problems if done in a build setup which tightly +controls how the environment is set up before invoking +<command>scons</command>, as in many continuous +integration setups. +</para> <para><command>scons</command> can scan known input files automatically for dependency -information (for example, #include statements -in C or C++ files) and will rebuild dependent files appropriately +information (for example, <literal>#include</literal> +preprocessor directives in C or C++ files) +and will rebuild dependent files appropriately whenever any "included" input file changes. <command>scons</command> supports the @@ -266,41 +266,46 @@ SCCS or RCS subdirectories using SCCS, RCS or BitKeeper.</para> <para><command>scons</command> -is normally executed in a top-level directory containing a -<emphasis>SConstruct</emphasis> -file, optionally specifying -as command-line arguments -the target file or files to be built.</para> - -<para>By default, the command</para> - -<literallayout class="monospaced"> -scons -</literallayout> +is normally executed in a top-level directory containing an +&SConstruct; file. +When <command>scons</command> is invoked, +the command line (including the contents +of the &SCONSFLAGS; environment variable, +if set) is processed. +The options are consumed (see <xref linkend="options"></xref>; +and also note that SConscript files may define additional options +for the project using the &AddOption; function). +Any variable argument assignments (see below) are also consumed. +Any remaining arguments are taken as the targets to build. +Targets on the command line may be files, directories, +or phony targets defined using the &Alias; function. +The command line targets are available in the +&COMMAND_LINE_TARGETS; list. +</para> -<para>will build all target files in or below the current directory. -Explicit default targets -(to be built when no targets are specified on the command line) -may be defined in the SConscript file(s) -using the -<emphasis role="bold">Default()</emphasis> -function, described below.</para> +<para>If no targets are specified on the command line, +<command>scons</command> +will build the default targets. The default targets +are those specified in the SConscript files via calls +to the &Default; function; if none, the default targets are +those target files in or below the current directory. +Targets specified via the &Default; function are available +in the &DEFAULT_TARGETS; list. +</para> -<para>Even when -<emphasis role="bold">Default()</emphasis> -targets are specified in the SConscript file(s), -all target files in or below the current directory -may be built by explicitly specifying -the current directory (.) +<para>To ignore the default targets specified +through calls to &Default; and instead build all +target files in or below the current directory +specify the current directory (<literal>.</literal>) as a command-line target:</para> <literallayout class="monospaced"> scons . </literallayout> -<para>Building all target files, +<para>To build all target files, including any files outside of the current directory, -may be specified by supplying a command-line target +supply a command-line target of the root directory (on POSIX systems):</para> <literallayout class="monospaced"> @@ -314,15 +319,33 @@ should be built (on Windows systems):</para> scons C:\ D:\ </literallayout> -<para>To build only specific targets, -supply them as command-line arguments:</para> +<para>A subset of a hierarchical tree may be built by +remaining at the top-level directory (where the +&SConstruct; +file lives) and specifying the subdirectory as the target to +build:</para> <literallayout class="monospaced"> -scons foo bar +scons src/subdir </literallayout> -<para>in which case only the specified targets will be built -(along with any derived files on which they depend).</para> +<para>or by changing directory and invoking scons with the +<option>-u</option> +option, which traverses up the directory +hierarchy until it finds the +&SConstruct; +file, and then builds +targets relatively to the current subdirectory (see +also the related <option>-D</option> and <option>-U</option> options):</para> + +<literallayout class="monospaced"> +cd src/subdir +scons -u . +</literallayout> + +<para>In all cases, more files may be built than are +requested, as <command>scons</command> needs to make +sure any dependent files are built.</para> <para>Specifying "cleanup" targets in SConscript files is not usually necessary. The @@ -334,45 +357,22 @@ necessary to build the specified target:</para> scons -c . </literallayout> -<para>to remove all target files, or:</para> +<para>to remove all target files in or under the current directory, or:</para> <literallayout class="monospaced"> scons -c build export </literallayout> -<para>to remove target files under build and export. +<para>to remove target files under <filename>build</filename> +and <filename>export</filename>.</para> + +<para> Additional files or directories to remove can be specified using the -<emphasis role="bold">Clean()</emphasis> -function. +&Clean; function in the SConscript files. Conversely, targets that would normally be removed by the <option>-c</option> -invocation -can be prevented from being removed by using the -<emphasis role="bold">NoClean</emphasis>() -function.</para> - -<para>A subset of a hierarchical tree may be built by -remaining at the top-level directory (where the -<emphasis>SConstruct</emphasis> -file lives) and specifying the subdirectory as the target to be -built:</para> - -<literallayout class="monospaced"> -scons src/subdir -</literallayout> - -<para>or by changing directory and invoking scons with the -<option>-u</option> -option, which traverses up the directory -hierarchy until it finds the -<emphasis>SConstruct</emphasis> -file, and then builds -targets relatively to the current subdirectory:</para> - -<literallayout class="monospaced"> -cd src/subdir -scons -u . -</literallayout> +invocation can be retained by calling the +&NoClean; function with those targets.</para> <para><command>scons</command> supports building multiple targets in parallel via a @@ -405,16 +405,16 @@ command-line options. The option is useful to prevent multiple builds from trying to update the cache simultaneously.</para> -<para>Values of variables to be passed to the SConscript file(s) +<para>Values of variables to be passed to the SConscript files may be specified on the command line:</para> <literallayout class="monospaced"> scons debug=1 . </literallayout> -<para>These variables are available in SConscript files -through the ARGUMENTS dictionary, -and can be used in the SConscript file(s) to modify +<para>These variables are available +through the &ARGUMENTS; dictionary, +and can be used in the SConscript files to modify the build in any way:</para> <literallayout class="monospaced"> @@ -425,13 +425,12 @@ else: </literallayout> <para>The command-line variable arguments are also available -in the ARGLIST list, +in the &ARGLIST; list, indexed by their order on the command line. This allows you to process them in order rather than by name, -if necessary. -ARGLIST[0] returns a tuple -containing (argname, argvalue). -A Python exception is thrown if you +if necessary. Each &ARGLIST; entry is a tuple +containing (<replaceable>argname</replaceable>, <replaceable>argvalue</replaceable>). +A Python <literal>IndexError</literal> exception is raised if you try to access a list member that does not exist.</para> @@ -443,24 +442,28 @@ There should be no other dependencies or requirements to run <!-- The following paragraph reflects the default tool search orders --> <!-- currently in SCons/Tool/__init__.py. If any of those search orders --> <!-- change, this documentation should change, too. --> + <para>By default, <command>scons</command> -knows how to search for available programming tools -on various systems. -On Windows systems, +searches for known programming tools +on various systems and initializes itself based on what is found. +On Windows systems which identify as <emphasis>win32</emphasis>, <command>scons</command> searches in order for the Microsoft Visual C++ tools, the MinGW tool chain, the Intel compiler tools, and the PharLap ETS compiler. +On Windows system which identify as <emphasis>cygwin</emphasis> +(that is, if <command>scons</command> is invoked from a cygwin shell), +the order changes to prefer the GCC toolchain over the MSVC tools. On OS/2 systems, <command>scons</command> searches in order for the OS/2 compiler, the GCC tool chain, and the Microsoft Visual C++ tools, -On SGI IRIX, IBM AIX, Hewlett Packard HP-UX, and Sun Solaris systems, +On SGI IRIX, IBM AIX, Hewlett Packard HP-UX, and Oracle Solaris systems, <command>scons</command> searches for the native compiler tools (MIPSpro, Visual Age, aCC, and Forte tools respectively) @@ -470,7 +473,6 @@ including POSIX (Linux and UNIX) platforms, <command>scons</command> searches in order for the GCC tool chain, -the Microsoft Visual C++ tools, and the Intel compiler tools. You may, of course, override these default values by appropriate configuration of @@ -4383,20 +4385,17 @@ vars.AddVariables( </refsect2> -<refsect2 id='file_and_directory_nodes'><title>File and Directory Nodes</title> +<refsect2 id='file_and_directory_nodes'> +<title>File and Directory Nodes</title> -<para>The -<emphasis>File</emphasis>() -and -<emphasis>Dir</emphasis>() -functions return -<emphasis>File</emphasis> -and -<emphasis>Dir</emphasis> -Nodes, respectively. -Python objects, respectively. -Those objects have several user-visible attributes -and methods that are often useful:</para> +<para> +The &f-link-File; and &f-link-Dir; +functions/methods return +File and Directory Nodes, respectively. +Such nodes are Python objects with +several user-visible attributes +and methods that are often useful to access +in SConscript files:</para> <variablelist> <varlistentry> @@ -4412,33 +4411,31 @@ file is found). The build path is the same as the source path if <emphasis>variant_dir</emphasis> is not being used.</para> - </listitem> </varlistentry> + <varlistentry> <term>abspath</term> <listitem> <para>The absolute build path of the given file or directory.</para> - </listitem> </varlistentry> + <varlistentry> <term>srcnode()</term> <listitem> <para>The <emphasis>srcnode</emphasis>() method -returns another -<emphasis>File</emphasis> -or -<emphasis>Dir</emphasis> -object representing the -<emphasis>source</emphasis> -path of the given -<emphasis>File</emphasis> -or -<emphasis>Dir</emphasis>. -The</para> +returns another File or Directory Node +representing the source path of the given +File or Directory Node. +</para> + </listitem> + </varlistentry> +</variablelist> + +<para>For example:</para> <literallayout class="monospaced"> # Get the current build dir's path, relative to top. @@ -4451,98 +4448,90 @@ File('foo.c').srcnode().path # source path of the given source file. # Builders also return File objects: foo = env.Program('foo.c') -print("foo will be built in %s"%foo.path) +print("foo will be built in", foo.path) </literallayout> -<para>A -<emphasis>Dir</emphasis> -Node or -<emphasis>File</emphasis> -Node can also be used to create -file and subdirectory Nodes relative to the generating Node. -A -<emphasis>Dir</emphasis> -Node will place the new Nodes within the directory it represents. -A -<emphasis>File</emphasis> -node will place the new Nodes within its parent directory -(that is, "beside" the file in question). -If -<emphasis>d</emphasis> -is a -<emphasis>Dir</emphasis> -(directory) Node and -<emphasis>f</emphasis> -is a -<emphasis>File</emphasis> -(file) Node, -then these methods are available:</para> +<para> +File and Directory Node objects have methods to create +File and Directory Nodes relative to the original Node. +</para> + +<para> +If the object is a Directory Node, +these methods will place the the new Node within the directory +the Node represents: +</para> - </listitem> - </varlistentry> -</variablelist> <variablelist> <varlistentry> - <term><emphasis>d</emphasis>.Dir(<emphasis>name</emphasis>)</term> + <term><replaceable>d</replaceable>.Dir(<replaceable>name</replaceable>)</term> <listitem> <para>Returns a directory Node for a subdirectory of -<emphasis>d</emphasis> +<replaceable>d</replaceable> named -<emphasis>name</emphasis>.</para> - +<replaceable>name</replaceable>.</para> </listitem> </varlistentry> + <varlistentry> - <term><emphasis>d</emphasis>.File(<emphasis>name</emphasis>)</term> + <term><replaceable>d</replaceable>.File(<replaceable>name</replaceable>)</term> <listitem> <para>Returns a file Node for a file within -<emphasis>d</emphasis> +<replaceable>d</replaceable> named -<emphasis>name</emphasis>.</para> - +<replaceable>name</replaceable>.</para> </listitem> </varlistentry> + <varlistentry> - <term><emphasis>d</emphasis>.Entry(<emphasis>name</emphasis>)</term> + <term><replaceable>d</replaceable>.Entry(<replaceable>name</replaceable>)</term> <listitem> <para>Returns an unresolved Node within -<emphasis>d</emphasis> +<replaceable>d</replaceable> named -<emphasis>name</emphasis>.</para> - +<replaceable>name</replaceable>.</para> </listitem> </varlistentry> +</variablelist> + +<para> +If the object is a File Node, +these methods will place the the new Node in the same +directory as the one the Node represents: +</para> + +<variablelist> <varlistentry> - <term><emphasis>f</emphasis>.Dir(<emphasis>name</emphasis>)</term> + <term><replaceable>d</replaceable>.Dir(<replaceable>name</replaceable>)</term> <listitem> <para>Returns a directory named -<emphasis>name</emphasis> +<replaceable>name</replaceable> within the parent directory of -<emphasis>f</emphasis>.</para> - +<replaceable>f</replaceable>.</para> </listitem> </varlistentry> + <varlistentry> - <term><emphasis>f</emphasis>.File(<emphasis>name</emphasis>)</term> + <term><replaceable>d</replaceable>.File(<replaceable>name</replaceable>)</term> <listitem> <para>Returns a file named -<emphasis>name</emphasis> +<replaceable>name</replaceable> within the parent directory of -<emphasis>f</emphasis>.</para> - +<replaceable>f</replaceable>.</para> </listitem> </varlistentry> + <varlistentry> - <term><emphasis>f</emphasis>.Entry(<emphasis>name</emphasis>)</term> + <term><replaceable>d</replaceable>.Entry(<replaceable>name</replaceable>)</term> <listitem> <para>Returns an unresolved Node named -<emphasis>name</emphasis> +<replaceable>name</replaceable> within the parent directory of -<emphasis>f</emphasis>.</para> - +<replaceable>f</replaceable>.</para> </listitem> </varlistentry> </variablelist> + <para>For example:</para> <literallayout class="monospaced"> @@ -5923,7 +5912,7 @@ action='$CC -c -o $TARGET $SOURCES' cc -c -o foo foo.c bar.c </literallayout> -<para>Variable names may be surrounded by curly braces +<para>Variable names may be surrounded by curly braces <emphasis role="bold">{ }</emphasis> to separate the name from surrounding characters which are not part of the name. @@ -7200,7 +7189,7 @@ in addition to those passed on the command line.</para> <listitem> <para>(Windows only). If set, save the shell environment variables generated when setting up the Microsoft Visual C++ compiler -(and/or Build Tools) to a file to give these settings, +(and/or Build Tools) to a file to give these settings, which are expensive to generate, persistence across &scons; invocations. Use of this option is primarily intended to aid performance diff --git a/doc/scons.mod b/doc/scons.mod index 3e843a0..024afab 100644 --- a/doc/scons.mod +++ b/doc/scons.mod @@ -225,6 +225,7 @@ <!ENTITY NoCache "<function xmlns='http://www.scons.org/dbxsd/v1.0'>NoCache</function>"> <!ENTITY Objects "<function xmlns='http://www.scons.org/dbxsd/v1.0'>Objects</function>"> <!ENTITY Options "<function xmlns='http://www.scons.org/dbxsd/v1.0'>Options</function>"> +<!ENTITY SConscriptFunc "<function xmlns='http://www.scons.org/dbxsd/v1.0'>SConscript</function>"> <!ENTITY Variables "<function xmlns='http://www.scons.org/dbxsd/v1.0'>Variables</function>"> <!ENTITY PackageOption "<function xmlns='http://www.scons.org/dbxsd/v1.0'>PackageOption</function>"> <!ENTITY PackageVariable "<function xmlns='http://www.scons.org/dbxsd/v1.0'>PackageVariable</function>"> @@ -418,27 +419,27 @@ --> -<!ENTITY buildfunc "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>builder function</literal>"> -<!ENTITY build_action "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>build action</literal>"> -<!ENTITY build_actions "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>build actions</literal>"> -<!ENTITY builder_method "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>builder method</literal>"> +<!ENTITY buildfunc "<phrase xmlns='http://www.scons.org/dbxsd/v1.0'>builder function</phrase>"> +<!ENTITY build_action "<phrase xmlns='http://www.scons.org/dbxsd/v1.0'>build action</phrase>"> +<!ENTITY build_actions "<phrase xmlns='http://www.scons.org/dbxsd/v1.0'>build actions</phrase>"> +<!ENTITY builder_method "<phrase xmlns='http://www.scons.org/dbxsd/v1.0'>builder method</phrase>"> -<!ENTITY Configure_Contexts "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>Configure Contexts</literal>"> -<!ENTITY configure_context "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>configure context</literal>"> +<!ENTITY Configure_Contexts "<phrase xmlns='http://www.scons.org/dbxsd/v1.0'>Configure Contexts</phrase>"> +<!ENTITY configure_context "<phrase xmlns='http://www.scons.org/dbxsd/v1.0'>configure context</phrase>"> -<!ENTITY ConsEnv "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>Construction Environment</literal>"> -<!ENTITY ConsEnvs "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>Construction Environments</literal>"> -<!ENTITY Consenv "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>Construction environment</literal>"> -<!ENTITY Consenvs "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>Construction environments</literal>"> -<!ENTITY consenv "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>construction environment</literal>"> -<!ENTITY consenvs "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>construction environments</literal>"> +<!ENTITY ConsEnv "<phrase xmlns='http://www.scons.org/dbxsd/v1.0'>Construction Environment</phrase>"> +<!ENTITY ConsEnvs "<phrase xmlns='http://www.scons.org/dbxsd/v1.0'>Construction Environments</phrase>"> +<!ENTITY Consenv "<phrase xmlns='http://www.scons.org/dbxsd/v1.0'>Construction environment</phrase>"> +<!ENTITY Consenvs "<phrase xmlns='http://www.scons.org/dbxsd/v1.0'>Construction environments</phrase>"> +<!ENTITY consenv "<phrase xmlns='http://www.scons.org/dbxsd/v1.0'>construction environment</phrase>"> +<!ENTITY consenvs "<phrase xmlns='http://www.scons.org/dbxsd/v1.0'>construction environments</phrase>"> -<!ENTITY ConsVar "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>Construction Variable</literal>"> -<!ENTITY ConsVars "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>Construction Variables</literal>"> -<!ENTITY Consvar "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>Construction variable</literal>"> -<!ENTITY Consvars "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>Construction variables</literal>"> -<!ENTITY consvar "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>construction variable</literal>"> -<!ENTITY consvars "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>construction variables</literal>"> +<!ENTITY ConsVar "<phrase xmlns='http://www.scons.org/dbxsd/v1.0'>Construction Variable</phrase>"> +<!ENTITY ConsVars "<phrase xmlns='http://www.scons.org/dbxsd/v1.0'>Construction Variables</phrase>"> +<!ENTITY Consvar "<phrase xmlns='http://www.scons.org/dbxsd/v1.0'>Construction variable</phrase>"> +<!ENTITY Consvars "<phrase xmlns='http://www.scons.org/dbxsd/v1.0'>Construction variables</phrase>"> +<!ENTITY consvar "<phrase xmlns='http://www.scons.org/dbxsd/v1.0'>construction variable</phrase>"> +<!ENTITY consvars "<phrase xmlns='http://www.scons.org/dbxsd/v1.0'>construction variables</phrase>"> <!ENTITY CPPPATH "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>CPPPATH</literal>"> @@ -529,14 +530,14 @@ --> -<!ENTITY scons-announce "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>announce@scons.tigris.org</literal>"> -<!ENTITY scons-devel "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>scons-dev@scons.org</literal>"> -<!ENTITY scons-users "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>scons-users@scons.org</literal>"> +<!ENTITY scons-announce "<email xmlns='http://www.scons.org/dbxsd/v1.0'>announce@scons.tigris.org</email>"> +<!ENTITY scons-devel "<email xmlns='http://www.scons.org/dbxsd/v1.0'>scons-dev@scons.org</email>"> +<!ENTITY scons-users "<email xmlns='http://www.scons.org/dbxsd/v1.0'>scons-users@scons.org</email>"> <!-- Character entities - + --> <!ENTITY lambda "Λ"> diff --git a/doc/user/depends.xml b/doc/user/depends.xml index cd5094a..24bede6 100644 --- a/doc/user/depends.xml +++ b/doc/user/depends.xml @@ -2,7 +2,7 @@ <!DOCTYPE sconsdoc [ <!ENTITY % scons SYSTEM "../scons.mod"> %scons; - + <!ENTITY % builders-mod SYSTEM "../generated/builders.mod"> %builders-mod; <!ENTITY % functions-mod SYSTEM "../generated/functions.mod"> @@ -372,7 +372,7 @@ int main() { printf("Hello, world!\n"); } instead of <literal>timestamp-match</literal>, would be if you have some specific reason - to require this &Make;-like behavior of + to require this &Make;-like behavior of not rebuilding a target when an otherwise-modified source file is older. @@ -561,13 +561,6 @@ int main() { printf("Hello, world!\n"); } </para> - <para> - The fourth argument <varname>repo_node</varname>, - is the &Node; to use if it is not None when comparing &BuildInfo;. - This is typically only set when the target node only exists in a - &Repository; - </para> - <variablelist> <varlistentry> @@ -612,22 +605,33 @@ int main() { printf("Hello, world!\n"); } <para> - Note that ignoring some of the arguments - in your custom &Decider; function - is a perfectly normal thing to do, - if they don't impact the way you want to - decide if the dependency file has changed. + These attributes may not be present at the time of the + first run. Without any prior build, no targets have been + created and no <filename>.sconsign</filename> DB file exists yet. + So you should always check whether the + <varname>prev_ni</varname> attribute in question is available + (use the Python <function>hasattr</function> method or a + <literal>try</literal>-<literal>except</literal> block). + + </para> + + + <para> + + The fourth argument <varname>repo_node</varname> + is the &Node; to use if it is not None when comparing &BuildInfo;. + This is typically only set when the target node only exists in a + &Repository; </para> <para> - Another thing to look out for is the fact that the three - attributes above may not be present at the time of the first run. - Without any prior build, no targets have been created and no - <filename>.sconsign</filename> DB file exists yet. - So, you should always check whether the - <varname>prev_ni</varname> attribute in question is available. + Note that ignoring some of the arguments + in your custom &Decider; function + is a perfectly normal thing to do, + if they don't impact the way you want to + decide if the dependency file has changed. </para> @@ -644,13 +648,14 @@ int main() { printf("Hello, world!\n"); } <sconstruct> env = Environment() + def config_file_decider(dependency, target, prev_ni, repo_node=None): import os.path # We always have to init the .csig value... dep_csig = dependency.get_csig() # .csig may not exist, because no target was built yet... - if 'csig' not in dir(prev_ni): + if not prev_ni.hasattr("csig"): return True # Target file may not exist yet if not os.path.exists(str(target.abspath)): @@ -660,17 +665,18 @@ def config_file_decider(dependency, target, prev_ni, repo_node=None): return True return False + def update_file(): - f = open("test.txt","a") - f.write("some line\n") - f.close() + with open("test.txt", "a") as f: + f.write("some line\n") + update_file() # Activate our own decider function env.Decider(config_file_decider) -env.Install("install","test.txt") +env.Install("install", "test.txt") </sconstruct> </section> @@ -747,26 +753,6 @@ int main() { printf("Hello, world!\n"); } </section> <section> - <title>Older Functions for Deciding When an Input File Has Changed</title> - - <para> - - &SCons; still supports two functions that used to be the - primary methods for configuring the - decision about whether or not an input file has changed. - These functions have been officially deprecated - as &SCons; version 2.0, - and their use is discouraged, - mainly because they rely on a somewhat - confusing distinction between how - source files and target files are handled. - These functions are documented here mainly in case you - encounter them in older &SConscript; files. - - </para> - </section> - - <section> <title>Implicit Dependencies: The &cv-CPPPATH; Construction Variable</title> <para> @@ -1426,11 +1412,11 @@ Ignore(hello, '/usr/include/stdio.h') </programlisting> <para> - &Ignore; can also be used to prevent a generated file from being built - by default. This is due to the fact that directories depend on - their contents. So to ignore a generated file from the default build, + &Ignore; can also be used to prevent a generated file from being built + by default. This is due to the fact that directories depend on + their contents. So to ignore a generated file from the default build, you specify that the directory should ignore the generated file. - Note that the file will still be built if the user specifically + Note that the file will still be built if the user specifically requests the target on scons command line, or if the file is a dependency of another file which is requested and/or is built by default. diff --git a/doc/user/output.xml b/doc/user/output.xml index f9e1a98..d1082a9 100644 --- a/doc/user/output.xml +++ b/doc/user/output.xml @@ -47,7 +47,7 @@ <para> A key aspect of creating a usable build configuration - is providing good output from the build + is providing useful output from the build so its users can readily understand what the build is doing and get information about how to control the build. @@ -355,6 +355,17 @@ cc -o foo.o -c foo.c cc -o foo foo.o </screen> + <para> + + A gentle reminder here: many of the commands for building come in + pairs, depending on whether the intent is to build an object for + use in a shared library or not. The command strings mirror this, + so it may be necessary to set, for example, both + <envar>CCCOMSTR</envar> and <envar>SHCCCOMSTR</envar> + to get the desired results. + + </para> + </section> <section> diff --git a/src/CHANGES.txt b/src/CHANGES.txt index ad936e4..3acd034 100755 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -15,6 +15,17 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Added C:\msys64\mingw64\bin to default mingw and clang windows PATH's. This is a reasonable default and also aligns with changes in Appveyor's VS2019 image. - Drop support for Python 2.7. SCons will be Python 3.5+ going forward. + - Change SCons.Node.ValueWithMemo to consider any name passed when memoizing Value() nodes + + From Jeremy Elson: + - Updated design doc to use the correct syntax for Depends() + + From Adam Gross: + - Added support for taking instances of the Value class as implicit + dependencies. + - Added new module SCons.Scanner.Python to allow scanning .py files. + - Added support for explicitly passing a name when creating Value() nodes. This may be useful + when the value can't be converted to a string or if having a name is otherwise desirable. From Andrew Morrow: - Fix Issue #3469 - Fixed improper reuse of temporary and compiled files by Configure when changing @@ -33,12 +44,19 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Improve performance of Subst by preventing unnecessary frame allocations by no longer defining the *Subber classes inside of their respective function calls. + - Improve performance of Subst in some cases by preventing + unnecessary calls to eval when a token is surrounded in braces + but is not a function call. + - Improve performance of subst by removing unnecessary recursion. From Mats Wichmann: - Remove deprecated SourceCode - str.format syntax errors fixed - a bunch of linter/checker syntax fixups - Convert remaining uses of insecure/deprecated mktemp method. + - Clean up some duplications in manpage. Clarify portion of manpage on Dir and File nodes. + - Reduce needless list conversions. + - Fixed regex in Python scanner. RELEASE 3.1.2 - Mon, 17 Dec 2019 02:06:27 +0000 diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in index 3125824..3c2c0c2 100644 --- a/src/engine/MANIFEST.in +++ b/src/engine/MANIFEST.in @@ -40,6 +40,7 @@ SCons/Scanner/Fortran.py SCons/Scanner/IDL.py SCons/Scanner/LaTeX.py SCons/Scanner/Prog.py +SCons/Scanner/Python.py SCons/Scanner/RC.py SCons/Scanner/SWIG.py SCons/SConf.py @@ -140,6 +141,7 @@ SCons/Tool/pdf.py SCons/Tool/pdflatex.py SCons/Tool/pdftex.py SCons/Tool/PharLapCommon.py +SCons/Tool/python.py SCons/Tool/qt.py SCons/Tool/rmic.py SCons/Tool/rpcgen.py diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py index 69bb19e..13949e5 100644 --- a/src/engine/SCons/Builder.py +++ b/src/engine/SCons/Builder.py @@ -230,7 +230,7 @@ class OverrideWarner(collections.UserDict): def warn(self): if self.already_warned: return - for k in list(self.keys()): + for k in self.keys(): if k in misleading_keywords: alt = misleading_keywords[k] msg = "Did you mean to use `%s' instead of `%s'?" % (alt, k) diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py index f28e201..b4286fd 100644 --- a/src/engine/SCons/BuilderTests.py +++ b/src/engine/SCons/BuilderTests.py @@ -173,7 +173,7 @@ class MyNode_without_target_from_source(object): def builder_set(self, builder): self.builder = builder def has_builder(self): - return not self.builder is None + return self.builder is not None def set_explicit(self, is_explicit): self.is_explicit = is_explicit def has_explicit_builder(self): @@ -205,7 +205,7 @@ class BuilderTestCase(unittest.TestCase): """Test simple Builder creation """ builder = SCons.Builder.Builder(action="foo") - assert not builder is None, builder + assert builder is not None, builder builder = SCons.Builder.Builder(action="foo", OVERRIDE='x') x = builder.overrides['OVERRIDE'] assert x == 'x', x @@ -429,7 +429,7 @@ class BuilderTestCase(unittest.TestCase): return Foo(target) builder = SCons.Builder.Builder(target_factory = FooFactory) assert builder.target_factory is FooFactory - assert not builder.source_factory is FooFactory + assert builder.source_factory is not FooFactory def test_source_factory(self): """Test a Builder that creates source nodes of a specified class @@ -440,7 +440,7 @@ class BuilderTestCase(unittest.TestCase): global Foo return Foo(source) builder = SCons.Builder.Builder(source_factory = FooFactory) - assert not builder.target_factory is FooFactory + assert builder.target_factory is not FooFactory assert builder.source_factory is FooFactory def test_splitext(self): @@ -737,7 +737,7 @@ class BuilderTestCase(unittest.TestCase): with open(str(t), 'w') as f: f.write("function2\n") for t in tlist: - if not t in list(map(str, target)): + if t not in list(map(str, target)): with open(t, 'w') as f: f.write("function2\n") return 1 @@ -768,7 +768,7 @@ class BuilderTestCase(unittest.TestCase): with open(str(t), 'w') as f: f.write("function3\n") for t in tlist: - if not t in list(map(str, target)): + if t not in list(map(str, target)): with open(t, 'w') as f: f.write("function3\n") return 1 @@ -821,7 +821,7 @@ class BuilderTestCase(unittest.TestCase): assert s == ['aaa.bar'], s builder3 = SCons.Builder.Builder(action='bld3') - assert not builder3.src_builder is builder1.src_builder + assert builder3.src_builder is not builder1.src_builder builder4 = SCons.Builder.Builder(action='bld4', src_suffix='.i', diff --git a/src/engine/SCons/CacheDir.py b/src/engine/SCons/CacheDir.py index 10c088d..9bb7ef3 100644 --- a/src/engine/SCons/CacheDir.py +++ b/src/engine/SCons/CacheDir.py @@ -36,7 +36,6 @@ import sys import SCons import SCons.Action import SCons.Warnings -from SCons.Util import PY3 cache_enabled = True cache_debug = False @@ -160,10 +159,7 @@ class CacheDir(object): if path is None: return - if PY3: - self._readconfig3(path) - else: - self._readconfig2(path) + self._readconfig3(path) def _readconfig3(self, path): diff --git a/src/engine/SCons/CacheDirTests.py b/src/engine/SCons/CacheDirTests.py index 0e242c4..ff22d01 100644 --- a/src/engine/SCons/CacheDirTests.py +++ b/src/engine/SCons/CacheDirTests.py @@ -33,7 +33,6 @@ import stat from TestCmd import TestCmd import SCons.CacheDir -from SCons.Util import PY3 built_it = None @@ -169,10 +168,7 @@ class ExceptionTestCase(unittest.TestCase): os.remove(old_config) try: - if PY3: - self._CacheDir._readconfig3(self._CacheDir.path) - else: - self._CacheDir._readconfig2(self._CacheDir.path) + self._CacheDir._readconfig3(self._CacheDir.path) assert False, "Should have raised exception and did not" except SCons.Errors.SConsEnvironmentError as e: assert str(e) == "Failed to write cache configuration for {}".format(self._CacheDir.path) diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py index 3e23196..5e76e35 100644 --- a/src/engine/SCons/Environment.py +++ b/src/engine/SCons/Environment.py @@ -1854,7 +1854,7 @@ class Base(SubstitutionEnvironment): uniq = {} for executor in [n.get_executor() for n in nodes]: uniq[executor] = 1 - for executor in list(uniq.keys()): + for executor in uniq.keys(): executor.add_pre_action(action) return nodes @@ -1864,7 +1864,7 @@ class Base(SubstitutionEnvironment): uniq = {} for executor in [n.get_executor() for n in nodes]: uniq[executor] = 1 - for executor in list(uniq.keys()): + for executor in uniq.keys(): executor.add_post_action(action) return nodes @@ -2220,10 +2220,10 @@ class Base(SubstitutionEnvironment): else: return [self.subst(arg)] - def Value(self, value, built_value=None): + def Value(self, value, built_value=None, name=None): """ """ - return SCons.Node.Python.ValueWithMemo(value, built_value) + return SCons.Node.Python.ValueWithMemo(value, built_value, name) def VariantDir(self, variant_dir, src_dir, duplicate=1): variant_dir = self.arg2nodes(variant_dir, self.fs.Dir)[0] diff --git a/src/engine/SCons/Environment.xml b/src/engine/SCons/Environment.xml index b1c2039..e1d5e74 100644 --- a/src/engine/SCons/Environment.xml +++ b/src/engine/SCons/Environment.xml @@ -65,15 +65,6 @@ env['BUILDERS']['NewBuilder'] = foo </summary> </cvar> -<cvar name="Dir"> -<summary> -<para> -A function that converts a string -into a Dir instance relative to the target being built. -</para> -</summary> -</cvar> - <cvar name="ENV"> <summary> <para> @@ -130,15 +121,6 @@ env = Environment(ENV = {'PATH' : os.environ['PATH']}) </summary> </cvar> -<cvar name="File"> -<summary> -<para> -A function that converts a string into a File instance relative to the -target being built. -</para> -</summary> -</cvar> - <cvar name="SCANNERS"> <summary> <para> @@ -460,6 +442,8 @@ including another alias. can be called multiple times for the same alias to add additional targets to the alias, or additional actions to the list for this alias. +Aliases are global even if set through +the construction environment method. </para> <para> @@ -1163,17 +1147,16 @@ env.Decider('content') </example_commands> <para> -In addition to the above already-available functions, -the +In addition to the above already-available functions, the <varname>function</varname> -argument may be an actual Python function -that takes the following three arguments: +argument may be a Python function you supply. +Such a function must accept the following four arguments: </para> <para> <variablelist> <varlistentry> -<term><parameter>dependency</parameter></term> +<term><parameter class="function">dependency</parameter></term> <listitem> <para> The Node (file) which @@ -1187,7 +1170,7 @@ was built. </listitem> </varlistentry> <varlistentry> -<term><parameter>target</parameter></term> +<term><parameter class="function">target</parameter></term> <listitem> <para> The Node (file) being built. @@ -1200,7 +1183,7 @@ has "changed." </listitem> </varlistentry> <varlistentry> -<term><parameter>prev_ni</parameter></term> +<term><parameter class="function">prev_ni</parameter></term> <listitem> <para> Stored information about the state of the @@ -1216,12 +1199,17 @@ size, or content signature. </listitem> </varlistentry> <varlistentry> -<term><parameter>repo_node</parameter></term> +<term><parameter class="function">repo_node</parameter></term> <listitem> <para> -Use this node instead of the one specified by +If set, use this Node instead of the one specified by <varname>dependency</varname> - to determine if the dependency has changed. +to determine if the dependency has changed. +This argument is optional so should be written +as a default argument (typically it would be +written as <literal>repo_node=None</literal>). +A caller will normally only set this if the +target only exists in a Repository. </para> </listitem> </varlistentry> @@ -1351,11 +1339,10 @@ cc_values = env.Dictionary('CC', 'CCFLAGS', 'CCCOM') </arguments> <summary> <para> -This returns a Directory Node, -an object that represents the specified directory -<varname>name</varname>. +Returns Directory Node(s). +A Directory Node is an object that represents a directory. <varname>name</varname> -can be a relative or absolute path. +can be a relative or absolute path or a list of such paths. <varname>directory</varname> is an optional directory that will be used as the parent directory. If no @@ -1366,7 +1353,10 @@ is specified, the current script's directory is used as the parent. <para> If <varname>name</varname> -is a list, SCons returns a list of Dir nodes. +is a single pathname, the corresponding node is returned. +If +<varname>name</varname> +is a list, SCons returns a list of nodes. Construction variables are expanded in <varname>name</varname>. </para> @@ -1511,20 +1501,24 @@ if Execute("mkdir sub/dir/ectory"): </arguments> <summary> <para> -This returns a -File Node, -an object that represents the specified file -<varname>name</varname>. +Returns File Node(s). +A File Node is an object that represents a file. <varname>name</varname> -can be a relative or absolute path. +can be a relative or absolute path or a list of such paths. <varname>directory</varname> is an optional directory that will be used as the parent directory. +If no +<varname>directory</varname> +is specified, the current script's directory is used as the parent. </para> <para> If <varname>name</varname> -is a list, SCons returns a list of File nodes. +is a single pathname, the corresponding node is returned. +If +<varname>name</varname> +is a list, SCons returns a list of nodes. Construction variables are expanded in <varname>name</varname>. </para> @@ -2879,6 +2873,38 @@ function. </summary> </scons_function> +<scons_function name="Split"> +<arguments>(arg)</arguments> +<summary> +<para> +Returns a list of file names or other objects. +If <varname>arg</varname> is a string, +it will be split on strings of white-space characters +within the string, +making it easier to write long lists of file names. +If <varname>arg</varname> is already a list, +the list will be returned untouched. +If <varname>arg</varname> is any other type of object, +it will be returned as a list +containing just the object. +</para> + +<para> +Example: +</para> + +<example_commands> +files = Split("f1.c f2.c f3.c") +files = env.Split("f4.c f5.c f6.c") +files = Split(""" + f7.c + f8.c + f9.c +""") +</example_commands> +</summary> +</scons_function> + <scons_function name="subst"> <arguments signature="env"> (input, [raw, target, source, conv]) @@ -3055,7 +3081,7 @@ env.Tool('opengl', toolpath = ['build/tools']) <scons_function name="Value"> <arguments> -(value, [built_value]) +(value, [built_value], [name]) </arguments> <summary> <para> @@ -3070,6 +3096,10 @@ will be rebuilt. files are up-to-date.) When using timestamp source signatures, Value Nodes' timestamps are equal to the system time when the Node is created. +<varname>name</varname> can be provided as an alternative name +for the resulting <literal>Value</literal> node; this is advised +if the <varname>value</varname> parameter can't be converted to +a string. </para> <para> diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py index f3779c7..01baba3 100644 --- a/src/engine/SCons/EnvironmentTests.py +++ b/src/engine/SCons/EnvironmentTests.py @@ -236,7 +236,7 @@ class SubstitutionTestCase(unittest.TestCase): """ env = SubstitutionEnvironment(XXX = 'x') assert 'XXX' in env - assert not 'YYY' in env + assert 'YYY' not in env def test_items(self): """Test the SubstitutionEnvironment items() method @@ -1759,7 +1759,7 @@ def exists(env): env2.Dictionary('ZZZ')[5] = 6 assert env1.Dictionary('XXX') is env2.Dictionary('XXX') assert 4 in env2.Dictionary('YYY') - assert not 4 in env1.Dictionary('YYY') + assert 4 not in env1.Dictionary('YYY') assert 5 in env2.Dictionary('ZZZ') assert 5 not in env1.Dictionary('ZZZ') @@ -3289,6 +3289,10 @@ def generate(env): v3 = env.Value('c', 'build-c') assert v3.value == 'c', v3.value + v4 = env.Value(b'\x00\x0F', name='name') + assert v4.value == b'\x00\x0F', v4.value + assert v4.name == 'name', v4.name + def test_Environment_global_variable(self): """Test setting Environment variable to an Environment.Base subclass""" @@ -3547,8 +3551,8 @@ class OverrideEnvironmentTestCase(unittest.TestCase,TestEnvironmentFixture): assert 'YYY' in env assert 'YYY' in env2 assert 'YYY' in env3 - assert not 'ZZZ' in env - assert not 'ZZZ' in env2 + assert 'ZZZ' not in env + assert 'ZZZ' not in env2 assert 'ZZZ' in env3 def test_items(self): diff --git a/src/engine/SCons/Node/AliasTests.py b/src/engine/SCons/Node/AliasTests.py index 5d9c799..27b75b3 100644 --- a/src/engine/SCons/Node/AliasTests.py +++ b/src/engine/SCons/Node/AliasTests.py @@ -93,7 +93,7 @@ class AliasTestCase(unittest.TestCase): a2 = SCons.Node.Alias.Alias('a') assert a2.name == 'a', a2.name - assert not a1 is a2 + assert a1 is not a2 assert a1.name == a2.name class AliasNodeInfoTestCase(unittest.TestCase): diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index e1d6f68..6f16256 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -1612,7 +1612,7 @@ class Dir(Base): This clears any cached information that is invalidated by changing the repository.""" - for node in list(self.entries.values()): + for node in self.entries.values(): if node != self.dir: if node != self and isinstance(node, Dir): node.__clearRepositoryCache(duplicate) @@ -1623,7 +1623,7 @@ class Dir(Base): except AttributeError: pass if duplicate is not None: - node.duplicate=duplicate + node.duplicate = duplicate def __resetDuplicate(self, node): if node != self: diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index 9c19481..bbfdd1b 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -3066,12 +3066,12 @@ class RepositoryTestCase(_tempdirTestCase): assert r is d1, r r = d2.rentry() - assert not r is d2, r + assert r is not d2, r r = str(r) assert r == os.path.join(self.rep1, 'd2'), r r = d3.rentry() - assert not r is d3, r + assert r is not d3, r r = str(r) assert r == os.path.join(self.rep2, 'd3'), r @@ -3079,12 +3079,12 @@ class RepositoryTestCase(_tempdirTestCase): assert r is e1, r r = e2.rentry() - assert not r is e2, r + assert r is not e2, r r = str(r) assert r == os.path.join(self.rep1, 'e2'), r r = e3.rentry() - assert not r is e3, r + assert r is not e3, r r = str(r) assert r == os.path.join(self.rep2, 'e3'), r @@ -3092,12 +3092,12 @@ class RepositoryTestCase(_tempdirTestCase): assert r is f1, r r = f2.rentry() - assert not r is f2, r + assert r is not f2, r r = str(r) assert r == os.path.join(self.rep1, 'f2'), r r = f3.rentry() - assert not r is f3, r + assert r is not f3, r r = str(r) assert r == os.path.join(self.rep2, 'f3'), r @@ -3127,12 +3127,12 @@ class RepositoryTestCase(_tempdirTestCase): assert r is d1, r r = d2.rdir() - assert not r is d2, r + assert r is not d2, r r = str(r) assert r == os.path.join(self.rep1, 'd2'), r r = d3.rdir() - assert not r is d3, r + assert r is not d3, r r = str(r) assert r == os.path.join(self.rep3, 'd3'), r @@ -3183,12 +3183,12 @@ class RepositoryTestCase(_tempdirTestCase): assert r is f1, r r = f2.rfile() - assert not r is f2, r + assert r is not f2, r r = str(r) assert r == os.path.join(self.rep1, 'f2'), r r = f3.rfile() - assert not r is f3, r + assert r is not f3, r r = f3.rstr() assert r == os.path.join(self.rep3, 'f3'), r diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py index d8179ff..8d9d3a9 100644 --- a/src/engine/SCons/Node/NodeTests.py +++ b/src/engine/SCons/Node/NodeTests.py @@ -1104,7 +1104,7 @@ class NodeTestCase(unittest.TestCase): for kid in [n1, n3, n4, n6, n7, n9, n10, n12]: assert kid in kids, kid for kid in [n2, n5, n8, n11]: - assert not kid in kids, kid + assert kid not in kids, kid def test_all_children(self): """Test fetching all the "children" of a Node. diff --git a/src/engine/SCons/Node/Python.py b/src/engine/SCons/Node/Python.py index a5bcd4b..68c6ee8 100644 --- a/src/engine/SCons/Node/Python.py +++ b/src/engine/SCons/Node/Python.py @@ -88,7 +88,7 @@ class Value(SCons.Node.Node): NodeInfo = ValueNodeInfo BuildInfo = ValueBuildInfo - def __init__(self, value, built_value=None): + def __init__(self, value, built_value=None, name=None): SCons.Node.Node.__init__(self) self.value = value self.changed_since_last_build = 6 @@ -96,6 +96,13 @@ class Value(SCons.Node.Node): if built_value is not None: self.built_value = built_value + # Set a name so it can be a child of a node and not break + # its parent's implementation of Node.get_contents. + if name: + self.name = name + else: + self.name = str(value) + def str_for_display(self): return repr(self.value) @@ -177,23 +184,26 @@ class Value(SCons.Node.Node): return contents -def ValueWithMemo(value, built_value=None): +def ValueWithMemo(value, built_value=None, name=None): + """ + Memoized Value() node factory. + """ global _memo_lookup_map # No current support for memoizing a value that needs to be built. if built_value: - return Value(value, built_value) + return Value(value, built_value, name=name) try: - memo_lookup_key = hash(value) + memo_lookup_key = hash((value, name)) except TypeError: # Non-primitive types will hit this codepath. - return Value(value) + return Value(value, name=name) try: return _memo_lookup_map[memo_lookup_key] except KeyError: - v = Value(value) + v = Value(value, built_value, name) _memo_lookup_map[memo_lookup_key] = v return v diff --git a/src/engine/SCons/Node/PythonTests.py b/src/engine/SCons/Node/PythonTests.py index dbb4ec8..51a49c0 100644 --- a/src/engine/SCons/Node/PythonTests.py +++ b/src/engine/SCons/Node/PythonTests.py @@ -64,6 +64,12 @@ class ValueTestCase(unittest.TestCase): v2.build() assert v2.built_value == 'faked', v2.built_value + v3 = SCons.Node.Python.Value(b'\x00\x0F', name='name') + v3.executor = fake_executor() + v3.build() + assert v3.name == 'name', v3.name + assert v3.built_value == 'faked', v3.built_value + def test_read(self): """Test the Value.read() method """ @@ -98,6 +104,9 @@ class ValueTestCase(unittest.TestCase): assert csig == 'None', csig + + + class ValueNodeInfoTestCase(unittest.TestCase): def test___init__(self): """Test ValueNodeInfo initialization""" @@ -112,6 +121,18 @@ class ValueBuildInfoTestCase(unittest.TestCase): bi = SCons.Node.Python.ValueBuildInfo() +class ValueChildTestCase(unittest.TestCase): + def test___init__(self): + """Test support for a Value() being an implicit dependency of a Node""" + value = SCons.Node.Python.Value('v') + node = SCons.Node.Node() + node._func_get_contents = 2 # Pretend to be a Dir. + node.add_to_implicit([value]) + contents = node.get_contents() + expected_contents = '%s %s\n' % (value.get_csig(), value.name) + assert contents == expected_contents + + class ValueMemoTestCase(unittest.TestCase): def test_memo(self): """Test memoization""" @@ -144,6 +165,19 @@ class ValueMemoTestCase(unittest.TestCase): v4 = SCons.Node.Python.ValueWithMemo(a) assert v3 is not v4 + def test_value_set_name(self): + """ Confirm setting name and caching takes the name into account """ + + v1 = SCons.Node.Python.ValueWithMemo(b'\x00\x0F', name='name') + v2 = SCons.Node.Python.ValueWithMemo(b'\x00\x0F', name='name2') + v3 = SCons.Node.Python.ValueWithMemo('Jibberish') + + self.assertEqual(v1.name,'name', msg=v1.name) + self.assertEqual(v2.name,'name2', msg=v2.name) + self.assertEqual(v3.name,'Jibberish', msg=v3.name) + self.assertTrue(v1 is not v2, msg="v1 and v2 should be different as they have different names but same values") + + if __name__ == "__main__": unittest.main() diff --git a/src/engine/SCons/PathListTests.py b/src/engine/SCons/PathListTests.py index 104be73..09b1132 100644 --- a/src/engine/SCons/PathListTests.py +++ b/src/engine/SCons/PathListTests.py @@ -185,7 +185,7 @@ class PathListTestCase(unittest.TestCase): x3 = SCons.PathList.PathList('x') - assert not x1 is x3, (x1, x3) + assert x1 is not x3, (x1, x3) if __name__ == "__main__": diff --git a/src/engine/SCons/Platform/__init__.py b/src/engine/SCons/Platform/__init__.py index 058241d..5e9a358 100644 --- a/src/engine/SCons/Platform/__init__.py +++ b/src/engine/SCons/Platform/__init__.py @@ -195,7 +195,7 @@ class TempFileMunge(object): # Default to the .lnk suffix for the benefit of the Phar Lap # linkloc linker, which likes to append an .lnk suffix if # none is given. - if env.has_key('TEMPFILESUFFIX'): + if 'TEMPFILESUFFIX' in env: suffix = env.subst('$TEMPFILESUFFIX') else: suffix = '.lnk' diff --git a/src/engine/SCons/SConf.py b/src/engine/SCons/SConf.py index b2a6357..50a1329 100644 --- a/src/engine/SCons/SConf.py +++ b/src/engine/SCons/SConf.py @@ -133,8 +133,8 @@ def CreateConfigHBuilder(env): _stringConfigH) sconfigHBld = SCons.Builder.Builder(action=action) env.Append( BUILDERS={'SConfigHBuilder':sconfigHBld} ) - for k in list(_ac_config_hs.keys()): - env.SConfigHBuilder(k, env.Value(_ac_config_hs[k])) + for k, v in _ac_config_hs.items(): + env.SConfigHBuilder(k, env.Value(v)) class SConfWarning(SCons.Warnings.Warning): @@ -717,7 +717,7 @@ class SConfBase(object): """Adds all the tests given in the tests dictionary to this SConf instance """ - for name in list(tests.keys()): + for name in tests.keys(): self.AddTest(name, tests[name]) def _createDir( self, node ): diff --git a/src/engine/SCons/SConsignTests.py b/src/engine/SCons/SConsignTests.py index d40a7b6..782072d 100644 --- a/src/engine/SCons/SConsignTests.py +++ b/src/engine/SCons/SConsignTests.py @@ -337,7 +337,7 @@ class SConsignFileTestCase(SConsignTestCase): SCons.SConsign.ForDirectory(DummyNode(test.workpath('dir'))) - assert not SCons.SConsign.DataBase is None, SCons.SConsign.DataBase + assert SCons.SConsign.DataBase is not None, SCons.SConsign.DataBase assert fake_dbm.name == file, fake_dbm.name assert fake_dbm.mode == "c", fake_dbm.mode diff --git a/src/engine/SCons/Scanner/Python.py b/src/engine/SCons/Scanner/Python.py new file mode 100644 index 0000000..deb2241 --- /dev/null +++ b/src/engine/SCons/Scanner/Python.py @@ -0,0 +1,171 @@ +"""SCons.Scanner.Python + +This module implements the dependency scanner for Python code. + +One important note about the design is that this does not take any dependencies +upon packages or binaries in the Python installation unless they are listed in +PYTHONPATH. To do otherwise would have required code to determine where the +Python installation is, which is outside of the scope of a scanner like this. +If consumers want to pick up dependencies upon these packages, they must put +those directories in PYTHONPATH. + +""" + +# +# __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 itertools +import os +import re +import SCons.Scanner + +# Capture python "from a import b" and "import a" statements. +from_cre = re.compile(r'^\s*from\s+([^\s]+)\s+import\s+(.*)', re.M) +import_cre = re.compile(r'^\s*import\s+([^\s]+)', re.M) + + +def path_function(env, dir=None, target=None, source=None, argument=None): + """Retrieves a tuple with all search paths.""" + paths = env['ENV'].get('PYTHONPATH', '').split(os.pathsep) + if source: + paths.append(source[0].dir.abspath) + return tuple(paths) + + +def find_include_names(node): + """ + Scans the node for all imports. + + Returns a list of tuples. Each tuple has two elements: + 1. The main import (e.g. module, module.file, module.module2) + 2. Additional optional imports that could be functions or files + in the case of a "from X import Y" statement. In the case of a + normal "import" statement, this is None. + """ + text = node.get_text_contents() + all_matches = [] + matches = from_cre.findall(text) + if matches: + for match in matches: + imports = [i.strip() for i in match[1].split(',')] + + # Add some custom logic to strip out "as" because the regex + # includes it. + last_import_split = imports[-1].split() + if len(last_import_split) > 1: + imports[-1] = last_import_split[0] + + all_matches.append((match[0], imports)) + + matches = import_cre.findall(text) + if matches: + for match in matches: + all_matches.append((match, None)) + + return all_matches + + +def scan(node, env, path=()): + # cache the includes list in node so we only scan it once: + if node.includes is not None: + includes = node.includes + else: + includes = find_include_names(node) + # Intern the names of the include files. Saves some memory + # if the same header is included many times. + node.includes = list(map(SCons.Util.silent_intern, includes)) + + # XXX TODO: Sort? + nodes = [] + if callable(path): + path = path() + for module, imports in includes: + is_relative = module.startswith('.') + if is_relative: + # This is a relative include, so we must ignore PYTHONPATH. + module_lstripped = module.lstrip('.') + # One dot is current directory, two is parent, three is + # grandparent, etc. + num_parents = len(module) - len(module_lstripped) - 1 + current_dir = node.get_dir() + for i in itertools.repeat(None, num_parents): + current_dir = current_dir.up() + + search_paths = [current_dir.abspath] + search_string = module_lstripped + else: + search_paths = path + search_string = module + + module_components = search_string.split('.') + for search_path in search_paths: + candidate_path = os.path.join(search_path, *module_components) + # The import stored in "module" could refer to a directory or file. + import_dirs = [] + if os.path.isdir(candidate_path): + import_dirs = module_components + + # Because this resolved to a directory, there is a chance that + # additional imports (e.g. from module import A, B) could refer + # to files to import. + if imports: + for imp in imports: + file = os.path.join(candidate_path, imp + '.py') + if os.path.isfile(file): + nodes.append(file) + elif os.path.isfile(candidate_path + '.py'): + nodes.append(candidate_path + '.py') + import_dirs = module_components[:-1] + + # We can ignore imports because this resolved to a file. Any + # additional imports (e.g. from module.file import A, B) would + # only refer to functions in this file. + + # Take a dependency on all __init__.py files from all imported + # packages unless it's a relative import. If it's a relative + # import, we don't need to take the dependency because Python + # requires that all referenced packages have already been imported, + # which means that the dependency has already been established. + if import_dirs and not is_relative: + for i in range(len(import_dirs)): + init_components = module_components[:i+1] + ['__init__.py'] + init_path = os.path.join(search_path, *(init_components)) + if os.path.isfile(init_path): + nodes.append(init_path) + break + + return sorted(nodes) + + +PythonSuffixes = ['.py'] +PythonScanner = SCons.Scanner.Base(scan, name='PythonScanner', + skeys=PythonSuffixes, + path_function=path_function, recursive=1) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/src/engine/SCons/Scanner/PythonTests.py b/src/engine/SCons/Scanner/PythonTests.py new file mode 100644 index 0000000..0d4e628 --- /dev/null +++ b/src/engine/SCons/Scanner/PythonTests.py @@ -0,0 +1,269 @@ +# +# __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 os +import unittest + +import TestCmd + +import SCons.Node.FS +import SCons.Scanner.Python + +test = TestCmd.TestCmd(workdir='') +test.dir_fixture('python_scanner') + +if os.path.normcase('foo') == os.path.normcase('FOO'): + my_normpath = os.path.normcase +else: + my_normpath = os.path.normpath + + +def deps_match(self, deps, headers): + global my_normpath + scanned = list(map(my_normpath, list(map(str, deps)))) + expect = list(map(my_normpath, headers)) + self.assertTrue(scanned == expect, + "expect %s != scanned %s" % (expect, scanned)) + + +# Copied from LaTeXTests.py. +class DummyEnvironment(collections.UserDict): + def __init__(self, **kw): + collections.UserDict.__init__(self) + self.data.update(kw) + self.fs = SCons.Node.FS.FS(test.workpath('')) + self['ENV'] = {} + + def Dictionary(self, *args): + return self.data + + def subst(self, strSubst, target=None, source=None, conv=None): + if strSubst[0] == '$': + return self.data[strSubst[1:]] + return strSubst + + def subst_list(self, strSubst, target=None, source=None, conv=None): + if strSubst[0] == '$': + return [self.data[strSubst[1:]]] + return [[strSubst]] + + def subst_path(self, path, target=None, source=None, conv=None): + if not isinstance(path, list): + path = [path] + return list(map(self.subst, path)) + + def get_calculator(self): + return None + + def get_factory(self, factory): + return factory or self.fs.File + + def Dir(self, filename): + return self.fs.Dir(filename) + + def File(self, filename): + return self.fs.File(filename) + + +class PythonScannerTestPythonPath(unittest.TestCase): + def runTest(self): + env = DummyEnvironment() + s = SCons.Scanner.Python.PythonScanner + env['ENV']['PYTHONPATH'] = test.workpath('') + path = s.path(env) + deps = s(env.File('imports_simple_package.py'), env, path) + files = ['simple_package/__init__.py'] + deps_match(self, deps, files) + + +class PythonScannerTestPythonCallablePath(unittest.TestCase): + def runTest(self): + env = DummyEnvironment() + s = SCons.Scanner.Python.PythonScanner + env['ENV']['PYTHONPATH'] = test.workpath('') + deps = s(env.File('imports_simple_package.py'), env, + lambda : s.path(env)) + files = ['simple_package/__init__.py'] + deps_match(self, deps, files) + + +class PythonScannerTestImportSimplePackage(unittest.TestCase): + def runTest(self): + env = DummyEnvironment() + s = SCons.Scanner.Python.PythonScanner + node = env.File('imports_simple_package.py') + path = s.path(env, source=[node]) + deps = s(node, env, path) + files = ['simple_package/__init__.py'] + deps_match(self, deps, files) + + # Repeat the test in case there are any issues caching includes. + deps = s(node, env, path) + deps_match(self, deps, files) + + +class PythonScannerTestImportSimplePackageModule1As(unittest.TestCase): + def runTest(self): + env = DummyEnvironment() + s = SCons.Scanner.Python.PythonScanner + node = env.File('import_simple_package_module1_as.py') + path = s.path(env, source=[node]) + deps = s(node, env, path) + files = ['simple_package/__init__.py', 'simple_package/module1.py'] + deps_match(self, deps, files) + + +class PythonScannerTestImportSimplePackageModuleAs(unittest.TestCase): + def runTest(self): + env = DummyEnvironment() + s = SCons.Scanner.Python.PythonScanner + node = env.File('import_simple_package_module1.py') + path = s.path(env, source=[node]) + deps = s(node, env, path) + files = ['simple_package/__init__.py', 'simple_package/module1.py'] + deps_match(self, deps, files) + + +class PythonScannerTestFromImportSimplePackageModule1(unittest.TestCase): + def runTest(self): + env = DummyEnvironment() + s = SCons.Scanner.Python.PythonScanner + node = env.File('from_import_simple_package_module1.py') + path = s.path(env, source=[node]) + deps = s(node, env, path) + files = ['simple_package/__init__.py', 'simple_package/module1.py'] + deps_match(self, deps, files) + + +class PythonScannerTestFromImportSimplePackageModule1As(unittest.TestCase): + def runTest(self): + env = DummyEnvironment() + s = SCons.Scanner.Python.PythonScanner + node = env.File('from_import_simple_package_module1_as.py') + path = s.path(env, source=[node]) + deps = s(node, env, path) + files = ['simple_package/__init__.py', 'simple_package/module1.py'] + deps_match(self, deps, files) + + +class PythonScannerTestFromImportSimplePackageModulesNoSpace( + unittest.TestCase): + def runTest(self): + env = DummyEnvironment() + s = SCons.Scanner.Python.PythonScanner + node = env.File('from_import_simple_package_modules_no_space.py') + path = s.path(env, source=[node]) + deps = s(node, env, path) + files = ['simple_package/__init__.py', 'simple_package/module1.py', + 'simple_package/module2.py'] + deps_match(self, deps, files) + + +class PythonScannerTestFromImportSimplePackageModulesWithSpace( + unittest.TestCase): + def runTest(self): + env = DummyEnvironment() + s = SCons.Scanner.Python.PythonScanner + node = env.File('from_import_simple_package_modules_with_space.py') + path = s.path(env, source=[node]) + deps = s(node, env, path) + files = ['simple_package/__init__.py', 'simple_package/module1.py', + 'simple_package/module2.py'] + deps_match(self, deps, files) + + +class PythonScannerTestCurdirReferenceScript(unittest.TestCase): + def runTest(self): + env = DummyEnvironment() + s = SCons.Scanner.Python.PythonScanner + node = env.Dir('curdir_reference').File('script.py') + path = s.path(env, source=[node]) + deps = s(node, env, path) + files = ['curdir_reference/helper.py'] + deps_match(self, deps, files) + + +class PythonScannerTestImportsNested3(unittest.TestCase): + def runTest(self): + env = DummyEnvironment() + s = SCons.Scanner.Python.PythonScanner + node = env.File('imports_nested3.py') + path = s.path(env, source=[node]) + deps = s(node, env, path) + files = ['nested1/__init__.py', 'nested1/nested2/__init__.py', + 'nested1/nested2/nested3/__init__.py'] + deps_match(self, deps, files) + + +class PythonScannerTestImportsGrandparentModule(unittest.TestCase): + def runTest(self): + env = DummyEnvironment() + s = SCons.Scanner.Python.PythonScanner + node = env.File( + 'nested1/nested2/nested3/imports_grandparent_module.py') + path = s.path(env, source=[node]) + deps = s(node, env, path) + # Note: there is some ambiguity here in what the scanner should return. + # Relative imports require that the referenced packages have already + # been imported. + files = ['nested1/module.py'] + deps_match(self, deps, files) + + +class PythonScannerTestImportsParentModule(unittest.TestCase): + def runTest(self): + env = DummyEnvironment() + s = SCons.Scanner.Python.PythonScanner + node = env.File( + 'nested1/nested2/nested3/imports_parent_module.py') + path = s.path(env, source=[node]) + deps = s(node, env, path) + files = ['nested1/nested2/module.py'] + deps_match(self, deps, files) + + +class PythonScannerTestImportsParentThenSubmodule(unittest.TestCase): + def runTest(self): + env = DummyEnvironment() + s = SCons.Scanner.Python.PythonScanner + node = env.File( + 'nested1/nested2/nested3/imports_parent_then_submodule.py') + path = s.path(env, source=[node]) + deps = s(node, env, path) + files = ['nested1/nested2a/module.py'] + deps_match(self, deps, files) + + +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/Scanner/RCTests.py b/src/engine/SCons/Scanner/RCTests.py index 551e613..347149c 100644 --- a/src/engine/SCons/Scanner/RCTests.py +++ b/src/engine/SCons/Scanner/RCTests.py @@ -82,7 +82,7 @@ class DummyEnvironment(collections.UserDict): def Dictionary(self, *args): return self.data - def subst(self, arg, target=None, source=None, conv=None): + def subst(self, strSubst, target=None, source=None, conv=None): if strSubst[0] == '$': return self.data[strSubst[1:]] return strSubst diff --git a/src/engine/SCons/Script/Interactive.py b/src/engine/SCons/Script/Interactive.py index cc4f23c..59299f1 100644 --- a/src/engine/SCons/Script/Interactive.py +++ b/src/engine/SCons/Script/Interactive.py @@ -247,7 +247,7 @@ version Prints SCons version information. while n: n = walker.get_next() - for node in list(seen_nodes.keys()): + for node in seen_nodes.keys(): # Call node.clear() to clear most of the state node.clear() # node.clear() doesn't reset node.state, so call diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py index f9c8384..a0d7f4c 100644 --- a/src/engine/SCons/Script/Main.py +++ b/src/engine/SCons/Script/Main.py @@ -743,9 +743,9 @@ def _load_site_scons_dir(topdir, site_dir_name=None): modname = os.path.basename(pathname)[:-len(sfx)] site_m = {"__file__": pathname, "__name__": modname, "__doc__": None} re_special = re.compile("__[^_]+__") - for k in list(m.__dict__.keys()): + for k, v in m.__dict__.items(): if not re_special.match(k): - site_m[k] = m.__dict__[k] + site_m[k] = v # This is the magic. exec(compile(fp.read(), fp.name, 'exec'), site_m) diff --git a/src/engine/SCons/Script/Main.xml b/src/engine/SCons/Script/Main.xml index 5c68dee..b54df0e 100644 --- a/src/engine/SCons/Script/Main.xml +++ b/src/engine/SCons/Script/Main.xml @@ -312,7 +312,7 @@ The options supported are: <term><literal>cache_debug</literal></term> <listitem> <para> -which corresponds to --cache-debug; +which corresponds to <option>--cache-debug</option>; </para> </listitem> </varlistentry> @@ -320,7 +320,7 @@ which corresponds to --cache-debug; <term><literal>cache_disable</literal></term> <listitem> <para> -which corresponds to --cache-disable; +which corresponds to <option>--cache-disable</option>; </para> </listitem> </varlistentry> @@ -328,7 +328,7 @@ which corresponds to --cache-disable; <term><literal>cache_force</literal></term> <listitem> <para> -which corresponds to --cache-force; +which corresponds to <option>--cache-force</option>; </para> </listitem> </varlistentry> @@ -336,7 +336,7 @@ which corresponds to --cache-force; <term><literal>cache_show</literal></term> <listitem> <para> -which corresponds to --cache-show; +which corresponds to <option>--cache-show</option>; </para> </listitem> </varlistentry> @@ -344,7 +344,8 @@ which corresponds to --cache-show; <term><literal>clean</literal></term> <listitem> <para> -which corresponds to -c, --clean and --remove; +which corresponds to <option>-c</option>, <option>--clean</option> +and <option>--remove</option>; </para> </listitem> </varlistentry> @@ -352,7 +353,7 @@ which corresponds to -c, --clean and --remove; <term><literal>config</literal></term> <listitem> <para> -which corresponds to --config; +which corresponds to <option>--config</option>; </para> </listitem> </varlistentry> @@ -360,7 +361,7 @@ which corresponds to --config; <term><literal>directory</literal></term> <listitem> <para> -which corresponds to -C and --directory; +which corresponds to <option>-C</option> and <option>--directory</option>; </para> </listitem> </varlistentry> @@ -368,7 +369,7 @@ which corresponds to -C and --directory; <term><literal>diskcheck</literal></term> <listitem> <para> -which corresponds to --diskcheck +which corresponds to <option>--diskcheck</option>; </para> </listitem> </varlistentry> @@ -376,7 +377,7 @@ which corresponds to --diskcheck <term><literal>duplicate</literal></term> <listitem> <para> -which corresponds to --duplicate; +which corresponds to <option>--duplicate</option>; </para> </listitem> </varlistentry> @@ -384,7 +385,7 @@ which corresponds to --duplicate; <term><literal>file</literal></term> <listitem> <para> -which corresponds to -f, --file, --makefile and --sconstruct; +which corresponds to <option>-f</option>, <option>--file</option>, <option>--makefile</option> and <option>--sconstruct</option>; </para> </listitem> </varlistentry> @@ -392,7 +393,7 @@ which corresponds to -f, --file, --makefile and --sconstruct; <term><literal>help</literal></term> <listitem> <para> -which corresponds to -h and --help; +which corresponds to <option>-h</option> and <option>--help</option>; </para> </listitem> </varlistentry> @@ -400,7 +401,7 @@ which corresponds to -h and --help; <term><literal>ignore_errors</literal></term> <listitem> <para> -which corresponds to --ignore-errors; +which corresponds to <option>--ignore-errors</option>; </para> </listitem> </varlistentry> @@ -408,7 +409,7 @@ which corresponds to --ignore-errors; <term><literal>implicit_cache</literal></term> <listitem> <para> -which corresponds to --implicit-cache; +which corresponds to <option>--implicit-cache</option>; </para> </listitem> </varlistentry> @@ -416,7 +417,7 @@ which corresponds to --implicit-cache; <term><literal>implicit_deps_changed</literal></term> <listitem> <para> -which corresponds to --implicit-deps-changed; +which corresponds to <option>--implicit-deps-changed</option>; </para> </listitem> </varlistentry> @@ -424,7 +425,7 @@ which corresponds to --implicit-deps-changed; <term><literal>implicit_deps_unchanged</literal></term> <listitem> <para> -which corresponds to --implicit-deps-unchanged; +which corresponds to <option>--implicit-deps-unchanged</option>; </para> </listitem> </varlistentry> @@ -432,7 +433,7 @@ which corresponds to --implicit-deps-unchanged; <term><literal>interactive</literal></term> <listitem> <para> -which corresponds to --interact and --interactive; +which corresponds to <option>--interact</option> and <option>--interactive</option>; </para> </listitem> </varlistentry> @@ -440,7 +441,7 @@ which corresponds to --interact and --interactive; <term><literal>keep_going</literal></term> <listitem> <para> -which corresponds to -k and --keep-going; +which corresponds to <option>-k</option> and <option>--keep-going</option>; </para> </listitem> </varlistentry> @@ -448,7 +449,7 @@ which corresponds to -k and --keep-going; <term><literal>max_drift</literal></term> <listitem> <para> -which corresponds to --max-drift; +which corresponds to <option>--max-drift</option>; </para> </listitem> </varlistentry> @@ -456,7 +457,9 @@ which corresponds to --max-drift; <term><literal>no_exec</literal></term> <listitem> <para> -which corresponds to -n, --no-exec, --just-print, --dry-run and --recon; +which corresponds to <option>-n</option>, +<option>--no-exec</option>, <option>--just-print</option>, +<option>--dry-run</option> and <option>--recon</option>; </para> </listitem> </varlistentry> @@ -464,7 +467,7 @@ which corresponds to -n, --no-exec, --just-print, --dry-run and --recon; <term><literal>no_site_dir</literal></term> <listitem> <para> -which corresponds to --no-site-dir; +which corresponds to <option>--no-site-dir</option>; </para> </listitem> </varlistentry> @@ -472,7 +475,7 @@ which corresponds to --no-site-dir; <term><literal>num_jobs</literal></term> <listitem> <para> -which corresponds to -j and --jobs; +which corresponds to <option>-j</option> and <option>--jobs</option>; </para> </listitem> </varlistentry> @@ -480,7 +483,7 @@ which corresponds to -j and --jobs; <term><literal>profile_file</literal></term> <listitem> <para> -which corresponds to --profile; +which corresponds to <option>--profile</option>; </para> </listitem> </varlistentry> @@ -488,7 +491,7 @@ which corresponds to --profile; <term><literal>question</literal></term> <listitem> <para> -which corresponds to -q and --question; +which corresponds to <option>-q</option> and <option>--question</option>; </para> </listitem> </varlistentry> @@ -496,7 +499,7 @@ which corresponds to -q and --question; <term><literal>random</literal></term> <listitem> <para> -which corresponds to --random; +which corresponds to <option>--random</option>; </para> </listitem> </varlistentry> @@ -504,7 +507,7 @@ which corresponds to --random; <term><literal>repository</literal></term> <listitem> <para> -which corresponds to -Y, --repository and --srcdir; +which corresponds to <option>-Y</option>, <option>--repository</option> and <option>--srcdir</option>; </para> </listitem> </varlistentry> @@ -512,7 +515,7 @@ which corresponds to -Y, --repository and --srcdir; <term><literal>silent</literal></term> <listitem> <para> -which corresponds to -s, --silent and --quiet; +which corresponds to <option>-s</option>, <option>--silent</option> and <option>--quiet</option>; </para> </listitem> </varlistentry> @@ -520,7 +523,7 @@ which corresponds to -s, --silent and --quiet; <term><literal>site_dir</literal></term> <listitem> <para> -which corresponds to --site-dir; +which corresponds to <option>--site-dir</option>; </para> </listitem> </varlistentry> @@ -528,7 +531,7 @@ which corresponds to --site-dir; <term><literal>stack_size</literal></term> <listitem> <para> -which corresponds to --stack-size; +which corresponds to <option>--stack-size</option>; </para> </listitem> </varlistentry> @@ -536,7 +539,7 @@ which corresponds to --stack-size; <term><literal>taskmastertrace_file</literal></term> <listitem> <para> -which corresponds to --taskmastertrace; and +which corresponds to <option>--taskmastertrace</option>; and </para> </listitem> </varlistentry> @@ -544,7 +547,7 @@ which corresponds to --taskmastertrace; and <term><literal>warn</literal></term> <listitem> <para> -which corresponds to --warn and --warning. +which corresponds to <option>--warn</option> and <option>--warning</option>. </para> </listitem> </varlistentry> @@ -553,7 +556,7 @@ which corresponds to --warn and --warning. <para> See the documentation for the -corresponding command line object for information about each specific +corresponding command line option for information about each specific option. </para> </summary> @@ -749,7 +752,8 @@ line options from a SConscript file. The options supported are: <term><literal>clean</literal></term> <listitem> <para> -which corresponds to -c, --clean and --remove; +which corresponds to <option>-c</option>, <option>--clean</option> +and <option>--remove</option>; </para> </listitem> </varlistentry> @@ -757,7 +761,7 @@ which corresponds to -c, --clean and --remove; <term><literal>duplicate</literal></term> <listitem> <para> -which corresponds to --duplicate; +which corresponds to <option>--duplicate</option>; </para> </listitem> </varlistentry> @@ -765,7 +769,7 @@ which corresponds to --duplicate; <term><literal>help</literal></term> <listitem> <para> -which corresponds to -h and --help; +which corresponds to <option>-h</option> and <option>--help</option>; </para> </listitem> </varlistentry> @@ -773,7 +777,7 @@ which corresponds to -h and --help; <term><literal>implicit_cache</literal></term> <listitem> <para> -which corresponds to --implicit-cache; +which corresponds to <option>--implicit-cache</option>; </para> </listitem> </varlistentry> @@ -781,7 +785,7 @@ which corresponds to --implicit-cache; <term><literal>max_drift</literal></term> <listitem> <para> -which corresponds to --max-drift; +which corresponds to <option>--max-drift</option>; </para> </listitem> </varlistentry> @@ -789,7 +793,9 @@ which corresponds to --max-drift; <term><literal>no_exec</literal></term> <listitem> <para> -which corresponds to -n, --no-exec, --just-print, --dry-run and --recon; +which corresponds to <option>-n</option>, <option>--no-exec</option>, +<option>--just-print</option>, <option>--dry-run</option> +and <option>--recon</option>; </para> </listitem> </varlistentry> @@ -797,7 +803,7 @@ which corresponds to -n, --no-exec, --just-print, --dry-run and --recon; <term><literal>num_jobs</literal></term> <listitem> <para> -which corresponds to -j and --jobs; +which corresponds to <option>-j</option> and <option>--jobs</option>; </para> </listitem> </varlistentry> @@ -805,7 +811,7 @@ which corresponds to -j and --jobs; <term><literal>random</literal></term> <listitem> <para> -which corresponds to --random; and +which corresponds to <option>--random</option>; and </para> </listitem> </varlistentry> @@ -813,7 +819,7 @@ which corresponds to --random; and <term><literal>silent</literal></term> <listitem> <para> -which corresponds to --silent. +which corresponds to <option>--silent</option>. </para> </listitem> </varlistentry> @@ -830,7 +836,7 @@ which corresponds to --stack-size. <para> See the documentation for the -corresponding command line object for information about each specific +corresponding command line option for information about each specific option. </para> diff --git a/src/engine/SCons/Script/SConscript.xml b/src/engine/SCons/Script/SConscript.xml index 874c110..a1e0129 100644 --- a/src/engine/SCons/Script/SConscript.xml +++ b/src/engine/SCons/Script/SConscript.xml @@ -252,16 +252,16 @@ This specifies help text to be printed if the <option>-h</option> argument is given to &scons;. -If +If &f-Help; -is called multiple times, the text is appended together in the order that +is called multiple times, the text is appended together in the order that &f-Help; -is called. With append set to False, any +is called. With append set to False, any &f-Help; -text generated with +text generated with &f-AddOption; is clobbered. If append is True, the AddOption help is prepended to the help -string, thus preserving the +string, thus preserving the <option>-h</option> message. </para> @@ -393,7 +393,7 @@ A single script may be specified as a string; multiple scripts must be specified as a list (either explicitly or as created by a function like -&f-Split;). +&f-link-Split;). Examples: </para> <example_commands> diff --git a/src/engine/SCons/Subst.py b/src/engine/SCons/Subst.py index 664cd6c..a1ef053 100644 --- a/src/engine/SCons/Subst.py +++ b/src/engine/SCons/Subst.py @@ -375,23 +375,29 @@ class StringSubber(object): if key[0] == '{' or '.' in key: if key[0] == '{': key = key[1:-1] - try: - s = eval(key, self.gvars, lvars) - except KeyboardInterrupt: - raise - except Exception as e: - if e.__class__ in AllowableExceptions: - return '' - raise_exception(e, lvars['TARGETS'], s) + + # Store for error messages if we fail to expand the + # value + old_s = s + s = None + if key in lvars: + s = lvars[key] + elif key in self.gvars: + s = self.gvars[key] else: - if key in lvars: - s = lvars[key] - elif key in self.gvars: - s = self.gvars[key] - elif NameError not in AllowableExceptions: - raise_exception(NameError(key), lvars['TARGETS'], s) - else: - return '' + try: + s = eval(key, self.gvars, lvars) + except KeyboardInterrupt: + raise + except Exception as e: + if e.__class__ in AllowableExceptions: + return '' + raise_exception(e, lvars['TARGETS'], old_s) + + if s is None and NameError not in AllowableExceptions: + raise_exception(NameError(key), lvars['TARGETS'], old_s) + elif s is None: + return '' # Before re-expanding the result, handle # recursive expansion by copying the local @@ -493,6 +499,21 @@ class ListSubber(collections.UserList): self.in_strip = None self.next_line() + def expanded(self, s): + """Determines if the string s requires further expansion. + + Due to the implementation of ListSubber expand will call + itself 2 additional times for an already expanded string. This + method is used to determine if a string is already fully + expanded and if so exit the loop early to prevent these + recursive calls. + """ + if not is_String(s) or isinstance(s, CmdStringHolder): + return False + + s = str(s) # in case it's a UserString + return _separate_args.findall(s) is None + def expand(self, s, lvars, within_list): """Expand a single "token" as necessary, appending the expansion to the current result. @@ -524,23 +545,35 @@ class ListSubber(collections.UserList): if key[0] == '{' or key.find('.') >= 0: if key[0] == '{': key = key[1:-1] - try: - s = eval(key, self.gvars, lvars) - except KeyboardInterrupt: - raise - except Exception as e: - if e.__class__ in AllowableExceptions: - return - raise_exception(e, lvars['TARGETS'], s) + + # Store for error messages if we fail to expand the + # value + old_s = s + s = None + if key in lvars: + s = lvars[key] + elif key in self.gvars: + s = self.gvars[key] else: - if key in lvars: - s = lvars[key] - elif key in self.gvars: - s = self.gvars[key] - elif NameError not in AllowableExceptions: - raise_exception(NameError(), lvars['TARGETS'], s) - else: - return + try: + s = eval(key, self.gvars, lvars) + except KeyboardInterrupt: + raise + except Exception as e: + if e.__class__ in AllowableExceptions: + return + raise_exception(e, lvars['TARGETS'], old_s) + + if s is None and NameError not in AllowableExceptions: + raise_exception(NameError(), lvars['TARGETS'], old_s) + elif s is None: + return + + # If the string is already full expanded there's no + # need to continue recursion. + if self.expanded(s): + self.append(s) + return # Before re-expanding the result, handle # recursive expansion by copying the local diff --git a/src/engine/SCons/Subst.xml b/src/engine/SCons/Subst.xml index 980a9ad..77372ce 100644 --- a/src/engine/SCons/Subst.xml +++ b/src/engine/SCons/Subst.xml @@ -39,7 +39,7 @@ or <literal>IndexError</literal> exception will expand to a <literal>''</literal> -(a null string) and not cause scons to fail. +(an empty string) and not cause scons to fail. All exceptions not in the specified list will generate an error message and terminate processing. diff --git a/src/engine/SCons/Taskmaster.py b/src/engine/SCons/Taskmaster.py index 06fc94c..1e5776c 100644 --- a/src/engine/SCons/Taskmaster.py +++ b/src/engine/SCons/Taskmaster.py @@ -792,7 +792,7 @@ class Taskmaster(object): self.ready_exc = None T = self.trace - if T: T.write(SCons.Util.UnicodeType('\n') + self.trace_message('Looking for a node to evaluate')) + if T: T.write('\n' + self.trace_message('Looking for a node to evaluate')) while True: node = self.next_candidate() diff --git a/src/engine/SCons/TaskmasterTests.py b/src/engine/SCons/TaskmasterTests.py index c0c77b0..1a47230 100644 --- a/src/engine/SCons/TaskmasterTests.py +++ b/src/engine/SCons/TaskmasterTests.py @@ -146,7 +146,7 @@ class Node(object): pass def has_builder(self): - return not self.builder is None + return self.builder is not None def is_derived(self): return self.has_builder or self.side_effect @@ -935,7 +935,7 @@ class TaskmasterTestCase(unittest.TestCase): except SCons.Errors.UserError: pass else: - raise TestFailed("did not catch expected UserError") + self.fail("did not catch expected UserError") def raise_BuildError(): raise SCons.Errors.BuildError @@ -948,7 +948,7 @@ class TaskmasterTestCase(unittest.TestCase): except SCons.Errors.BuildError: pass else: - raise TestFailed("did not catch expected BuildError") + self.fail("did not catch expected BuildError") # On a generic (non-BuildError) exception from a Builder, # the target should throw a BuildError exception with the @@ -968,7 +968,7 @@ class TaskmasterTestCase(unittest.TestCase): exc_traceback = sys.exc_info()[2] assert isinstance(e.exc_info[2], type(exc_traceback)), e.exc_info[2] else: - raise TestFailed("did not catch expected BuildError") + self.fail("did not catch expected BuildError") built_text = None cache_text = [] @@ -1049,7 +1049,7 @@ class TaskmasterTestCase(unittest.TestCase): assert cache_text == ["n1 retrieved"], cache_text # If no binfo exists anymore, something has gone wrong... has_binfo = hasattr(n1, 'binfo') - assert has_binfo == True, has_binfo + assert has_binfo, has_binfo def test_exception(self): """Test generic Taskmaster exception handling diff --git a/src/engine/SCons/Tool/DCommon.xml b/src/engine/SCons/Tool/DCommon.xml index 7cc47da..6d907c6 100644 --- a/src/engine/SCons/Tool/DCommon.xml +++ b/src/engine/SCons/Tool/DCommon.xml @@ -27,6 +27,7 @@ See its __doc__ string for a discussion of the format. <summary> <para> The D compiler to use. +See also &cv-link-SHDC; for compiling to shared objects. </para> </summary> </cvar> @@ -37,6 +38,18 @@ The D compiler to use. The command line used to compile a D file to an object file. Any options specified in the &cv-link-DFLAGS; construction variable is included on this command line. +See also &cv-link-SHDCOM; for compiling to shared objects. +</para> +</summary> +</cvar> + +<cvar name="DCOMSTR"> +<summary> +<para> +If set, the string displayed when a D source file +is compiled to a (static) object file. +If not set, then &cv-link-DCOM; (the command line) is displayed. +See also &cv-link-SHDCOMSTR; for compiling to shared objects. </para> </summary> </cvar> @@ -181,6 +194,7 @@ DLIBLINKSUFFIX. <summary> <para> Name of the linker to use for linking systems including D sources. +See also &cv-link-SHDLINK; for linking shared objects. </para> </summary> </cvar> @@ -189,6 +203,7 @@ Name of the linker to use for linking systems including D sources. <summary> <para> The command line to use when linking systems including D sources. +See also &cv-link-SHDLINKCOM; for linking shared objects. </para> </summary> </cvar> @@ -197,6 +212,7 @@ The command line to use when linking systems including D sources. <summary> <para> List of linker flags. +See also &cv-link-SHDLINKFLAGS; for linking shared objects. </para> </summary> </cvar> @@ -278,6 +294,7 @@ DVERSUFFIX. <para> The name of the compiler to use when compiling D source destined to be in a shared objects. +See also &cv-link-DC; for compiling to static objects. </para> </summary> </cvar> @@ -286,6 +303,18 @@ destined to be in a shared objects. <summary> <para> The command line to use when compiling code to be part of shared objects. +See also &cv-link-DCOM; for compiling to static objects. +</para> +</summary> +</cvar> + +<cvar name="SHDCOMSTR"> +<summary> +<para> +If set, the string displayed when a D source file +is compiled to a (shared) object file. +If not set, then &cv-link-SHDCOM; (the command line) is displayed. +See also &cv-link-DCOMSTR; for compiling to static objects. </para> </summary> </cvar> @@ -311,6 +340,7 @@ SHDLIBVERSIONFLAGS. <para> The linker to use when creating shared objects for code bases include D sources. +See also &cv-link-DLINK; for linking static objects. </para> </summary> </cvar> @@ -319,6 +349,7 @@ include D sources. <summary> <para> The command line to use when generating shared objects. +See also &cv-link-DLINKCOM; for linking static objects. </para> </summary> </cvar> @@ -327,6 +358,7 @@ The command line to use when generating shared objects. <summary> <para> The list of flags to use when generating a shared object. +See also &cv-link-DLINKFLAGS; for linking static objects. </para> </summary> </cvar> diff --git a/src/engine/SCons/Tool/MSCommon/common.py b/src/engine/SCons/Tool/MSCommon/common.py index 386f445..4ce605b 100644 --- a/src/engine/SCons/Tool/MSCommon/common.py +++ b/src/engine/SCons/Tool/MSCommon/common.py @@ -152,8 +152,8 @@ def normalize_env(env, keys, force=False): Note: the environment is copied.""" normenv = {} if env: - for k in list(env.keys()): - normenv[k] = copy.deepcopy(env[k]) + for k, v in env.items(): + normenv[k] = copy.deepcopy(v) for k in keys: if k in os.environ and (force or k not in normenv): diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index 14306ab..76a0913 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -1130,7 +1130,7 @@ class ToolInitializer(object): so we no longer copy and re-bind them when the construction environment gets cloned. """ - for method in list(self.methods.values()): + for method in self.methods.values(): env.RemoveMethod(method) def apply_tools(self, env): @@ -1312,6 +1312,8 @@ def tool_list(platform, env): 'tar', 'zip', # File builders (text) 'textfile', + # Python scanner tool + 'python', ], env) tools = ([linker, c_compiler, cxx_compiler, diff --git a/src/engine/SCons/Tool/c++.xml b/src/engine/SCons/Tool/c++.xml index b59816b..019821c 100644 --- a/src/engine/SCons/Tool/c++.xml +++ b/src/engine/SCons/Tool/c++.xml @@ -47,6 +47,7 @@ Sets construction variables for generic POSIX C++ compilers. </sets> <uses> <item>CXXCOMSTR</item> +<item>SHCXXCOMSTR</item> </uses> </tool> @@ -54,6 +55,7 @@ Sets construction variables for generic POSIX C++ compilers. <summary> <para> The C++ compiler. +See also &cv-link-SHCXX; for compiling to shared objects.. </para> </summary> </cvar> @@ -65,6 +67,7 @@ The command line used to compile a C++ source file to an object file. Any options specified in the &cv-link-CXXFLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. +See also &cv-link-SHCXXCOM; for compiling to shared objects.. </para> </summary> </cvar> @@ -72,9 +75,10 @@ are included on this command line. <cvar name="CXXCOMSTR"> <summary> <para> -The string displayed when a C++ source file +If set, the string displayed when a C++ source file is compiled to a (static) object file. -If this is not set, then &cv-link-CXXCOM; (the command line) is displayed. +If not set, then &cv-link-CXXCOM; (the command line) is displayed. +See also &cv-link-SHCXXCOMSTR; for compiling to shared objects.. </para> <example_commands> @@ -91,6 +95,7 @@ By default, this includes the value of &cv-link-CCFLAGS;, so that setting &cv-CCFLAGS; affects both C and C++ compilation. If you want to add C++-specific flags, you must set or override the value of &cv-link-CXXFLAGS;. +See also &cv-link-SHCXXFLAGS; for compiling to shared objects.. </para> </summary> </cvar> @@ -99,6 +104,7 @@ you must set or override the value of &cv-link-CXXFLAGS;. <summary> <para> The C++ compiler used for generating shared-library objects. +See also &cv-link-CXX; for compiling to static objects. </para> </summary> </cvar> @@ -111,6 +117,7 @@ to a shared-library object file. Any options specified in the &cv-link-SHCXXFLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. +See also &cv-link-CXXCOM; for compiling to static objects. </para> </summary> </cvar> @@ -118,9 +125,10 @@ are included on this command line. <cvar name="SHCXXCOMSTR"> <summary> <para> -The string displayed when a C++ source file +If set, the string displayed when a C++ source file is compiled to a shared object file. -If this is not set, then &cv-link-SHCXXCOM; (the command line) is displayed. +If not set, then &cv-link-SHCXXCOM; (the command line) is displayed. +See also &cv-link-CXXCOMSTR; for compiling to static objects. </para> <example_commands> @@ -134,6 +142,7 @@ env = Environment(SHCXXCOMSTR = "Compiling shared object $TARGET") <para> Options that are passed to the C++ compiler to generate shared-library objects. +See also &cv-link-CXXFLAGS; for compiling to static objects. </para> </summary> </cvar> diff --git a/src/engine/SCons/Tool/cc.xml b/src/engine/SCons/Tool/cc.xml index 06e73ff..e47cf2d 100644 --- a/src/engine/SCons/Tool/cc.xml +++ b/src/engine/SCons/Tool/cc.xml @@ -51,6 +51,8 @@ Sets construction variables for generic POSIX C compilers. </sets> <uses> <item>PLATFORM</item> +<item>CCCOMSTR</item> +<item>SHCCCOMSTR</item> </uses> </tool> @@ -67,8 +69,8 @@ The C compiler. <para> The command line used to compile a C source file to a (static) object file. Any options specified in the &cv-link-CFLAGS;, &cv-link-CCFLAGS; and -&cv-link-CPPFLAGS; construction variables are included on this command -line. +&cv-link-CPPFLAGS; construction variables are included on this command line. +See also &cv-link-SHCCCOM; for compiling to shared objects. </para> </summary> </cvar> @@ -76,9 +78,10 @@ line. <cvar name="CCCOMSTR"> <summary> <para> -The string displayed when a C source file +If set, the string displayed when a C source file is compiled to a (static) object file. -If this is not set, then &cv-link-CCCOM; (the command line) is displayed. +If not set, then &cv-link-CCCOM; (the command line) is displayed. +See also &cv-link-SHCCCOMSTR; for compiling to shared objects. </para> <example_commands> @@ -91,6 +94,7 @@ env = Environment(CCCOMSTR = "Compiling static object $TARGET") <summary> <para> General options that are passed to the C and C++ compilers. +See also &cv-link-SHCCFLAGS; for compiling to shared objects. </para> </summary> </cvar> @@ -99,6 +103,7 @@ General options that are passed to the C and C++ compilers. <summary> <para> General options that are passed to the C compiler (C only; not C++). +See also &cv-link-SHCFLAGS; for compiling to shared objects. </para> </summary> </cvar> @@ -156,6 +161,7 @@ The default list is: <summary> <para> The C compiler used for generating shared-library objects. +See also &cv-link-CC; for compiling to static objects. </para> </summary> </cvar> @@ -169,6 +175,7 @@ Any options specified in the &cv-link-SHCFLAGS;, &cv-link-SHCCFLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. +See also &cv-link-CCCOM; for compiling to static objects. </para> </summary> </cvar> @@ -176,9 +183,10 @@ are included on this command line. <cvar name="SHCCCOMSTR"> <summary> <para> -The string displayed when a C source file +If set, the string displayed when a C source file is compiled to a shared object file. -If this is not set, then &cv-link-SHCCCOM; (the command line) is displayed. +If not set, then &cv-link-SHCCCOM; (the command line) is displayed. +See also &cv-link-CCCOMSTR; for compiling to static objects. </para> <example_commands> @@ -192,6 +200,7 @@ env = Environment(SHCCCOMSTR = "Compiling shared object $TARGET") <para> Options that are passed to the C and C++ compilers to generate shared-library objects. +See also &cv-link-CCFLAGS; for compiling to static objects. </para> </summary> </cvar> @@ -201,6 +210,7 @@ to generate shared-library objects. <para> Options that are passed to the C compiler (only; not C++) to generate shared-library objects. +See also &cv-link-CFLAGS; for compiling to static objects. </para> </summary> </cvar> diff --git a/src/engine/SCons/Tool/f03.xml b/src/engine/SCons/Tool/f03.xml index c020b81..61c02ef 100644 --- a/src/engine/SCons/Tool/f03.xml +++ b/src/engine/SCons/Tool/f03.xml @@ -77,9 +77,9 @@ for all Fortran versions. <cvar name="F03COMSTR"> <summary> <para> -The string displayed when a Fortran 03 source file +If set, the string displayed when a Fortran 03 source file is compiled to an object file. -If this is not set, then &cv-link-F03COM; or &cv-link-FORTRANCOM; +If not set, then &cv-link-F03COM; or &cv-link-FORTRANCOM; (the command line) is displayed. </para> </summary> @@ -217,10 +217,10 @@ for all Fortran versions. <cvar name="F03PPCOMSTR"> <summary> <para> -The string displayed when a Fortran 03 source file +If set, the string displayed when a Fortran 03 source file is compiled to an object file after first running the file through the C preprocessor. -If this is not set, then &cv-link-F03PPCOM; or &cv-link-FORTRANPPCOM; +If not set, then &cv-link-F03PPCOM; or &cv-link-FORTRANPPCOM; (the command line) is displayed. </para> </summary> @@ -256,9 +256,9 @@ for all Fortran versions. <cvar name="SHF03COMSTR"> <summary> <para> -The string displayed when a Fortran 03 source file +If set, the string displayed when a Fortran 03 source file is compiled to a shared-library object file. -If this is not set, then &cv-link-SHF03COM; or &cv-link-SHFORTRANCOM; +If not set, then &cv-link-SHF03COM; or &cv-link-SHFORTRANCOM; (the command line) is displayed. </para> </summary> @@ -299,10 +299,10 @@ for all Fortran versions. <cvar name="SHF03PPCOMSTR"> <summary> <para> -The string displayed when a Fortran 03 source file +If set, the string displayed when a Fortran 03 source file is compiled to a shared-library object file after first running the file through the C preprocessor. -If this is not set, then &cv-link-SHF03PPCOM; or &cv-link-SHFORTRANPPCOM; +If not set, then &cv-link-SHF03PPCOM; or &cv-link-SHFORTRANPPCOM; (the command line) is displayed. </para> </summary> diff --git a/src/engine/SCons/Tool/f08.xml b/src/engine/SCons/Tool/f08.xml index 802e4cc..a56d60b 100644 --- a/src/engine/SCons/Tool/f08.xml +++ b/src/engine/SCons/Tool/f08.xml @@ -77,9 +77,9 @@ for all Fortran versions. <cvar name="F08COMSTR"> <summary> <para> -The string displayed when a Fortran 08 source file +If set, the string displayed when a Fortran 08 source file is compiled to an object file. -If this is not set, then &cv-link-F08COM; or &cv-link-FORTRANCOM; +If not set, then &cv-link-F08COM; or &cv-link-FORTRANCOM; (the command line) is displayed. </para> </summary> @@ -217,10 +217,10 @@ for all Fortran versions. <cvar name="F08PPCOMSTR"> <summary> <para> -The string displayed when a Fortran 08 source file +If set, the string displayed when a Fortran 08 source file is compiled to an object file after first running the file through the C preprocessor. -If this is not set, then &cv-link-F08PPCOM; or &cv-link-FORTRANPPCOM; +If not set, then &cv-link-F08PPCOM; or &cv-link-FORTRANPPCOM; (the command line) is displayed. </para> </summary> @@ -256,9 +256,9 @@ for all Fortran versions. <cvar name="SHF08COMSTR"> <summary> <para> -The string displayed when a Fortran 08 source file +If set, the string displayed when a Fortran 08 source file is compiled to a shared-library object file. -If this is not set, then &cv-link-SHF08COM; or &cv-link-SHFORTRANCOM; +If not set, then &cv-link-SHF08COM; or &cv-link-SHFORTRANCOM; (the command line) is displayed. </para> </summary> @@ -299,10 +299,10 @@ for all Fortran versions. <cvar name="SHF08PPCOMSTR"> <summary> <para> -The string displayed when a Fortran 08 source file +If set, the string displayed when a Fortran 08 source file is compiled to a shared-library object file after first running the file through the C preprocessor. -If this is not set, then &cv-link-SHF08PPCOM; or &cv-link-SHFORTRANPPCOM; +If not set, then &cv-link-SHF08PPCOM; or &cv-link-SHFORTRANPPCOM; (the command line) is displayed. </para> </summary> diff --git a/src/engine/SCons/Tool/f77.xml b/src/engine/SCons/Tool/f77.xml index abfc4a2..70ec721 100644 --- a/src/engine/SCons/Tool/f77.xml +++ b/src/engine/SCons/Tool/f77.xml @@ -108,9 +108,9 @@ F77 dialect will be used. By default, this is empty <cvar name="F77COMSTR"> <summary> <para> -The string displayed when a Fortran 77 source file +If set, the string displayed when a Fortran 77 source file is compiled to an object file. -If this is not set, then &cv-link-F77COM; or &cv-link-FORTRANCOM; +If not set, then &cv-link-F77COM; or &cv-link-FORTRANCOM; (the command line) is displayed. </para> </summary> @@ -230,10 +230,10 @@ for all Fortran versions. <cvar name="F77PPCOMSTR"> <summary> <para> -The string displayed when a Fortran 77 source file +If set, the string displayed when a Fortran 77 source file is compiled to an object file after first running the file through the C preprocessor. -If this is not set, then &cv-link-F77PPCOM; or &cv-link-FORTRANPPCOM; +If not set, then &cv-link-F77PPCOM; or &cv-link-FORTRANPPCOM; (the command line) is displayed. </para> </summary> @@ -269,9 +269,9 @@ for all Fortran versions. <cvar name="SHF77COMSTR"> <summary> <para> -The string displayed when a Fortran 77 source file +If set, the string displayed when a Fortran 77 source file is compiled to a shared-library object file. -If this is not set, then &cv-link-SHF77COM; or &cv-link-SHFORTRANCOM; +If not set, then &cv-link-SHF77COM; or &cv-link-SHFORTRANCOM; (the command line) is displayed. </para> </summary> @@ -312,10 +312,10 @@ for all Fortran versions. <cvar name="SHF77PPCOMSTR"> <summary> <para> -The string displayed when a Fortran 77 source file +If set, the string displayed when a Fortran 77 source file is compiled to a shared-library object file after first running the file through the C preprocessor. -If this is not set, then &cv-link-SHF77PPCOM; or &cv-link-SHFORTRANPPCOM; +If not set, then &cv-link-SHF77PPCOM; or &cv-link-SHFORTRANPPCOM; (the command line) is displayed. </para> </summary> diff --git a/src/engine/SCons/Tool/f90.xml b/src/engine/SCons/Tool/f90.xml index 94249a3..64dc6e1 100644 --- a/src/engine/SCons/Tool/f90.xml +++ b/src/engine/SCons/Tool/f90.xml @@ -77,9 +77,9 @@ for all Fortran versions. <cvar name="F90COMSTR"> <summary> <para> -The string displayed when a Fortran 90 source file +If set, the string displayed when a Fortran 90 source file is compiled to an object file. -If this is not set, then &cv-link-F90COM; or &cv-link-FORTRANCOM; +If not set, then &cv-link-F90COM; or &cv-link-FORTRANCOM; (the command line) is displayed. </para> </summary> @@ -217,9 +217,9 @@ for all Fortran versions. <cvar name="F90PPCOMSTR"> <summary> <para> -The string displayed when a Fortran 90 source file +If set, the string displayed when a Fortran 90 source file is compiled after first running the file through the C preprocessor. -If this is not set, then &cv-link-F90PPCOM; or &cv-link-FORTRANPPCOM; +If not set, then &cv-link-F90PPCOM; or &cv-link-FORTRANPPCOM; (the command line) is displayed. </para> </summary> @@ -255,9 +255,9 @@ for all Fortran versions. <cvar name="SHF90COMSTR"> <summary> <para> -The string displayed when a Fortran 90 source file +If set, the string displayed when a Fortran 90 source file is compiled to a shared-library object file. -If this is not set, then &cv-link-SHF90COM; or &cv-link-SHFORTRANCOM; +If not set, then &cv-link-SHF90COM; or &cv-link-SHFORTRANCOM; (the command line) is displayed. </para> </summary> @@ -298,10 +298,10 @@ for all Fortran versions. <cvar name="SHF90PPCOMSTR"> <summary> <para> -The string displayed when a Fortran 90 source file +If set, the string displayed when a Fortran 90 source file is compiled to a shared-library object file after first running the file through the C preprocessor. -If this is not set, then &cv-link-SHF90PPCOM; or &cv-link-SHFORTRANPPCOM; +If not set, then &cv-link-SHF90PPCOM; or &cv-link-SHFORTRANPPCOM; (the command line) is displayed. </para> </summary> diff --git a/src/engine/SCons/Tool/f95.xml b/src/engine/SCons/Tool/f95.xml index 4a4db46..4bda653 100644 --- a/src/engine/SCons/Tool/f95.xml +++ b/src/engine/SCons/Tool/f95.xml @@ -77,9 +77,9 @@ for all Fortran versions. <cvar name="F95COMSTR"> <summary> <para> -The string displayed when a Fortran 95 source file +If set, the string displayed when a Fortran 95 source file is compiled to an object file. -If this is not set, then &cv-link-F95COM; or &cv-link-FORTRANCOM; +If not set, then &cv-link-F95COM; or &cv-link-FORTRANCOM; (the command line) is displayed. </para> </summary> @@ -217,10 +217,10 @@ for all Fortran versions. <cvar name="F95PPCOMSTR"> <summary> <para> -The string displayed when a Fortran 95 source file +If set, the string displayed when a Fortran 95 source file is compiled to an object file after first running the file through the C preprocessor. -If this is not set, then &cv-link-F95PPCOM; or &cv-link-FORTRANPPCOM; +If not set, then &cv-link-F95PPCOM; or &cv-link-FORTRANPPCOM; (the command line) is displayed. </para> </summary> @@ -256,9 +256,9 @@ for all Fortran versions. <cvar name="SHF95COMSTR"> <summary> <para> -The string displayed when a Fortran 95 source file +If set, the string displayed when a Fortran 95 source file is compiled to a shared-library object file. -If this is not set, then &cv-link-SHF95COM; or &cv-link-SHFORTRANCOM; +If not set, then &cv-link-SHF95COM; or &cv-link-SHFORTRANCOM; (the command line) is displayed. </para> </summary> @@ -299,10 +299,10 @@ for all Fortran versions. <cvar name="SHF95PPCOMSTR"> <summary> <para> -The string displayed when a Fortran 95 source file +If set, the string displayed when a Fortran 95 source file is compiled to a shared-library object file after first running the file through the C preprocessor. -If this is not set, then &cv-link-SHF95PPCOM; or &cv-link-SHFORTRANPPCOM; +If not set, then &cv-link-SHF95PPCOM; or &cv-link-SHFORTRANPPCOM; (the command line) is displayed. </para> </summary> diff --git a/src/engine/SCons/Tool/fortran.xml b/src/engine/SCons/Tool/fortran.xml index 53bcfb1..7b3c51f 100644 --- a/src/engine/SCons/Tool/fortran.xml +++ b/src/engine/SCons/Tool/fortran.xml @@ -73,9 +73,9 @@ are included on this command line. <cvar name="FORTRANCOMSTR"> <summary> <para> -The string displayed when a Fortran source file +If set, the string displayed when a Fortran source file is compiled to an object file. -If this is not set, then &cv-link-FORTRANCOM; +If not set, then &cv-link-FORTRANCOM; (the command line) is displayed. </para> </summary> @@ -285,10 +285,10 @@ construction variables are included on this command line. <cvar name="FORTRANPPCOMSTR"> <summary> <para> -The string displayed when a Fortran source file +If set, the string displayed when a Fortran source file is compiled to an object file after first running the file through the C preprocessor. -If this is not set, then &cv-link-FORTRANPPCOM; +If not set, then &cv-link-FORTRANPPCOM; (the command line) is displayed. </para> </summary> @@ -330,9 +330,9 @@ to a shared-library object file. <cvar name="SHFORTRANCOMSTR"> <summary> <para> -The string displayed when a Fortran source file +If set, the string displayed when a Fortran source file is compiled to a shared-library object file. -If this is not set, then &cv-link-SHFORTRANCOM; +If not set, then &cv-link-SHFORTRANCOM; (the command line) is displayed. </para> </summary> @@ -364,10 +364,10 @@ are included on this command line. <cvar name="SHFORTRANPPCOMSTR"> <summary> <para> -The string displayed when a Fortran source file +If set, the string displayed when a Fortran source file is compiled to a shared-library object file after first running the file through the C preprocessor. -If this is not set, then &cv-link-SHFORTRANPPCOM; +If not set, then &cv-link-SHFORTRANPPCOM; (the command line) is displayed. </para> </summary> diff --git a/src/engine/SCons/Tool/install.xml b/src/engine/SCons/Tool/install.xml index 150308b..ce70d91 100644 --- a/src/engine/SCons/Tool/install.xml +++ b/src/engine/SCons/Tool/install.xml @@ -50,10 +50,24 @@ a builder. </para> <example_commands> -env.Install('/usr/local/bin', source = ['foo', 'bar']) +env.Install(target='/usr/local/bin', source=['foo', 'bar']) </example_commands> <para> +Note that if target paths chosen for the +&Install; builder (and the related &InstallAs; and +&InstallVersionedLib; builders) are outside the +project tree, such as in the example above, +they may not be selected for "building" by default, +since in the absence of other instructions +&scons; builds targets that are underneath the top directory +(the directory that contains the &SConstruct; file, +usually the current directory). +Use command line targets or the &Default; function +in this case. +</para> + +<para> If the <option>--install-sandbox</option> command line option is given, the target directory will be prefixed by the directory path specified. @@ -86,12 +100,16 @@ arguments list different numbers of files or directories. </para> <example_commands> -env.InstallAs(target = '/usr/local/bin/foo', - source = 'foo_debug') -env.InstallAs(target = ['../lib/libfoo.a', '../lib/libbar.a'], - source = ['libFOO.a', 'libBAR.a']) +env.InstallAs(target='/usr/local/bin/foo', + source='foo_debug') +env.InstallAs(target=['../lib/libfoo.a', '../lib/libbar.a'], + source=['libFOO.a', 'libBAR.a']) </example_commands> +<para> +See the note under &Install;. +</para> + </summary> </builder> @@ -103,9 +121,14 @@ architecture will be generated based on symlinks of the source library. </para> <example_commands> -env.InstallVersionedLib(target = '/usr/local/bin/foo', - source = 'libxyz.1.5.2.so') +env.InstallVersionedLib(target='/usr/local/bin/foo', + source='libxyz.1.5.2.so') </example_commands> + +<para> +See the note under &Install;. +</para> + </summary> </builder> diff --git a/src/engine/SCons/Tool/intelc.py b/src/engine/SCons/Tool/intelc.py index 5a101b4..778cba1 100644 --- a/src/engine/SCons/Tool/intelc.py +++ b/src/engine/SCons/Tool/intelc.py @@ -495,15 +495,15 @@ def generate(env, version=None, abi=None, topdir=None, verbose=0): 'LIB' : libdir, 'PATH' : bindir, 'LD_LIBRARY_PATH' : libdir} - for p in list(paths.keys()): - env.PrependENVPath(p, os.path.join(topdir, paths[p])) + for p, v in paths.items(): + env.PrependENVPath(p, os.path.join(topdir, v)) if is_mac: paths={'INCLUDE' : 'include', 'LIB' : libdir, 'PATH' : bindir, 'LD_LIBRARY_PATH' : libdir} - for p in list(paths.keys()): - env.PrependENVPath(p, os.path.join(topdir, paths[p])) + for p, v in paths.items(): + env.PrependENVPath(p, os.path.join(topdir, v)) if is_windows: # env key reg valname default subdir of top paths=(('INCLUDE', 'IncludeDir', 'Include'), diff --git a/src/engine/SCons/Tool/javac.xml b/src/engine/SCons/Tool/javac.xml index bc89342..893130b 100644 --- a/src/engine/SCons/Tool/javac.xml +++ b/src/engine/SCons/Tool/javac.xml @@ -95,9 +95,9 @@ See its __doc__ string for a discussion of the format. </para> <example_commands> - env.Java(target = 'classes', source = 'src') - env.Java(target = 'classes', source = ['src1', 'src2']) - env.Java(target = 'classes', source = ['File1.java', 'File2.java']) +env.Java(target = 'classes', source = 'src') +env.Java(target = 'classes', source = ['src1', 'src2']) +env.Java(target = 'classes', source = ['File1.java', 'File2.java']) </example_commands> <para> @@ -114,8 +114,8 @@ See its __doc__ string for a discussion of the format. </para> <example_commands> - env = Environment() - env['ENV']['LANG'] = 'en_GB.UTF-8' +env = Environment() +env['ENV']['LANG'] = 'en_GB.UTF-8' </example_commands> </summary> </builder> @@ -174,7 +174,7 @@ See its __doc__ string for a discussion of the format. </para> <example_commands> - env = Environment(JAVACCOMSTR = "Compiling class files $TARGETS from $SOURCES") +env = Environment(JAVACCOMSTR = "Compiling class files $TARGETS from $SOURCES") </example_commands> </summary> </cvar> diff --git a/src/engine/SCons/Tool/link.xml b/src/engine/SCons/Tool/link.xml index 654dafc..281e147 100644 --- a/src/engine/SCons/Tool/link.xml +++ b/src/engine/SCons/Tool/link.xml @@ -32,9 +32,6 @@ based on the types of source files. </para> </summary> <sets> -<item>SHLINK</item> -<item>SHLINKFLAGS</item> -<item>SHLINKCOM</item> <item>LINK</item> <item>LINKFLAGS</item> <item>LINKCOM</item> @@ -42,6 +39,9 @@ based on the types of source files. <item>LIBDIRSUFFIX</item> <item>LIBLINKPREFIX</item> <item>LIBLINKSUFFIX</item> +<item>SHLINK</item> +<item>SHLINKFLAGS</item> +<item>SHLINKCOM</item> <item>SHLIBSUFFIX</item> <item>__SHLIBVERSIONFLAGS</item> <item>LDMODULE</item> @@ -55,8 +55,8 @@ based on the types of source files. <item>__LDMODULEVERSIONFLAGS</item> </sets> <uses> -<item>SHLINKCOMSTR</item> <item>LINKCOMSTR</item> +<item>SHLINKCOMSTR</item> <item>LDMODULECOMSTR</item> </uses> </tool> @@ -184,8 +184,8 @@ On other systems, this is the same as &cv-link-SHLINK;. <cvar name="LDMODULECOMSTR"> <summary> <para> -The string displayed when building loadable modules. -If this is not set, then &cv-link-LDMODULECOM; (the command line) is displayed. +If set, the string displayed when building loadable modules. +If not set, then &cv-link-LDMODULECOM; (the command line) is displayed. </para> </summary> </cvar> @@ -243,6 +243,7 @@ set. <summary> <para> The linker. +See also &cv-link-SHLINK; for linking shared objects. </para> </summary> </cvar> @@ -251,6 +252,7 @@ The linker. <summary> <para> The command line used to link object files into an executable. +See also &cv-link-SHLINKCOM; for linking shared objects. </para> </summary> </cvar> @@ -258,9 +260,10 @@ The command line used to link object files into an executable. <cvar name="LINKCOMSTR"> <summary> <para> -The string displayed when object files +If set, the string displayed when object files are linked into an executable. -If this is not set, then &cv-link-LINKCOM; (the command line) is displayed. +If not set, then &cv-link-LINKCOM; (the command line) is displayed. +See also &cv-link-SHLINKCOMSTR;. for linking shared objects. </para> <example_commands> @@ -290,6 +293,7 @@ and &cv-link-_LIBDIRFLAGS; above, for the variable that expands to library search path options. +See also &cv-link-SHLINKFLAGS;. for linking shared objects. </para> </summary> </cvar> @@ -317,6 +321,7 @@ set. <summary> <para> The linker for programs that use shared libraries. +See also &cv-link-LINK; for linking static objects. </para> </summary> </cvar> @@ -325,6 +330,7 @@ The linker for programs that use shared libraries. <summary> <para> The command line used to link programs using shared libraries. +See also &cv-link-LINKCOM; for linking static objects. </para> </summary> </cvar> @@ -334,6 +340,7 @@ The command line used to link programs using shared libraries. <para> The string displayed when programs using shared libraries are linked. If this is not set, then &cv-link-SHLINKCOM; (the command line) is displayed. +See also &cv-link-LINKCOMSTR; for linking static objects. </para> <example_commands> @@ -363,6 +370,7 @@ and &cv-link-_LIBDIRFLAGS; above, for the variable that expands to library search path options. +See also &cv-link-LINKFLAGS; for linking static objects. </para> </summary> </cvar> diff --git a/src/engine/SCons/Tool/msvs.py b/src/engine/SCons/Tool/msvs.py index 929e558..9952ccc 100644 --- a/src/engine/SCons/Tool/msvs.py +++ b/src/engine/SCons/Tool/msvs.py @@ -220,7 +220,7 @@ class _UserGenerator(object): for var, src in dbg_settings.items(): # Update only expected keys trg = {} - for key in [k for k in list(self.usrdebg.keys()) if k in src]: + for key in [k for k in self.usrdebg.keys() if k in src]: trg[key] = str(src[key]) self.configs[var].debug = trg @@ -544,7 +544,7 @@ class _DSPGenerator(object): if t[1] in self.env: if SCons.Util.is_List(self.env[t[1]]): for i in self.env[t[1]]: - if not i in self.sources[t[0]]: + if i not in self.sources[t[0]]: self.sources[t[0]].append(i) else: if not self.env[t[1]] in self.sources[t[0]]: @@ -578,11 +578,10 @@ class _DSPGenerator(object): for i in range(len(variants)): AddConfig(self, variants[i], buildtarget[i], outdir[i], runfile[i], cmdargs[i], cppdefines[i], cpppaths[i]) - self.platforms = [] - for key in list(self.configs.keys()): - platform = self.configs[key].platform - if platform not in self.platforms: - self.platforms.append(platform) + seen = set() + self.platforms = [p.platform for p in self.configs.values() + if not (p.platform in seen or seen.add(p.platform))] + def Build(self): pass @@ -702,7 +701,7 @@ class _GenerateV6DSP(_DSPGenerator): 'Resource Files': 'r|rc|ico|cur|bmp|dlg|rc2|rct|bin|cnt|rtf|gif|jpg|jpeg|jpe', 'Other Files': ''} - for kind in sorted(list(categories.keys()), key=lambda a: a.lower()): + for kind in sorted(categories.keys(), key=lambda a: a.lower()): if not self.sources[kind]: continue # skip empty groups @@ -1003,7 +1002,7 @@ class _GenerateV7DSP(_DSPGenerator, _GenerateV7User): self.file.write('\t<Files>\n') - cats = sorted([k for k in list(categories.keys()) if self.sources[k]], + cats = sorted([k for k in categories.keys() if self.sources[k]], key=lambda a: a.lower()) for kind in cats: if len(cats) > 1: @@ -1348,7 +1347,7 @@ class _GenerateV10DSP(_DSPGenerator, _GenerateV10User): 'Resource Files': 'r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe', 'Other Files': ''} - cats = sorted([k for k in list(categories.keys()) if self.sources[k]], + cats = sorted([k for k in categories.keys() if self.sources[k]], key = lambda a: a.lower()) # print vcxproj.filters file first @@ -1505,11 +1504,9 @@ class _GenerateV7DSW(_DSWGenerator): for variant in env['variant']: AddConfig(self, variant) - self.platforms = [] - for key in list(self.configs.keys()): - platform = self.configs[key].platform - if platform not in self.platforms: - self.platforms.append(platform) + seen = set() + self.platforms = [p.platform for p in self.configs.values() + if not (p.platform in seen or seen.add(p.platform))] def GenerateProjectFilesInfo(self): for dspfile in self.dspfiles: diff --git a/src/engine/SCons/Tool/msvsTests.py b/src/engine/SCons/Tool/msvsTests.py index cc4f717..38a100f 100644 --- a/src/engine/SCons/Tool/msvsTests.py +++ b/src/engine/SCons/Tool/msvsTests.py @@ -405,20 +405,20 @@ class DummyEnv(object): return self.dict return self.dict[key] - def __setitem__(self,key,value): + def __setitem__(self, key, value): self.dict[key] = value - def __getitem__(self,key): + def __getitem__(self, key): return self.dict[key] - def __contains__(self,key): + def __contains__(self, key): return key in self.dict - def has_key(self,name): + def has_key(self, name): return name in self.dict def get(self, name, value=None): - if self.has_key(name): + if name in self.dict: return self.dict[name] else: return value @@ -771,8 +771,8 @@ class msvsTestCase(unittest.TestCase): # Check expected result self.assertListEqual(list(genDSP.configs.keys()), list(expected_configs.keys())) - for key in list(genDSP.configs.keys()): - self.assertDictEqual(genDSP.configs[key].__dict__, expected_configs[key]) + for key, v in genDSP.configs.items(): + self.assertDictEqual(v.__dict__, expected_configs[key]) genDSP.Build() diff --git a/src/engine/SCons/Tool/packaging.xml b/src/engine/SCons/Tool/packaging.xml index 8ab4912..55fecec 100644 --- a/src/engine/SCons/Tool/packaging.xml +++ b/src/engine/SCons/Tool/packaging.xml @@ -34,7 +34,7 @@ A framework for building binary and source packages. <builder name="Package"> <summary> <para> -Builds a Binary Package of the given source files. +Builds a Binary Package of the given source files. </para> <example_commands> @@ -43,68 +43,4 @@ env.Package(source = FindInstalledFiles()) </summary> </builder> -<cvar name="JAR"> -<summary> -<para> -The Java archive tool. -</para> -</summary> -</cvar> - -<cvar name="JARCHDIR"> -<summary> -<para> -The directory to which the Java archive tool should change -(using the -<option>-C</option> -option). -</para> -</summary> -</cvar> - -<cvar name="JARCOM"> -<summary> -<para> -The command line used to call the Java archive tool. -</para> -</summary> -</cvar> - -<cvar name="JARCOMSTR"> -<summary> -<para> -The string displayed when the Java archive tool -is called -If this is not set, then &cv-JARCOM; (the command line) is displayed. -</para> - -<example_commands> -env = Environment(JARCOMSTR = "JARchiving $SOURCES into $TARGET") -</example_commands> -</summary> -</cvar> - -<cvar name="JARFLAGS"> -<summary> -<para> -General options passed to the Java archive tool. -By default this is set to -<option>cf</option> -to create the necessary -<command>jar</command> -file. -</para> -</summary> -</cvar> - -<cvar name="JARSUFFIX"> -<summary> -<para> -The suffix for Java archives: -<filename>.jar</filename> -by default. -</para> -</summary> -</cvar> - </sconsdoc> diff --git a/src/engine/SCons/Tool/packaging/__init__.xml b/src/engine/SCons/Tool/packaging/__init__.xml index 31c6eed..66a7aa0 100644 --- a/src/engine/SCons/Tool/packaging/__init__.xml +++ b/src/engine/SCons/Tool/packaging/__init__.xml @@ -62,24 +62,25 @@ option or with the &cv-PACKAGETYPE; construction variable. Currently the following packagers available: </para> -<para> - * msi - Microsoft Installer - * rpm - RPM Package Manger - * ipkg - Itsy Package Management System - * tarbz2 - bzip2 compressed tar - * targz - gzip compressed tar - * tarxz - xz compressed tar - * zip - zip file - * src_tarbz2 - bzip2 compressed tar source - * src_targz - gzip compressed tar source - * src_tarxz - xz compressed tar source - * src_zip - zip file source -</para> +<para><literal>msi</literal> - Microsoft Installer</para> +<para><literal>rpm</literal> - RPM Package Manger</para> +<para><literal>ipkg</literal> - Itsy Package Management System</para> +<para><literal>tarbz2</literal> - bzip2 compressed tar</para> +<para><literal>targz</literal> - gzip compressed tar</para> +<para><literal>tarxz</literal> - xz compressed tar</para> +<para><literal>zip</literal> - zip file</para> +<para><literal>src_tarbz2</literal> - bzip2 compressed tar source</para> +<para><literal>src_targz</literal> - gzip compressed tar source</para> +<para><literal>src_tarxz</literal> - xz compressed tar source</para> +<para><literal>src_zip</literal> - zip file source</para> <para> -An updated list is always available under the "package_type" option when -running "scons --help" on a project that has packaging activated. +An updated list is always available under the +<replaceable>package_type</replaceable> option when +running <command>scons --help</command> +on a project that has packaging activated. </para> + <example_commands> env = Environment(tools=['default', 'packaging']) env.Install('/bin/', 'my_program') @@ -198,20 +199,21 @@ placed if applicable. The default value is "$NAME-$VERSION". Selects the package type to build. Currently these are available: </para> -<para> - * msi - Microsoft Installer - * rpm - Redhat Package Manger - * ipkg - Itsy Package Management System - * tarbz2 - compressed tar - * targz - compressed tar - * zip - zip file - * src_tarbz2 - compressed tar source - * src_targz - compressed tar source - * src_zip - zip file source -</para> +<para><literal>msi</literal> - Microsoft Installer</para> +<para><literal>rpm</literal> - RPM Package Manger</para> +<para><literal>ipkg</literal> - Itsy Package Management System</para> +<para><literal>tarbz2</literal> - bzip2 compressed tar</para> +<para><literal>targz</literal> - gzip compressed tar</para> +<para><literal>tarxz</literal> - xz compressed tar</para> +<para><literal>zip</literal> - zip file</para> +<para><literal>src_tarbz2</literal> - bzip2 compressed tar source</para> +<para><literal>src_targz</literal> - gzip compressed tar source</para> +<para><literal>src_tarxz</literal> - xz compressed tar source</para> +<para><literal>src_zip</literal> - zip file source</para> <para> -This may be overridden with the "package_type" command line option. +This may be overridden with the <option>package_type</option> +command line option. </para> </summary> </cvar> diff --git a/src/engine/SCons/Tool/packaging/ipk.py b/src/engine/SCons/Tool/packaging/ipk.py index fe3f49b..ac8b992 100644 --- a/src/engine/SCons/Tool/packaging/ipk.py +++ b/src/engine/SCons/Tool/packaging/ipk.py @@ -173,7 +173,7 @@ Description: $X_IPK_DESCRIPTION # # close all opened files - for f in list(opened_files.values()): + for f in opened_files.values(): f.close() # call a user specified function diff --git a/src/engine/SCons/Tool/packaging/rpm.py b/src/engine/SCons/Tool/packaging/rpm.py index ebaa701..db8ae24 100644 --- a/src/engine/SCons/Tool/packaging/rpm.py +++ b/src/engine/SCons/Tool/packaging/rpm.py @@ -284,7 +284,7 @@ def build_specfile_filesection(spec, files): for file in files: # build the tagset tags = {} - for k in list(supported_tags.keys()): + for k in supported_tags.keys(): try: v = file.GetTag(k) if v: diff --git a/src/engine/SCons/Tool/python.py b/src/engine/SCons/Tool/python.py new file mode 100644 index 0000000..c61fc8d --- /dev/null +++ b/src/engine/SCons/Tool/python.py @@ -0,0 +1,49 @@ +"""SCons.Tool.python + +Registers the Python scanner for the supported Python source file suffixes. + +""" + +# +# __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.Tool +from SCons.Scanner.Python import PythonScanner, PythonSuffixes + + +def generate(env): + """Hook the python builder and scanner into the environment.""" + for suffix in PythonSuffixes: + SCons.Tool.SourceFileScanner.add_scanner(suffix, PythonScanner) + + +def exists(env): + return True + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/src/engine/SCons/Tool/python.xml b/src/engine/SCons/Tool/python.xml new file mode 100644 index 0000000..e3f7da3 --- /dev/null +++ b/src/engine/SCons/Tool/python.xml @@ -0,0 +1,36 @@ +<?xml version="1.0"?> +<!-- +__COPYRIGHT__ + +This file is processed by the bin/SConsDoc.py module. +See its __doc__ string for a discussion of the format. +--> + +<!DOCTYPE sconsdoc [ +<!ENTITY % scons SYSTEM '../../../../doc/scons.mod'> +%scons; +<!ENTITY % builders-mod SYSTEM '../../../../doc/generated/builders.mod'> +%builders-mod; +<!ENTITY % functions-mod SYSTEM '../../../../doc/generated/functions.mod'> +%functions-mod; +<!ENTITY % tools-mod SYSTEM '../../../../doc/generated/tools.mod'> +%tools-mod; +<!ENTITY % variables-mod SYSTEM '../../../../doc/generated/variables.mod'> +%variables-mod; +]> + +<sconsdoc xmlns="http://www.scons.org/dbxsd/v1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> + +<tool name="python"> +<summary> +<para> +Loads the Python scanner scanner into the invoking environment. When loaded, the scanner will +attempt to find implicit dependencies for any Python source files in the list of sources +provided to an actual that uses this environment. +</para> +</summary> +</tool> + +</sconsdoc> diff --git a/src/engine/SCons/Tool/suncxx.py b/src/engine/SCons/Tool/suncxx.py index c155484..1c35612 100644 --- a/src/engine/SCons/Tool/suncxx.py +++ b/src/engine/SCons/Tool/suncxx.py @@ -39,7 +39,6 @@ import os import re import subprocess -from SCons.Util import PY3 import SCons.Tool.cxx cplusplus = SCons.Tool.cxx # cplusplus = __import__('c++', globals(), locals(), []) @@ -53,10 +52,7 @@ def get_package_info(package_name, pkginfo, pkgchk): except KeyError: version = None pathname = None - try: - from subprocess import DEVNULL # py3k - except ImportError: - DEVNULL = open(os.devnull, 'wb') + from subprocess import DEVNULL try: with open('/var/sadm/install/contents', 'r') as f: @@ -72,16 +68,14 @@ def get_package_info(package_name, pkginfo, pkgchk): try: popen_args = {'stdout': subprocess.PIPE, 'stderr': DEVNULL} - if PY3: - popen_args['universal_newlines'] = True + popen_args['universal_newlines'] = True p = subprocess.Popen([pkginfo, '-l', package_name], **popen_args) except EnvironmentError: pass else: pkginfo_contents = p.communicate()[0] - if not PY3: - pkginfo_contents.decode() + pkginfo_contents.decode() version_re = re.compile(r'^ *VERSION:\s*(.*)$', re.M) version_match = version_re.search(pkginfo_contents) if version_match: @@ -91,16 +85,14 @@ def get_package_info(package_name, pkginfo, pkgchk): try: popen_args = {'stdout': subprocess.PIPE, 'stderr': DEVNULL} - if PY3: - popen_args['universal_newlines'] = True + popen_args['universal_newlines'] = True p = subprocess.Popen([pkgchk, '-l', package_name], **popen_args) except EnvironmentError: pass else: pkgchk_contents = p.communicate()[0] - if not PY3: - pkgchk_contents.decode() + pkgchk_contents.decode() pathname_re = re.compile(r'^Pathname:\s*(.*/bin/CC)$', re.M) pathname_match = pathname_re.search(pkgchk_contents) if pathname_match: diff --git a/src/engine/SCons/Tool/textfile.py b/src/engine/SCons/Tool/textfile.py index 9e2327a..48a2904 100644 --- a/src/engine/SCons/Tool/textfile.py +++ b/src/engine/SCons/Tool/textfile.py @@ -53,13 +53,10 @@ import re from SCons.Node import Node from SCons.Node.Python import Value -from SCons.Util import is_String, is_Sequence, is_Dict, to_bytes, PY3 +from SCons.Util import is_String, is_Sequence, is_Dict, to_bytes -if PY3: - TEXTFILE_FILE_WRITE_MODE = 'w' -else: - TEXTFILE_FILE_WRITE_MODE = 'wb' +TEXTFILE_FILE_WRITE_MODE = 'w' LINESEP = '\n' @@ -126,10 +123,7 @@ def _action(target, source, env): # write the file try: - if SCons.Util.PY3: - target_file = open(target[0].get_path(), TEXTFILE_FILE_WRITE_MODE, newline='') - else: - target_file = open(target[0].get_path(), TEXTFILE_FILE_WRITE_MODE) + target_file = open(target[0].get_path(), TEXTFILE_FILE_WRITE_MODE, newline='') except (OSError, IOError): raise SCons.Errors.UserError("Can't write target file %s" % target[0]) diff --git a/src/engine/SCons/Tool/textfile.xml b/src/engine/SCons/Tool/textfile.xml index 957e18c..878eb9f 100644 --- a/src/engine/SCons/Tool/textfile.xml +++ b/src/engine/SCons/Tool/textfile.xml @@ -59,7 +59,7 @@ see the &b-Substfile; description for details. <para> The prefix and suffix specified by the &cv-TEXTFILEPREFIX; and &cv-TEXTFILESUFFIX; construction variables -(the null string and <filename>.txt</filename> by default, respectively) +(an empty string and <filename>.txt</filename> by default, respectively) are automatically added to the target if they are not already present. Examples: </para> @@ -124,7 +124,7 @@ the suffix is stripped and the remainder is used as the default target name. <para> The prefix and suffix specified by the &cv-SUBSTFILEPREFIX; and &cv-SUBSTFILESUFFIX; construction variables -(the null string by default in both cases) +(an empty string by default in both cases) are automatically added to the target if they are not already present. </para> @@ -218,7 +218,7 @@ lists of tuples are also acceptable. <summary> <para> The prefix used for &b-Substfile; file names, -the null string by default. +an empty string by default. </para> </summary> </cvar> @@ -227,7 +227,7 @@ the null string by default. <summary> <para> The suffix used for &b-Substfile; file names, -the null string by default. +an empty string by default. </para> </summary> </cvar> @@ -236,7 +236,7 @@ the null string by default. <summary> <para> The prefix used for &b-Textfile; file names, -the null string by default. +an empty string by default. </para> </summary> </cvar> diff --git a/src/engine/SCons/Tool/xgettext.py b/src/engine/SCons/Tool/xgettext.py index 11ca32f..7aba08d 100644 --- a/src/engine/SCons/Tool/xgettext.py +++ b/src/engine/SCons/Tool/xgettext.py @@ -70,7 +70,7 @@ class _CmdRunner(object): self.out, self.err = proc.communicate() self.status = proc.wait() if self.err: - sys.stderr.write(SCons.Util.UnicodeType(self.err)) + sys.stderr.write(str(self.err)) return self.status def strfunction(self, target, source, env): diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py index d930dde..130cc5e 100644 --- a/src/engine/SCons/Util.py +++ b/src/engine/SCons/Util.py @@ -39,18 +39,8 @@ PY3 = sys.version_info[0] == 3 PYPY = hasattr(sys, 'pypy_translation_info') -try: - from collections import UserDict, UserList, UserString -except ImportError: - from UserDict import UserDict - from UserList import UserList - from UserString import UserString - -try: - from collections.abc import Iterable, MappingView -except ImportError: - from collections import Iterable - +from collections import UserDict, UserList, UserString +from collections.abc import Iterable, MappingView from collections import OrderedDict # Don't "from types import ..." these because we need to get at the @@ -62,13 +52,6 @@ from collections import OrderedDict MethodType = types.MethodType FunctionType = types.FunctionType -try: - _ = type(unicode) -except NameError: - UnicodeType = str -else: - UnicodeType = unicode - def dictify(keys, values, result={}): for k, v in zip(keys, values): result[k] = v @@ -210,14 +193,16 @@ def get_environment_var(varstr): else: return None + class DisplayEngine(object): print_it = True + def __call__(self, text, append_newline=1): if not self.print_it: return if append_newline: text = text + '\n' try: - sys.stdout.write(UnicodeType(text)) + sys.stdout.write(str(text)) except IOError: # Stdout might be connected to a pipe that has been closed # by now. The most likely reason for the pipe being closed @@ -1582,11 +1567,8 @@ del __revision__ def to_bytes(s): if s is None: return b'None' - if not PY3 and isinstance(s, UnicodeType): - # PY2, must encode unicode - return bytearray(s, 'utf-8') - if isinstance (s, (bytes, bytearray)) or bytes is str: - # Above case not covered here as py2 bytes and strings are the same + if isinstance(s, (bytes, bytearray)): + # if already bytes return. return s return bytes(s, 'utf-8') @@ -1594,9 +1576,9 @@ def to_bytes(s): def to_str(s): if s is None: return 'None' - if bytes is str or is_String(s): + if is_String(s): return s - return str (s, 'utf-8') + return str(s, 'utf-8') def cmp(a, b): diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py index e2dd57f..ee07e61 100644 --- a/src/engine/SCons/UtilTests.py +++ b/src/engine/SCons/UtilTests.py @@ -37,14 +37,6 @@ import SCons.Errors from SCons.Util import * -try: - eval('unicode') -except NameError: - HasUnicode = False -else: - HasUnicode = True - - class OutBuffer(object): def __init__(self): self.buffer = "" @@ -274,8 +266,7 @@ class UtilTestCase(unittest.TestCase): assert not is_Dict([]) assert not is_Dict(()) assert not is_Dict("") - if HasUnicode: - exec ("assert not is_Dict(u'')") + def test_is_List(self): assert is_List([]) @@ -290,13 +281,9 @@ class UtilTestCase(unittest.TestCase): assert not is_List(()) assert not is_List({}) assert not is_List("") - if HasUnicode: - exec ("assert not is_List(u'')") def test_is_String(self): assert is_String("") - if HasUnicode: - exec ("assert is_String(u'')") assert is_String(UserString('')) try: class mystr(str): @@ -321,15 +308,12 @@ class UtilTestCase(unittest.TestCase): assert not is_Tuple([]) assert not is_Tuple({}) assert not is_Tuple("") - if HasUnicode: - exec ("assert not is_Tuple(u'')") def test_to_Bytes(self): """ Test the to_Bytes method""" - if not PY3: - self.assertEqual(to_bytes(UnicodeType('Hello')), - bytearray(u'Hello', 'utf-8'), - "Check that to_bytes creates byte array when presented with unicode string. PY2 only") + self.assertEqual(to_bytes('Hello'), + bytearray('Hello', 'utf-8'), + "Check that to_bytes creates byte array when presented with unicode string.") def test_to_String(self): """Test the to_String() method.""" @@ -352,18 +336,6 @@ class UtilTestCase(unittest.TestCase): assert to_String(s2) == s2, s2 assert to_String(s2) == 'foo', s2 - if HasUnicode: - s3 = UserString(unicode('bar')) - assert to_String(s3) == s3, s3 - assert to_String(s3) == unicode('bar'), s3 - assert isinstance(to_String(s3), unicode), \ - type(to_String(s3)) - - if HasUnicode: - s4 = unicode('baz') - assert to_String(s4) == unicode('baz'), to_String(s4) - assert isinstance(to_String(s4), unicode), \ - type(to_String(s4)) def test_WhereIs(self): test = TestCmd.TestCmd(workdir='') diff --git a/src/engine/SCons/Variables/ListVariableTests.py b/src/engine/SCons/Variables/ListVariableTests.py index ef4832c..8f79e07 100644 --- a/src/engine/SCons/Variables/ListVariableTests.py +++ b/src/engine/SCons/Variables/ListVariableTests.py @@ -42,7 +42,7 @@ class ListVariableTestCase(unittest.TestCase): assert o.help == 'test option help\n (all|none|comma-separated list of names)\n allowed names: one two three', repr(o.help) assert o.default == 'all', o.default assert o.validator is None, o.validator - assert not o.converter is None, o.converter + assert o.converter is not None, o.converter opts = SCons.Variables.Variables() opts.Add(SCons.Variables.ListVariable('test2', 'test2 help', diff --git a/src/engine/SCons/Variables/PackageVariableTests.py b/src/engine/SCons/Variables/PackageVariableTests.py index cda3a4a..a7e6b0d 100644 --- a/src/engine/SCons/Variables/PackageVariableTests.py +++ b/src/engine/SCons/Variables/PackageVariableTests.py @@ -80,10 +80,10 @@ class PackageVariableTestCase(unittest.TestCase): # False when we give it str(False). This assures consistent operation # through a cycle of Variables.Save(<file>) -> Variables(<file>). x = o.converter(str(True)) - assert x == True, "converter returned a string when given str(True)" + assert x, "converter returned a string when given str(True)" x = o.converter(str(False)) - assert x == False, "converter returned a string when given str(False)" + assert not x, "converter returned a string when given str(False)" def test_validator(self): """Test the PackageVariable validator""" diff --git a/src/engine/SCons/cpp.py b/src/engine/SCons/cpp.py index 5b35390..0c20c94 100644 --- a/src/engine/SCons/cpp.py +++ b/src/engine/SCons/cpp.py @@ -89,7 +89,7 @@ del op_list override = { 'if' : 'if(?!n?def)', } -l = [override.get(x, x) for x in list(Table.keys())] +l = [override.get(x, x) for x in Table.keys()] # Turn the list of expressions into one big honkin' regular expression @@ -268,7 +268,7 @@ class PreProcessor(object): d = { 'scons_current_file' : self.scons_current_file } - for op in list(Table.keys()): + for op in Table.keys(): d[op] = getattr(self, 'do_' + op) self.default_table = d diff --git a/src/test_interrupts.py b/src/test_interrupts.py index de18a54..8e1b379 100644 --- a/src/test_interrupts.py +++ b/src/test_interrupts.py @@ -105,7 +105,7 @@ for f in files: indent_list.append( (line_num, match.group('try_or_except') ) ) try_except_lines[match.group('indent')] = indent_list uncaught_this_file = [] - for indent in list(try_except_lines.keys()): + for indent in try_except_lines.keys(): exc_keyboardint_seen = 0 exc_all_seen = 0 for (l,statement) in try_except_lines[indent] + [(-1,indent + 'try')]: diff --git a/test/CacheDir/value_dependencies.py b/test/CacheDir/value_dependencies.py new file mode 100644 index 0000000..7992bef --- /dev/null +++ b/test/CacheDir/value_dependencies.py @@ -0,0 +1,52 @@ +#!/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__" + +""" +Verify that bwuilds with caching work for an action with a Value as a child +in a variety of cases. Specifically: + +1. A source file that depends on a Value. +2. A source directory that depends on a Value. +3. A scanner that returns a Value and a directory that depends on a Value. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.dir_fixture('value_dependencies') +test.subdir('cache') + +# First build, populates the cache +test.run(arguments='.') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CacheDir/value_dependencies/SConstruct b/test/CacheDir/value_dependencies/SConstruct new file mode 100644 index 0000000..7b7e596 --- /dev/null +++ b/test/CacheDir/value_dependencies/SConstruct @@ -0,0 +1,32 @@ +import SCons.Node + +CacheDir('cache') + +def b(target, source, env): + with open(target[0].abspath, 'w') as f: + pass + +def scan(node, env, path): + # Have the node depend on a directory, which depends on an instance of + # SCons.Node.Python.Value. + sample_dir = env.fs.Dir('dir2') + env.Depends(sample_dir, env.Value('c')) + return [sample_dir, env.Value('d'), env.Value(b'\x03\x0F', name='name3')] + +scanner = Scanner(function=scan, node_class=SCons.Node.Node) +builder = Builder(action=b, source_scanner=scanner) + +env = Environment() +env.Append(BUILDERS={'B': builder}) + +# Create a node and a directory that each depend on an instance of +# SCons.Node.Python.Value. +sample_dir = env.fs.Dir('dir1') +env.Depends(sample_dir, + [env.Value('a'), env.Value(b'\x01\x0F', name='name1')]) + +sample_file = env.fs.File('testfile') +env.Depends(sample_file, + [env.Value('b'), env.Value(b'\x02\x0F', name='name2')]) + +env.B(target='File1.out', source=[sample_dir, sample_file]) diff --git a/test/CacheDir/value_dependencies/testfile b/test/CacheDir/value_dependencies/testfile new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/CacheDir/value_dependencies/testfile diff --git a/test/DVIPDF/makeindex.py b/test/DVIPDF/makeindex.py index b4ac2d0..bb44d27 100644 --- a/test/DVIPDF/makeindex.py +++ b/test/DVIPDF/makeindex.py @@ -32,15 +32,15 @@ test = TestSCons.TestSCons() dvipdf = test.where_is('dvipdf') if not dvipdf: - test.skip_test('Could not find dvipdf; skipping test(s).\n') + test.skip_test("Could not find 'dvipdf'; skipping test(s).\n") tex = test.where_is('tex') if not tex: - test.skip_test('Could not find tex; skipping test(s).\n') + test.skip_test("Could not find 'tex'; skipping test(s).\n") latex = test.where_is('latex') if not latex: - test.skip_test('Could not find latex; skipping test(s).\n') + test.skip_test("Could not find 'latex'; skipping test(s).\n") diff --git a/test/DVIPS/DVIPS.py b/test/DVIPS/DVIPS.py index 27e89ba..df7811a 100644 --- a/test/DVIPS/DVIPS.py +++ b/test/DVIPS/DVIPS.py @@ -116,13 +116,13 @@ test.must_match('test4.ps', "This is a .latex test.\n", mode='r') have_latex = test.where_is('latex') if not have_latex: - test.skip_test('Could not find latex; skipping test(s).\n') + test.skip_test("Could not find 'latex'; skipping test(s).\n") dvips = test.where_is('dvips') +if not dvips: + test.skip_test("Could not find 'dvips'; skipping test(s).\n") -if dvips: - - test.write("wrapper.py", """ +test.write("wrapper.py", """ import os import sys cmd = " ".join(sys.argv[1:]) @@ -130,7 +130,7 @@ open('%s', 'a').write("%%s\\n" %% cmd) os.system(cmd) """ % test.workpath('wrapper.out').replace('\\', '\\\\')) - test.write('SConstruct', """ +test.write('SConstruct', """ import os ENV = { 'PATH' : os.environ['PATH'] } foo = Environment(ENV = ENV) @@ -142,41 +142,41 @@ bar.PostScript(target = 'bar2', source = 'bar2.ltx') bar.PostScript(target = 'bar3', source = 'bar3.latex') """ % locals()) - tex = r""" +tex = r""" This is the %s TeX file. \end """ - latex = r""" +latex = r""" \documentclass{letter} \begin{document} This is the %s LaTeX file. \end{document} """ - test.write('foo.tex', tex % 'foo.tex') - test.write('bar1.tex', tex % 'bar1.tex') - test.write('bar2.ltx', latex % 'bar2.ltx') - test.write('bar3.latex', latex % 'bar3.latex') +test.write('foo.tex', tex % 'foo.tex') +test.write('bar1.tex', tex % 'bar1.tex') +test.write('bar2.ltx', latex % 'bar2.ltx') +test.write('bar3.latex', latex % 'bar3.latex') - test.run(arguments = 'foo.dvi', stderr = None) +test.run(arguments = 'foo.dvi', stderr = None) - test.must_not_exist(test.workpath('wrapper.out')) +test.must_not_exist(test.workpath('wrapper.out')) - test.must_exist(test.workpath('foo.dvi')) +test.must_exist(test.workpath('foo.dvi')) - test.run(arguments = 'bar1.ps bar2.ps bar3.ps', stderr = None) +test.run(arguments = 'bar1.ps bar2.ps bar3.ps', stderr = None) - expect = """dvips -o bar1.ps bar1.dvi +expect = """dvips -o bar1.ps bar1.dvi dvips -o bar2.ps bar2.dvi dvips -o bar3.ps bar3.dvi """ - test.must_match('wrapper.out', expect, mode='r') +test.must_match('wrapper.out', expect, mode='r') - test.must_exist(test.workpath('bar1.ps')) - test.must_exist(test.workpath('bar2.ps')) - test.must_exist(test.workpath('bar3.ps')) +test.must_exist(test.workpath('bar1.ps')) +test.must_exist(test.workpath('bar2.ps')) +test.must_exist(test.workpath('bar3.ps')) test.pass_test() diff --git a/test/Dir/Dir.py b/test/Dir/Dir.py index e726b94..42a48f3 100644 --- a/test/Dir/Dir.py +++ b/test/Dir/Dir.py @@ -36,28 +36,34 @@ test = TestSCons.TestSCons() test.write('SConstruct', """ DefaultEnvironment(tools=[]) -env = Environment(tools=[], FOO = 'fff', BAR = 'bbb') +env = Environment(tools=[], FOO='fff', BAR='bbb') print(Dir('ddd')) print(Dir('$FOO')) print(Dir('${BAR}_$BAR')) +rv = Dir(['mmm', 'nnn']) +rv_msg = [node.path for node in rv] +print(rv_msg) print(env.Dir('eee')) print(env.Dir('$FOO')) print(env.Dir('${BAR}_$BAR')) +rv = env.Dir(['ooo', 'ppp']) +rv_msg = [node.path for node in rv] +print(rv_msg) """) -test.run(stdout = test.wrap_stdout(read_str = """\ +test.run(stdout=test.wrap_stdout(read_str="""\ ddd $FOO ${BAR}_$BAR +['mmm', 'nnn'] eee fff bbb_bbb -""", build_str = """\ +['ooo', 'ppp'] +""", build_str="""\ scons: `.' is up to date. """)) - - test.write('SConstruct', """\ DefaultEnvironment(tools=[]) import os @@ -66,7 +72,7 @@ def my_mkdir(target=None, source=None, env=None): MDBuilder = Builder(action=my_mkdir, target_factory=Dir) env = Environment(tools=[]) -env.Append(BUILDERS = {'MD':MDBuilder}) +env.Append(BUILDERS={'MD': MDBuilder}) env.MD(target='sub1', source=['SConstruct']) env.MD(target='sub2', source=['SConstruct'], OVERRIDE='foo') """) diff --git a/test/File.py b/test/File.py index ec148b2..bde4449 100644 --- a/test/File.py +++ b/test/File.py @@ -37,13 +37,19 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """ -env = Environment(FOO = 'fff', BAR = 'bbb') +env = Environment(FOO='fff', BAR='bbb') print(File('ddd')) print(File('$FOO')) print(File('${BAR}_$BAR')) +rv = File(['mmm', 'nnn']) +rv_msg = [node.path for node in rv] +print(rv_msg) print(env.File('eee')) print(env.File('$FOO')) print(env.File('${BAR}_$BAR')) +rv = env.File(['ooo', 'ppp']) +rv_msg = [node.path for node in rv] +print(rv_msg) f1 = env.File('f1') print(f1) f2 = f1.File('f2') @@ -54,16 +60,18 @@ expect = test.wrap_stdout(read_str = """\ ddd $FOO ${BAR}_$BAR +['mmm', 'nnn'] eee fff bbb_bbb +['ooo', 'ppp'] f1 f2 -""", build_str = """\ +""", build_str="""\ scons: `.' is up to date. """) -test.run(stdout = expect) +test.run(stdout=expect) test.pass_test() diff --git a/test/TEX/TEX.py b/test/TEX/TEX.py index 3964eb8..f0d4043 100644 --- a/test/TEX/TEX.py +++ b/test/TEX/TEX.py @@ -38,7 +38,7 @@ test = TestSCons.TestSCons() have_latex = test.where_is('latex') if not have_latex: - test.skip_test('Could not find latex; skipping test(s).\n') + test.skip_test("Could not find 'latex'; skipping test(s).\n") test.write('mytex.py', r""" diff --git a/test/TEX/auxiliaries.py b/test/TEX/auxiliaries.py index e28c212..1e37e7a 100644 --- a/test/TEX/auxiliaries.py +++ b/test/TEX/auxiliaries.py @@ -45,8 +45,8 @@ test = TestSCons.TestSCons() dvips = test.where_is('dvips') latex = test.where_is('latex') -if not dvips or not latex: - test.skip_test("Could not find dvips or latex; skipping test(s).\n") +if not all((dvips, latex)): + test.skip_test("Could not find 'dvips' and/or 'latex'; skipping test(s).\n") test.subdir(['docs']) diff --git a/test/TEX/bibliography.py b/test/TEX/bibliography.py index a8032db..afccf8f 100644 --- a/test/TEX/bibliography.py +++ b/test/TEX/bibliography.py @@ -38,15 +38,15 @@ test = TestSCons.TestSCons() dvips = test.where_is('dvips') if not dvips: - test.skip_test("Could not find dvips; skipping test(s).\n") + test.skip_test("Could not find 'dvips'; skipping test(s).\n") bibtex = test.where_is('bibtex') if not bibtex: - test.skip_test("Could not find bibtex; skipping test(s).\n") + test.skip_test("Could not find 'bibtex'; skipping test(s).\n") have_latex = test.where_is('latex') if not have_latex: - test.skip_test('Could not find latex; skipping test(s).\n') + test.skip_test("Could not find 'latex'; skipping test(s).\n") test.write('SConstruct', """\ diff --git a/test/TEX/bibtex-latex-rerun.py b/test/TEX/bibtex-latex-rerun.py index 300f03b..f0f8c34 100644 --- a/test/TEX/bibtex-latex-rerun.py +++ b/test/TEX/bibtex-latex-rerun.py @@ -39,7 +39,7 @@ test = TestSCons.TestSCons() pdflatex = test.where_is('pdflatex') if not pdflatex: - test.skip_test("Could not find pdflatex; skipping test(s).\n") + test.skip_test("Could not find 'pdflatex'; skipping test(s).\n") test.write(['SConstruct'], """\ import os diff --git a/test/TEX/clean.py b/test/TEX/clean.py index ad828d2..781caa1 100644 --- a/test/TEX/clean.py +++ b/test/TEX/clean.py @@ -36,7 +36,7 @@ test = TestSCons.TestSCons() latex = test.where_is('latex') if not latex: - test.skip_test("Could not find tex or latex; skipping test(s).\n") + test.skip_test("Could not find 'latex'; skipping test(s).\n") comment = os.system('kpsewhich comment.sty') if not comment==0: diff --git a/test/TEX/configure.py b/test/TEX/configure.py index 763f86f..9fb4b3e 100644 --- a/test/TEX/configure.py +++ b/test/TEX/configure.py @@ -40,8 +40,8 @@ test = TestSCons.TestSCons() dvips = test.where_is('dvips') latex = test.where_is('latex') -if not dvips or not latex: - test.skip_test("Could not find dvips or latex; skipping test(s).\n") +if not all((dvips, latex)): + test.skip_test("Could not find 'dvips' and/or 'latex'; skipping test(s).\n") NCR = test.NCR # non-cached rebuild diff --git a/test/TEX/dryrun.py b/test/TEX/dryrun.py index 4265791..90357fc 100644 --- a/test/TEX/dryrun.py +++ b/test/TEX/dryrun.py @@ -39,7 +39,7 @@ test = TestSCons.TestSCons() latex = test.where_is('latex') if not latex: - test.skip_test('could not find latex; skipping test\n') + test.skip_test("could not find 'latex'; skipping test\n") test.write('SConstruct', """ import os diff --git a/test/TEX/glossaries.py b/test/TEX/glossaries.py index 21180a0..cbb6964 100644 --- a/test/TEX/glossaries.py +++ b/test/TEX/glossaries.py @@ -39,7 +39,7 @@ test = TestSCons.TestSCons() latex = test.where_is('latex') if not latex: - test.skip_test("Could not find latex; skipping test(s).\n") + test.skip_test("Could not find 'latex'; skipping test(s).\n") gloss = os.system('kpsewhich glossaries.sty') if not gloss==0: diff --git a/test/TEX/glossary.py b/test/TEX/glossary.py index 0becb40..ef13ca1 100644 --- a/test/TEX/glossary.py +++ b/test/TEX/glossary.py @@ -39,7 +39,7 @@ test = TestSCons.TestSCons() latex = test.where_is('latex') if not latex: - test.skip_test("Could not find latex; skipping test(s).\n") + test.skip_test("Could not find 'latex'; skipping test(s).\n") gloss = os.system('kpsewhich glossary.sty') if not gloss==0: diff --git a/test/TEX/lstinputlisting.py b/test/TEX/lstinputlisting.py index 1d60df7..1f5020b 100644 --- a/test/TEX/lstinputlisting.py +++ b/test/TEX/lstinputlisting.py @@ -39,7 +39,7 @@ test = TestSCons.TestSCons() pdflatex = test.where_is('pdflatex') if not pdflatex: - test.skip_test("Could not find pdflatex; skipping test(s).\n") + test.skip_test("Could not find 'pdflatex'; skipping test(s).\n") listings = os.system('kpsewhich listings.sty') if not listings==0: diff --git a/test/TEX/makeindex.py b/test/TEX/makeindex.py index 960ed68..0b81f31 100644 --- a/test/TEX/makeindex.py +++ b/test/TEX/makeindex.py @@ -38,8 +38,8 @@ test = TestSCons.TestSCons() pdflatex = test.where_is('pdflatex') makeindex = test.where_is('makeindex') -if not pdflatex or not makeindex: - test.skip_test("Could not find pdflatex or makeindex; skipping test(s).\n") +if not all((pdflatex, makeindex)): + test.skip_test("Could not find 'pdflatex' and/or 'makeindex'; skipping test(s).\n") test.write('SConstruct', """\ import os diff --git a/test/TEX/multi-line_include_options.py b/test/TEX/multi-line_include_options.py index bb8a5f2..94466b6 100644 --- a/test/TEX/multi-line_include_options.py +++ b/test/TEX/multi-line_include_options.py @@ -44,7 +44,7 @@ test = TestSCons.TestSCons() latex = test.where_is('latex') if not latex: - test.skip_test("Could not find latex; skipping test(s).\n") + test.skip_test("Could not find 'latex'; skipping test(s).\n") test.write('SConstruct', """\ import os diff --git a/test/TEX/multi-run.py b/test/TEX/multi-run.py index 9de0da4..3c4e901 100644 --- a/test/TEX/multi-run.py +++ b/test/TEX/multi-run.py @@ -38,11 +38,8 @@ test = TestSCons.TestSCons() tex = test.where_is('tex') latex = test.where_is('latex') - -if not latex: - test.skip_test("Could not find latex; skipping test(s).\n") -if not tex and not latex: - test.skip_test("Could not find tex or latex; skipping test(s).\n") +if not all((tex, latex)): + test.skip_test("Could not find 'tex' and/or 'latex'; skipping test(s).\n") test.subdir('work1', 'work2', 'work3', 'work4') diff --git a/test/TEX/newglossary.py b/test/TEX/newglossary.py index faae7d3..5d868a8 100644 --- a/test/TEX/newglossary.py +++ b/test/TEX/newglossary.py @@ -39,7 +39,7 @@ test = TestSCons.TestSCons() latex = test.where_is('latex') if not latex: - test.skip_test("Could not find latex; skipping test(s).\n") + test.skip_test("Could not find 'latex'; skipping test(s).\n") gloss = os.system('kpsewhich glossaries.sty') if not gloss==0: diff --git a/test/TEX/nomencl.py b/test/TEX/nomencl.py index 7afb84b..0eb0b84 100644 --- a/test/TEX/nomencl.py +++ b/test/TEX/nomencl.py @@ -39,7 +39,7 @@ test = TestSCons.TestSCons() latex = test.where_is('latex') if not latex: - test.skip_test("Could not find latex; skipping test(s).\n") + test.skip_test("Could not find 'latex'; skipping test(s).\n") nomencl = os.system('kpsewhich nomencl.sty') if not nomencl==0: diff --git a/test/TEX/recursive_scanner_dependencies_import.py b/test/TEX/recursive_scanner_dependencies_import.py index c8c6569..a7b5e4a 100644 --- a/test/TEX/recursive_scanner_dependencies_import.py +++ b/test/TEX/recursive_scanner_dependencies_import.py @@ -42,7 +42,7 @@ test = TestSCons.TestSCons() pdflatex = test.where_is('pdflatex') if not pdflatex: - test.skip_test("Could not find pdflatex; skipping test(s).\n") + test.skip_test("Could not find 'pdflatex'; skipping test(s).\n") latex_import = os.system('kpsewhich import.sty') if latex_import != 0: diff --git a/test/TEX/recursive_scanner_dependencies_input.py b/test/TEX/recursive_scanner_dependencies_input.py index 5f37bf1..5afcbc2 100644 --- a/test/TEX/recursive_scanner_dependencies_input.py +++ b/test/TEX/recursive_scanner_dependencies_input.py @@ -36,7 +36,7 @@ test = TestSCons.TestSCons() pdflatex = test.where_is('pdflatex') if not pdflatex: - test.skip_test("Could not find pdflatex; skipping test(s).\n") + test.skip_test("Could not find 'pdflatex'; skipping test(s).\n") test.write(['SConstruct'], """\ env = Environment(tools=['pdftex', 'tex']) diff --git a/test/TEX/rename_result.py b/test/TEX/rename_result.py index b06d388..f67e569 100644 --- a/test/TEX/rename_result.py +++ b/test/TEX/rename_result.py @@ -38,7 +38,7 @@ test = TestSCons.TestSCons() latex = test.where_is('latex') if not latex: - test.skip_test('could not find latex; skipping test\n') + test.skip_test("could not find 'latex'; skipping test\n") test.write('SConstruct', """ import os diff --git a/test/TEX/synctex.py b/test/TEX/synctex.py index f07db78..385a173 100644 --- a/test/TEX/synctex.py +++ b/test/TEX/synctex.py @@ -39,7 +39,7 @@ test = TestSCons.TestSCons() latex = test.where_is('latex') if not latex: - test.skip_test("Could not find latex; skipping test(s).\n") + test.skip_test("Could not find 'latex'; skipping test(s).\n") test.write('SConstruct', """\ import os diff --git a/test/TEX/usepackage.py b/test/TEX/usepackage.py index 0bb8c22..66510c2 100644 --- a/test/TEX/usepackage.py +++ b/test/TEX/usepackage.py @@ -39,7 +39,7 @@ test = TestSCons.TestSCons() latex = test.where_is('latex') if not latex: - test.skip_test('could not find latex; skipping test\n') + test.skip_test("could not find 'latex'; skipping test\n") test.write('SConstruct', """ import os diff --git a/test/TEX/variant_dir.py b/test/TEX/variant_dir.py index d81f542..9d05863 100644 --- a/test/TEX/variant_dir.py +++ b/test/TEX/variant_dir.py @@ -36,8 +36,9 @@ import TestSCons test = TestSCons.TestSCons() latex = test.where_is('latex') -if not latex: - test.skip_test("Could not find 'latex'; skipping test.\n") +dvipdf = test.where_is('dvipdf') +if not all((latex, dvipdf)): + test.skip_test("Could not find 'latex' and/or 'dvipdf'; skipping test(s).\n") test.subdir(['docs']) diff --git a/test/TEX/variant_dir_bibunit.py b/test/TEX/variant_dir_bibunit.py index cd3409e..e127a76 100644 --- a/test/TEX/variant_dir_bibunit.py +++ b/test/TEX/variant_dir_bibunit.py @@ -41,8 +41,8 @@ test = TestSCons.TestSCons() latex = test.where_is('pdflatex') bibtex = test.where_is('bibtex') -if not latex or not bibtex: - test.skip_test("Could not find 'latex' or 'bibtex'; skipping test.\n") +if not all((latex, bibtex)): + test.skip_test("Could not find 'latex' and/or 'bibtex'; skipping test.\n") bibunits = os.system('kpsewhich bibunits.sty') if not bibunits==0: diff --git a/test/TEX/variant_dir_dup0.py b/test/TEX/variant_dir_dup0.py index 8f4334f..ea6b51e 100644 --- a/test/TEX/variant_dir_dup0.py +++ b/test/TEX/variant_dir_dup0.py @@ -42,8 +42,8 @@ latex = test.where_is('latex') dvipdf = test.where_is('dvipdf') makeindex = test.where_is('makeindex') bibtex = test.where_is('bibtex') -if not latex or not makeindex or not bibtex or not dvipdf: - test.skip_test("Could not find 'latex', 'makeindex', 'bibtex', or dvipdf; skipping test.\n") +if not all((latex, makeindex, bibtex, dvipdf)): + test.skip_test("Could not find one or more of 'latex', 'makeindex', 'bibtex', or 'dvipdf'; skipping test.\n") test.subdir(['docs']) diff --git a/test/TEX/variant_dir_newglossary.py b/test/TEX/variant_dir_newglossary.py index 1e6ab43..5e4d10d 100644 --- a/test/TEX/variant_dir_newglossary.py +++ b/test/TEX/variant_dir_newglossary.py @@ -39,7 +39,7 @@ test = TestSCons.TestSCons() latex = test.where_is('latex') if not latex: - test.skip_test("Could not find latex; skipping test(s).\n") + test.skip_test("Could not find 'latex'; skipping test(s).\n") gloss = os.system('kpsewhich glossaries.sty') if gloss!=0: diff --git a/test/TEX/variant_dir_style_dup0.py b/test/TEX/variant_dir_style_dup0.py index a9649b0..430b792 100644 --- a/test/TEX/variant_dir_style_dup0.py +++ b/test/TEX/variant_dir_style_dup0.py @@ -45,8 +45,8 @@ latex = test.where_is('latex') dvipdf = test.where_is('dvipdf') makeindex = test.where_is('makeindex') bibtex = test.where_is('bibtex') -if not latex or not makeindex or not bibtex or not dvipdf: - test.skip_test("Could not find 'latex', 'makeindex', 'bibtex', or 'dvipdf'; skipping test.\n") +if not all((latex, makeindex, bibtex, dvipdf)): + test.skip_test("Could not find one or more of 'latex', 'makeindex', 'bibtex', or 'dvipdf'; skipping test.\n") test.subdir(['docs']) diff --git a/test/fixture/python_scanner/curdir_reference/helper.py b/test/fixture/python_scanner/curdir_reference/helper.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/fixture/python_scanner/curdir_reference/helper.py diff --git a/test/fixture/python_scanner/curdir_reference/sconstest.skip b/test/fixture/python_scanner/curdir_reference/sconstest.skip new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/fixture/python_scanner/curdir_reference/sconstest.skip diff --git a/test/fixture/python_scanner/curdir_reference/script.py b/test/fixture/python_scanner/curdir_reference/script.py new file mode 100644 index 0000000..d533863 --- /dev/null +++ b/test/fixture/python_scanner/curdir_reference/script.py @@ -0,0 +1 @@ +from . import helper diff --git a/test/fixture/python_scanner/from_import_simple_package_module1.py b/test/fixture/python_scanner/from_import_simple_package_module1.py new file mode 100644 index 0000000..dcc86de --- /dev/null +++ b/test/fixture/python_scanner/from_import_simple_package_module1.py @@ -0,0 +1 @@ +from simple_package import module1 diff --git a/test/fixture/python_scanner/from_import_simple_package_module1_as.py b/test/fixture/python_scanner/from_import_simple_package_module1_as.py new file mode 100644 index 0000000..51061c6 --- /dev/null +++ b/test/fixture/python_scanner/from_import_simple_package_module1_as.py @@ -0,0 +1 @@ +from simple_package import module1 as m1 diff --git a/test/fixture/python_scanner/from_import_simple_package_modules_no_space.py b/test/fixture/python_scanner/from_import_simple_package_modules_no_space.py new file mode 100644 index 0000000..17b82f8 --- /dev/null +++ b/test/fixture/python_scanner/from_import_simple_package_modules_no_space.py @@ -0,0 +1 @@ +from simple_package import module1,module2 diff --git a/test/fixture/python_scanner/from_import_simple_package_modules_with_space.py b/test/fixture/python_scanner/from_import_simple_package_modules_with_space.py new file mode 100644 index 0000000..169e6f7 --- /dev/null +++ b/test/fixture/python_scanner/from_import_simple_package_modules_with_space.py @@ -0,0 +1 @@ +from simple_package import module1, module2 diff --git a/test/fixture/python_scanner/import_simple_package_module1.py b/test/fixture/python_scanner/import_simple_package_module1.py new file mode 100644 index 0000000..bd85010 --- /dev/null +++ b/test/fixture/python_scanner/import_simple_package_module1.py @@ -0,0 +1 @@ +import simple_package.module1 diff --git a/test/fixture/python_scanner/import_simple_package_module1_as.py b/test/fixture/python_scanner/import_simple_package_module1_as.py new file mode 100644 index 0000000..a706672 --- /dev/null +++ b/test/fixture/python_scanner/import_simple_package_module1_as.py @@ -0,0 +1 @@ +import simple_package.module1 as m1 diff --git a/test/fixture/python_scanner/imports_nested3.py b/test/fixture/python_scanner/imports_nested3.py new file mode 100644 index 0000000..c2929d0 --- /dev/null +++ b/test/fixture/python_scanner/imports_nested3.py @@ -0,0 +1 @@ +import nested1.nested2.nested3 diff --git a/test/fixture/python_scanner/imports_simple_package.py b/test/fixture/python_scanner/imports_simple_package.py new file mode 100644 index 0000000..d974128 --- /dev/null +++ b/test/fixture/python_scanner/imports_simple_package.py @@ -0,0 +1 @@ +import simple_package diff --git a/test/fixture/python_scanner/nested1/__init__.py b/test/fixture/python_scanner/nested1/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/fixture/python_scanner/nested1/__init__.py diff --git a/test/fixture/python_scanner/nested1/module.py b/test/fixture/python_scanner/nested1/module.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/fixture/python_scanner/nested1/module.py diff --git a/test/fixture/python_scanner/nested1/nested2/__init__.py b/test/fixture/python_scanner/nested1/nested2/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/fixture/python_scanner/nested1/nested2/__init__.py diff --git a/test/fixture/python_scanner/nested1/nested2/module.py b/test/fixture/python_scanner/nested1/nested2/module.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/fixture/python_scanner/nested1/nested2/module.py diff --git a/test/fixture/python_scanner/nested1/nested2/nested3/__init__.py b/test/fixture/python_scanner/nested1/nested2/nested3/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/fixture/python_scanner/nested1/nested2/nested3/__init__.py diff --git a/test/fixture/python_scanner/nested1/nested2/nested3/imports_grandparent_module.py b/test/fixture/python_scanner/nested1/nested2/nested3/imports_grandparent_module.py new file mode 100644 index 0000000..06bb7f5 --- /dev/null +++ b/test/fixture/python_scanner/nested1/nested2/nested3/imports_grandparent_module.py @@ -0,0 +1 @@ +from ... import module diff --git a/test/fixture/python_scanner/nested1/nested2/nested3/imports_parent_module.py b/test/fixture/python_scanner/nested1/nested2/nested3/imports_parent_module.py new file mode 100644 index 0000000..be10279 --- /dev/null +++ b/test/fixture/python_scanner/nested1/nested2/nested3/imports_parent_module.py @@ -0,0 +1 @@ +from .. import module diff --git a/test/fixture/python_scanner/nested1/nested2/nested3/imports_parent_then_submodule.py b/test/fixture/python_scanner/nested1/nested2/nested3/imports_parent_then_submodule.py new file mode 100644 index 0000000..39f1a1a --- /dev/null +++ b/test/fixture/python_scanner/nested1/nested2/nested3/imports_parent_then_submodule.py @@ -0,0 +1 @@ +from ...nested2a import module diff --git a/test/fixture/python_scanner/nested1/nested2/nested3/module.py b/test/fixture/python_scanner/nested1/nested2/nested3/module.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/fixture/python_scanner/nested1/nested2/nested3/module.py diff --git a/test/fixture/python_scanner/nested1/nested2/nested3/sconstest.skip b/test/fixture/python_scanner/nested1/nested2/nested3/sconstest.skip new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/fixture/python_scanner/nested1/nested2/nested3/sconstest.skip diff --git a/test/fixture/python_scanner/nested1/nested2/sconstest.skip b/test/fixture/python_scanner/nested1/nested2/sconstest.skip new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/fixture/python_scanner/nested1/nested2/sconstest.skip diff --git a/test/fixture/python_scanner/nested1/nested2a/__init__.py b/test/fixture/python_scanner/nested1/nested2a/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/fixture/python_scanner/nested1/nested2a/__init__.py diff --git a/test/fixture/python_scanner/nested1/nested2a/module.py b/test/fixture/python_scanner/nested1/nested2a/module.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/fixture/python_scanner/nested1/nested2a/module.py diff --git a/test/fixture/python_scanner/nested1/nested2a/sconstest.skip b/test/fixture/python_scanner/nested1/nested2a/sconstest.skip new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/fixture/python_scanner/nested1/nested2a/sconstest.skip diff --git a/test/fixture/python_scanner/nested1/sconstest.skip b/test/fixture/python_scanner/nested1/sconstest.skip new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/fixture/python_scanner/nested1/sconstest.skip diff --git a/test/fixture/python_scanner/sconstest.skip b/test/fixture/python_scanner/sconstest.skip new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/fixture/python_scanner/sconstest.skip diff --git a/test/fixture/python_scanner/simple_package/__init__.py b/test/fixture/python_scanner/simple_package/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/fixture/python_scanner/simple_package/__init__.py diff --git a/test/fixture/python_scanner/simple_package/module1.py b/test/fixture/python_scanner/simple_package/module1.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/fixture/python_scanner/simple_package/module1.py diff --git a/test/fixture/python_scanner/simple_package/module2.py b/test/fixture/python_scanner/simple_package/module2.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/fixture/python_scanner/simple_package/module2.py diff --git a/test/fixture/python_scanner/simple_package/sconstest.skip b/test/fixture/python_scanner/simple_package/sconstest.skip new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/fixture/python_scanner/simple_package/sconstest.skip diff --git a/test/srcchange.py b/test/srcchange.py index c2356b6..3c23737 100644 --- a/test/srcchange.py +++ b/test/srcchange.py @@ -62,7 +62,7 @@ SubRevision = Action(subrevision) env=Environment() content_env=env.Clone() -content_env.Command('revision.in', [], '%(_python_)s getrevision > $TARGET') +content_env.Command('revision.in', [], r'%(_python_)s getrevision > $TARGET') content_env.AlwaysBuild('revision.in') env.Precious('main.c') env.Command('main.c', 'revision.in', SubRevision) diff --git a/testing/framework/test-framework.rst b/testing/framework/test-framework.rst index 24240ac..a0fe861 100644 --- a/testing/framework/test-framework.rst +++ b/testing/framework/test-framework.rst @@ -308,6 +308,9 @@ 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. +Note: fixtures must not be treated by SCons as runnable tests. To exclude +them, see instructions in the above section named "Finding Tests". + Directory Fixtures ################## |