diff options
author | William Deegan <bill@baddogconsulting.com> | 2022-07-19 18:28:59 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-19 18:28:59 (GMT) |
commit | 4e70c9f1dec398e509d6463b77b890351f3a7a92 (patch) | |
tree | e5d923229ffd749eaa31a95db3eb8ea2b2366129 | |
parent | 9bc75d285f813282fd285bc834118925fc4e2ae1 (diff) | |
parent | b16ae6440ad87e47592a2ffb06bd50be3d6a84e5 (diff) | |
download | SCons-4e70c9f1dec398e509d6463b77b890351f3a7a92.zip SCons-4e70c9f1dec398e509d6463b77b890351f3a7a92.tar.gz SCons-4e70c9f1dec398e509d6463b77b890351f3a7a92.tar.bz2 |
Merge pull request #4183 from mwichmann/maint/lex_yacc
Improvements to lex and yacc tools
30 files changed, 665 insertions, 261 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index b516ff2..66d6e6c 100755 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -202,6 +202,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER but was not applied to other dialects, and e2e tests explicitly checked that FORTRANFLAGS did not propagate outside the FORTRAN dialect, so the conclusion is that behavior is intentional (issue #2257) + - Improvements to lex and yacc tools: better documentation of + extra-file options, add test for extra-file behavior. From Zhichang Yu: - Added MSVC_USE_SCRIPT_ARGS variable to pass arguments to MSVC_USE_SCRIPT. diff --git a/SCons/Tool/__init__.py b/SCons/Tool/__init__.py index 8e4a22d..afc579f 100644 --- a/SCons/Tool/__init__.py +++ b/SCons/Tool/__init__.py @@ -36,6 +36,7 @@ tool specifications. import sys import os import importlib.util +from typing import Optional import SCons.Builder import SCons.Errors @@ -829,7 +830,7 @@ def tool_list(platform, env): return [x for x in tools if x] -def find_program_path(env, key_program, default_paths=None, add_path=False) -> str: +def find_program_path(env, key_program, default_paths=None, add_path=False) -> Optional[str]: """ Find the location of a tool using various means. @@ -849,6 +850,8 @@ def find_program_path(env, key_program, default_paths=None, add_path=False) -> s # Then in the OS path path = SCons.Util.WhereIs(key_program) if path: + if add_path: + env.AppendENVPath('PATH', os.path.dirname(path)) return path # Finally, add the defaults and check again. @@ -864,8 +867,7 @@ def find_program_path(env, key_program, default_paths=None, add_path=False) -> s # leave that to the caller, unless add_path is true. env['ENV']['PATH'] = save_path if path and add_path: - bin_dir = os.path.dirname(path) - env.AppendENVPath('PATH', bin_dir) + env.AppendENVPath('PATH', os.path.dirname(path)) return path diff --git a/SCons/Tool/lex.py b/SCons/Tool/lex.py index 96f9bcb..0cf7de7 100644 --- a/SCons/Tool/lex.py +++ b/SCons/Tool/lex.py @@ -23,6 +23,9 @@ """Tool-specific initialization for lex. +This tool should support multiple lex implementations, +but is in actuality biased towards GNU Flex. + There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. @@ -30,14 +33,17 @@ selection method. import os.path import sys +from typing import Optional import SCons.Action import SCons.Tool -import SCons.Util import SCons.Warnings from SCons.Platform.mingw import MINGW_DEFAULT_PATHS from SCons.Platform.cygwin import CYGWIN_DEFAULT_PATHS from SCons.Platform.win32 import CHOCO_DEFAULT_PATH +from SCons.Util import CLVar, to_String + +DEFAULT_PATHS = CHOCO_DEFAULT_PATH + MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS LexAction = SCons.Action.Action("$LEXCOM", "$LEXCOMSTR") @@ -47,20 +53,25 @@ else: BINS = ["flex", "lex"] -def lexEmitter(target, source, env): - sourceBase, sourceExt = os.path.splitext(SCons.Util.to_String(source[0])) +def lexEmitter(target, source, env) -> tuple: + """Adds extra files generated by lex program to target list.""" + sourceBase, sourceExt = os.path.splitext(to_String(source[0])) if sourceExt == ".lm": # If using Objective-C target = [sourceBase + ".m"] # the extension is ".m". - # This emitter essentially tries to add to the target all extra - # files generated by flex. - - # Different options that are used to trigger the creation of extra files. + # With --header-file and ----tables-file, the file to write is defined + # by the option argument. Extract this and include in the list of targets. + # NOTE: a filename passed to the command this way is not modified by SCons, + # and so will be interpreted relative to the project top directory at + # execution time, while the name added to the target list will be + # interpreted relative to the SConscript directory - a possible mismatch. + # + # These are GNU flex-only options. + # TODO: recognize --outfile also? file_gen_options = ["--header-file=", "--tables-file="] - lexflags = env.subst_list("$LEXFLAGS", target=target, source=source) - for option in SCons.Util.CLVar(lexflags): + for option in lexflags[0]: for fileGenOption in file_gen_options: l = len(fileGenOption) if option[:l] == fileGenOption: @@ -68,28 +79,29 @@ def lexEmitter(target, source, env): # file name to the target list. file_name = option[l:].strip() target.append(file_name) + return target, source -def get_lex_path(env, append_paths=False): +def get_lex_path(env, append_paths=False) -> Optional[str]: """ - Find the path to the lex tool, searching several possible names + Returns the path to the lex tool, searching several possible names. - Only called in the Windows case, so the default_path - can be Windows-specific + Only called in the Windows case, so the `default_path` argument to + :func:`find_program_path` can be Windows-specific. - :param env: current construction environment - :param append_paths: if set, add the path to the tool to PATH - :return: path to lex tool, if found + Args: + env: current construction environment + append_paths: if set, add the path to the tool to PATH """ for prog in BINS: bin_path = SCons.Tool.find_program_path( env, prog, - default_paths=CHOCO_DEFAULT_PATH + MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS ) + default_paths=DEFAULT_PATHS, + add_path=append_paths, + ) if bin_path: - if append_paths: - env.AppendENVPath('PATH', os.path.dirname(bin_path)) return bin_path SCons.Warnings.warn( @@ -98,7 +110,7 @@ def get_lex_path(env, append_paths=False): ) -def generate(env): +def generate(env) -> None: """Add Builders and construction variables for lex to an Environment.""" c_file, cxx_file = SCons.Tool.createCFileBuilders(env) @@ -117,21 +129,21 @@ def generate(env): cxx_file.add_action(".ll", LexAction) cxx_file.add_emitter(".ll", lexEmitter) - env["LEXFLAGS"] = SCons.Util.CLVar("") + env["LEXFLAGS"] = CLVar("") if sys.platform == 'win32': # ignore the return - we do not need the full path here _ = get_lex_path(env, append_paths=True) env["LEX"] = env.Detect(BINS) if not env.get("LEXUNISTD"): - env["LEXUNISTD"] = SCons.Util.CLVar("") + env["LEXUNISTD"] = CLVar("") env["LEXCOM"] = "$LEX $LEXUNISTD $LEXFLAGS -t $SOURCES > $TARGET" else: env["LEX"] = env.Detect(BINS) env["LEXCOM"] = "$LEX $LEXFLAGS -t $SOURCES > $TARGET" -def exists(env): +def exists(env) -> Optional[str]: if sys.platform == 'win32': return get_lex_path(env) else: diff --git a/SCons/Tool/lex.xml b/SCons/Tool/lex.xml index 5afb754..8622ced 100644 --- a/SCons/Tool/lex.xml +++ b/SCons/Tool/lex.xml @@ -1,6 +1,28 @@ <?xml version="1.0"?> <!-- -__COPYRIGHT__ + MIT License + + Copyright The SCons Foundation + + 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. + This file is processed by the bin/SConsDoc.py module. See its __doc__ string for a discussion of the format. @@ -37,6 +59,7 @@ Sets construction variables for the &lex; lexical analyser. </sets> <uses> <item>LEXCOMSTR</item> +<item>LEXFLAGS</item> </uses> </tool> @@ -66,7 +89,7 @@ If this is not set, then &cv-link-LEXCOM; (the command line) is displayed. </para> <example_commands> -env = Environment(LEXCOMSTR = "Lex'ing $TARGET from $SOURCES") +env = Environment(LEXCOMSTR="Lex'ing $TARGET from $SOURCES") </example_commands> </summary> </cvar> @@ -75,6 +98,14 @@ env = Environment(LEXCOMSTR = "Lex'ing $TARGET from $SOURCES") <summary> <para> General options passed to the lexical analyzer generator. +In addition to passing the value on during invocation, +the &t-link-lex; tool also examines this &consvar; for options +which cause additional output files to be generated, +and adds those to the target list. +Recognized for this purpose are GNU &flex; options +<option>--header-file=</option> and +<option>--tables-file=</option>; +the output file is named by the option argument. </para> </summary> </cvar> diff --git a/SCons/Tool/yacc.py b/SCons/Tool/yacc.py index 61ad61a..a2fef93 100644 --- a/SCons/Tool/yacc.py +++ b/SCons/Tool/yacc.py @@ -23,6 +23,9 @@ """Tool-specific initialization for yacc. +This tool should support multiple yacc implementations, +but is in actuality biased towards GNU Bison. + There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. @@ -30,14 +33,17 @@ selection method. import os.path import sys +from typing import Optional import SCons.Defaults import SCons.Tool -import SCons.Util import SCons.Warnings from SCons.Platform.mingw import MINGW_DEFAULT_PATHS from SCons.Platform.cygwin import CYGWIN_DEFAULT_PATHS from SCons.Platform.win32 import CHOCO_DEFAULT_PATH +from SCons.Util import CLVar, to_String + +DEFAULT_PATHS = CHOCO_DEFAULT_PATH + MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS YaccAction = SCons.Action.Action("$YACCCOM", "$YACCCOMSTR") @@ -47,10 +53,12 @@ else: BINS = ["bison", "yacc"] -def _yaccEmitter(target, source, env, ysuf, hsuf): - yaccflags = env.subst("$YACCFLAGS", target=target, source=source) - flags = SCons.Util.CLVar(yaccflags) - targetBase, targetExt = os.path.splitext(SCons.Util.to_String(target[0])) +def _yaccEmitter(target, source, env, ysuf, hsuf) -> tuple: + """Adds extra files generated by yacc program to target list.""" + + yaccflags = env.subst_list("$YACCFLAGS", target=target, source=source) + flags = yaccflags[0] + targetBase, targetExt = os.path.splitext(to_String(target[0])) if '.ym' in ysuf: # If using Objective-C target = [targetBase + ".m"] # the extension is ".m". @@ -63,71 +71,76 @@ def _yaccEmitter(target, source, env, ysuf, hsuf): # If -g is specified on the command line, yacc will emit a .vcg # file with the same base name as the .y, .yacc, .ym or .yy file. if "-g" in flags: - base, ext = os.path.splitext(SCons.Util.to_String(source[0])) + base, ext = os.path.splitext(to_String(source[0])) target.append(base + env.subst("$YACCVCGFILESUFFIX")) # If -v is specified yacc will create the output debug file # which is not really source for any process, but should - # be noted and also be cleaned - # Bug #2558 + # be noted and also be cleaned (issue #2558) if "-v" in flags: env.SideEffect(targetBase + '.output', target[0]) env.Clean(target[0], targetBase + '.output') - # With --defines and --graph, the name of the file is totally defined - # in the options. - fileGenOptions = ["--defines=", "--graph="] + # With --defines and --graph, the file to write is defined by the option + # argument. Extract this and include in the list of targets. + # NOTE: a filename passed to the command this way is not modified by SCons, + # and so will be interpreted relative to the project top directory at + # execution time, while the name added to the target list will be + # interpreted relative to the SConscript directory - a possible mismatch. + # + # These are GNU bison-only options. + # Since bison 3.8, --header is the preferred name over --defines + fileGenOptions = ["--defines=", "--header=", "--graph="] for option in flags: for fileGenOption in fileGenOptions: l = len(fileGenOption) if option[:l] == fileGenOption: - # A file generating option is present, so add the file - # name to the list of targets. fileName = option[l:].strip() target.append(fileName) - return (target, source) + return target, source -def yEmitter(target, source, env): +def yEmitter(target, source, env) -> tuple: return _yaccEmitter(target, source, env, ['.y', '.yacc'], '$YACCHFILESUFFIX') -def ymEmitter(target, source, env): +def ymEmitter(target, source, env) -> tuple: return _yaccEmitter(target, source, env, ['.ym'], '$YACCHFILESUFFIX') -def yyEmitter(target, source, env): +def yyEmitter(target, source, env) -> tuple: return _yaccEmitter(target, source, env, ['.yy'], '$YACCHXXFILESUFFIX') -def get_yacc_path(env, append_paths=False): +def get_yacc_path(env, append_paths=False) -> Optional[str]: """ - Find the path to the yacc tool, searching several possible names + Returns the path to the yacc tool, searching several possible names. - Only called in the Windows case, so the default_path - can be Windows-specific + Only called in the Windows case, so the `default_path` argument to + :func:`find_program_path` can be Windows-specific. - :param env: current construction environment - :param append_paths: if set, add the path to the tool to PATH - :return: path to yacc tool, if found + Args: + env: current construction environment + append_paths: if true, add the path to the tool to PATH """ for prog in BINS: bin_path = SCons.Tool.find_program_path( env, prog, - default_paths=CHOCO_DEFAULT_PATH + MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS) + default_paths=DEFAULT_PATHS, + add_path=append_paths, + ) if bin_path: - if append_paths: - env.AppendENVPath('PATH', os.path.dirname(bin_path)) return bin_path + SCons.Warnings.warn( SCons.Warnings.SConsWarning, 'yacc tool requested, but yacc or bison binary not found in ENV PATH' ) -def generate(env): +def generate(env) -> None: """Add Builders and construction variables for yacc to an Environment.""" c_file, cxx_file = SCons.Tool.createCFileBuilders(env) @@ -153,14 +166,14 @@ def generate(env): if 'YACC' not in env: env["YACC"] = env.Detect(BINS) - env['YACCFLAGS'] = SCons.Util.CLVar('') + env['YACCFLAGS'] = CLVar('') env['YACCCOM'] = '$YACC $YACCFLAGS -o $TARGET $SOURCES' env['YACCHFILESUFFIX'] = '.h' env['YACCHXXFILESUFFIX'] = '.hpp' env['YACCVCGFILESUFFIX'] = '.vcg' -def exists(env): +def exists(env) -> Optional[str]: if 'YACC' in env: return env.Detect(env['YACC']) diff --git a/SCons/Tool/yacc.xml b/SCons/Tool/yacc.xml index c8e1bb4..2f06451 100644 --- a/SCons/Tool/yacc.xml +++ b/SCons/Tool/yacc.xml @@ -1,6 +1,28 @@ <?xml version="1.0"?> <!-- -__COPYRIGHT__ + MIT License + + Copyright The SCons Foundation + + 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. + This file is processed by the bin/SConsDoc.py module. See its __doc__ string for a discussion of the format. @@ -39,6 +61,7 @@ Sets construction variables for the &yacc; parse generator. </sets> <uses> <item>YACCCOMSTR</item> +<item>YACCFLAGS</item> </uses> </tool> @@ -68,7 +91,7 @@ If this is not set, then &cv-link-YACCCOM; (the command line) is displayed. </para> <example_commands> -env = Environment(YACCCOMSTR = "Yacc'ing $TARGET from $SOURCES") +env = Environment(YACCCOMSTR="Yacc'ing $TARGET from $SOURCES") </example_commands> </summary> </cvar> @@ -77,11 +100,44 @@ env = Environment(YACCCOMSTR = "Yacc'ing $TARGET from $SOURCES") <summary> <para> General options passed to the parser generator. -If &cv-link-YACCFLAGS; contains a <option>-d</option> option, -SCons assumes that the call will also create a .h file -(if the yacc source file ends in a .y suffix) -or a .hpp file -(if the yacc source file ends in a .yy suffix) +In addition to passing the value on during invocation, +the &t-link-yacc; tool also examines this &consvar; for options +which cause additional output files to be generated, +and adds those to the target list. +</para> + +<para> +If a <option>-d</option> option is present, +&scons; assumes that the call will also create a header file +with the suffix defined by &cv-link-YACCHFILESUFFIX; +if the yacc source file ends in a <filename>.y</filename> suffix, +or a file with the suffix defined by &cv-link-YACCHXXFILESUFFIX; +if the yacc source file ends in a <filename>.yy</filename> suffix. +</para> + +<para> +If a <option>-g</option> option is present, +&scons; assumes that the call will also create a graph file +with the suffix defined by &cv-link-YACCVCGFILESUFFIX;. +</para> + +<para> +If a <option>-v</option> option is present, +&scons; assumes that the call will also create an output debug file +with the suffix <filename>.output</filename>. +</para> + +<para> +Also recognized are GNU &bison; options +<option>--header=</option> and its deprecated synonym +<option>--defines=</option>, +which is similar to +<option>-d</option> +but the output filename is named by the option argument; +and <option>--graph=</option>, +which is similar to +<option>-g</option> +but the output filename is named by the option argument. </para> </summary> </cvar> diff --git a/doc/scons.mod b/doc/scons.mod index 77a7d24..0c1a85e 100644 --- a/doc/scons.mod +++ b/doc/scons.mod @@ -59,7 +59,6 @@ <!ENTITY Cons "<application xmlns='http://www.scons.org/dbxsd/v1.0'>Cons</application>"> <!ENTITY cp "<application xmlns='http://www.scons.org/dbxsd/v1.0'>cp</application>"> <!ENTITY csh "<application xmlns='http://www.scons.org/dbxsd/v1.0'>csh</application>"> -<!ENTITY flex "<application xmlns='http://www.scons.org/dbxsd/v1.0'>flex</application>"> <!ENTITY f77 "<application xmlns='http://www.scons.org/dbxsd/v1.0'>f77</application>"> <!ENTITY f90 "<application xmlns='http://www.scons.org/dbxsd/v1.0'>f90</application>"> <!ENTITY f95 "<application xmlns='http://www.scons.org/dbxsd/v1.0'>f95</application>"> diff --git a/test/LEX/FLEXFLAGS.py b/test/LEX/FLEXFLAGS.py new file mode 100644 index 0000000..0844030 --- /dev/null +++ b/test/LEX/FLEXFLAGS.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python +# +# MIT License +# +# Copyright The SCons Foundation +# +# 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. + +""" +Test that detection of file-writing options in LEXFLAGS works. +""" + +import sysconfig + +import TestSCons +from TestCmd import IS_WINDOWS + +_python_ = TestSCons._python_ +_exe = TestSCons._exe + +test = TestSCons.TestSCons() + +test.subdir('sub') + +test.file_fixture('mylex.py') + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +SConscript("sub/SConscript") +""") + +test.write(['sub', 'SConscript'], f"""\ +import sys + +env = Environment( + LEX=r'{_python_} mylex.py', + LEXFLAGS='-x --header-file=header.h --tables-file=tables.t', + tools=['default', 'lex'], +) +targs = env.CFile(target='aaa', source='aaa.l') +t = [str(target) for target in targs] +# fail ourselves if the two extra files were not detected +if not all((len(t) == 3, "header.h" in t, "tables.t" in t)): + sys.exit(1) +""") +test.write(['sub', 'aaa.l'], "aaa.l\nLEXFLAGS\n") + +test.run('.', stderr=None) + +lexflags = ' -x --header-file=header.h --tables-file=tables.t -t' +if IS_WINDOWS and not sysconfig.get_platform() in ("mingw",): + lexflags = ' --nounistd' + lexflags +# Read in with mode='r' because mylex.py implicitly wrote to stdout +# with mode='w'. +test.must_match(['sub', 'aaa.c'], "aaa.l\n%s\n" % lexflags, mode='r') + +# NOTE: this behavior is "wrong" but we're keeping it for compat. +# the generated files should go into 'sub'. +test.must_match(['header.h'], 'lex header\n') +test.must_match(['tables.t'], 'lex table\n') + +# To confirm the files from the file-output options were tracked, +# do a clean and make sure they got removed. As noted, they currently +# don't go into the tracked location, so using the the SConscript check instead. +#test.run(arguments='-c .') +#test.must_not_exist(test.workpath(['sub', 'header.h'])) +#test.must_not_exist(test.workpath(['sub', 'tables.t'])) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/LEX/LEX.py b/test/LEX/LEX.py index 7dc8436..3eb7960 100644 --- a/test/LEX/LEX.py +++ b/test/LEX/LEX.py @@ -33,6 +33,7 @@ test = TestSCons.TestSCons() test.file_fixture('mylex.py') test.write('SConstruct', """ +DefaultEnvironment(tools=[]) env = Environment(LEX=r'%(_python_)s mylex.py', tools=['default', 'lex']) env.CFile(target='aaa', source='aaa.l') env.CFile(target='bbb', source='bbb.lex') diff --git a/test/LEX/LEXCOM.py b/test/LEX/LEXCOM.py index 75f21b7..d4ed91b 100644 --- a/test/LEX/LEXCOM.py +++ b/test/LEX/LEXCOM.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,9 +22,6 @@ # 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__" """ Test the ability to configure the $LEXCOM construction variable. @@ -37,16 +36,19 @@ test = TestSCons.TestSCons() test.file_fixture('mycompile.py') test.write('SConstruct', """ -env = Environment(tools=['default', 'lex'], - LEXCOM = r'%(_python_)s mycompile.py lex $TARGET $SOURCES') -env.CFile(target = 'aaa', source = 'aaa.l') -env.CFile(target = 'bbb', source = 'bbb.lex') +DefaultEnvironment(tools=[]) +env = Environment( + tools=['default', 'lex'], + LEXCOM=r'%(_python_)s mycompile.py lex $TARGET $SOURCES', +) +env.CFile(target='aaa', source='aaa.l') +env.CFile(target='bbb', source='bbb.lex') """ % locals()) test.write('aaa.l', "aaa.l\n/*lex*/\n") test.write('bbb.lex', "bbb.lex\n/*lex*/\n") -test.run(arguments = '.') +test.run(arguments='.') test.must_match('aaa.c', "aaa.l\n") test.must_match('bbb.c', "bbb.lex\n") diff --git a/test/LEX/LEXCOMSTR.py b/test/LEX/LEXCOMSTR.py index 2130d60..b888db8 100644 --- a/test/LEX/LEXCOMSTR.py +++ b/test/LEX/LEXCOMSTR.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,9 +22,6 @@ # 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__" """ Test that the $LEXCOMSTR construction variable allows you to customize @@ -38,17 +37,20 @@ test = TestSCons.TestSCons() test.file_fixture('mycompile.py') test.write('SConstruct', """ -env = Environment(tools=['default', 'lex'], - LEXCOM = r'%(_python_)s mycompile.py lex $TARGET $SOURCES', - LEXCOMSTR = 'Lexing $TARGET from $SOURCE') -env.CFile(target = 'aaa', source = 'aaa.l') -env.CFile(target = 'bbb', source = 'bbb.lex') +DefaultEnvironment(tools=[]) +env = Environment( + tools=['default', 'lex'], + LEXCOM=r'%(_python_)s mycompile.py lex $TARGET $SOURCES', + LEXCOMSTR='Lexing $TARGET from $SOURCE', +) +env.CFile(target='aaa', source='aaa.l') +env.CFile(target='bbb', source='bbb.lex') """ % locals()) test.write('aaa.l', "aaa.l\n/*lex*/\n") test.write('bbb.lex', "bbb.lex\n/*lex*/\n") -test.run(stdout = test.wrap_stdout("""\ +test.run(stdout=test.wrap_stdout("""\ Lexing aaa.c from aaa.l Lexing bbb.c from bbb.lex """)) diff --git a/test/LEX/LEXFLAGS.py b/test/LEX/LEXFLAGS.py index f7d16c1..9cb16ab 100644 --- a/test/LEX/LEXFLAGS.py +++ b/test/LEX/LEXFLAGS.py @@ -27,6 +27,7 @@ import sys import sysconfig import TestSCons +from TestCmd import IS_WINDOWS _python_ = TestSCons._python_ _exe = TestSCons._exe @@ -37,7 +38,8 @@ test.subdir('in') test.file_fixture('mylex.py') -test.write('SConstruct', """ +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) env = Environment( LEX=r'%(_python_)s mylex.py', LEXFLAGS='-x -I${TARGET.dir} -I${SOURCE.dir}', @@ -48,12 +50,12 @@ env.CFile(target='out/aaa', source='in/aaa.l') test.write(['in', 'aaa.l'], "aaa.l\nLEXFLAGS\nI_ARGS\n") -test.run('.', stderr = None) +test.run('.', stderr=None) lexflags = ' -x -t' -if sys.platform == 'win32' and not sysconfig.get_platform() in ("mingw",): +if IS_WINDOWS and not sysconfig.get_platform() in ("mingw",): lexflags = ' --nounistd' + lexflags -# Read in with mode='r' because mylex.py implicitley wrote to stdout +# Read in with mode='r' because mylex.py implicitly wrote to stdout # with mode='w'. test.must_match(['out', 'aaa.c'], "aaa.l\n%s\n out in\n" % lexflags, mode='r') diff --git a/test/LEX/lex_headerfile.py b/test/LEX/lex_headerfile.py index c2e2e8b..7ff035a 100644 --- a/test/LEX/lex_headerfile.py +++ b/test/LEX/lex_headerfile.py @@ -31,6 +31,10 @@ import TestSCons test = TestSCons.TestSCons() +lex = test.where_is('win_flex') or test.where_is('lex') or test.where_is('flex') +if not lex: + test.skip_test('No lex or flex found; skipping test.\n') + test.dir_fixture('lex_headerfile') test.run(chdir='spaced path', arguments='.') diff --git a/test/LEX/live.py b/test/LEX/live.py index 91a2d49..d223043 100644 --- a/test/LEX/live.py +++ b/test/LEX/live.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,9 +22,6 @@ # 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__" """ Test LEX and LEXFLAGS with a live lex. @@ -36,19 +35,18 @@ _python_ = TestSCons._python_ test = TestSCons.TestSCons() lex = test.where_is('win_flex') or test.where_is('lex') or test.where_is('flex') - if not lex: test.skip_test('No lex or flex found; skipping test.\n') test.file_fixture('wrapper.py') test.write('SConstruct', """ +DefaultEnvironment(tools=[]) foo = Environment() lex = foo.Dictionary('LEX') -bar = Environment(LEX = r'%(_python_)s wrapper.py ' + lex, - LEXFLAGS = '-b') -foo.Program(target = 'foo', source = 'foo.l') -bar.Program(target = 'bar', source = 'bar.l') +bar = Environment(LEX=r'%(_python_)s wrapper.py ' + lex, LEXFLAGS='-b') +foo.Program(target='foo', source='foo.l') +bar.Program(target='bar', source='bar.l') """ % locals()) lex = r""" @@ -70,22 +68,18 @@ main() """ test.write('foo.l', lex % ('foo.l', 'foo.l')) - test.write('bar.l', lex % ('bar.l', 'bar.l')) -test.run(arguments = 'foo' + _exe, stderr = None) - +test.run(arguments='foo' + _exe, stderr=None) test.must_not_exist(test.workpath('wrapper.out')) test.must_not_exist(test.workpath('lex.backup')) -test.run(program = test.workpath('foo'), stdin = "a\n", stdout = "Afoo.lA\n") - -test.run(arguments = 'bar' + _exe) - +test.run(program=test.workpath('foo'), stdin="a\n", stdout="Afoo.lA\n") +test.run(arguments='bar' + _exe) test.must_match(test.workpath('wrapper.out'), "wrapper.py\n") test.must_exist(test.workpath('lex.backup')) -test.run(program = test.workpath('bar'), stdin = "b\n", stdout = "Bbar.lB\n") +test.run(program=test.workpath('bar'), stdin="b\n", stdout="Bbar.lB\n") test.pass_test() diff --git a/test/LEX/live_mingw.py b/test/LEX/live_mingw.py index 5c64eb8..6c865e8 100644 --- a/test/LEX/live_mingw.py +++ b/test/LEX/live_mingw.py @@ -30,13 +30,14 @@ Test LEX and LEXFLAGS and unistd.h with a live lex in mingw environment. import sys import TestSCons +from TestCmd import IS_WINDOWS _exe = TestSCons._exe _python_ = TestSCons._python_ test = TestSCons.TestSCons() -if sys.platform != 'win32': +if not IS_WINDOWS: test.skip_test('Not windows environment; skipping test.\n') if not test.where_is('gcc'): @@ -50,6 +51,7 @@ if not lex: test.file_fixture('wrapper.py') test.write('SConstruct', """ +DefaultEnvironment(tools=[]) foo = Environment(tools=['default', 'mingw', 'lex'], LEXUNISTD="") lex = foo.Dictionary('LEX') bar = Environment( diff --git a/test/LEX/no_lex.py b/test/LEX/no_lex.py index 89ffdc7..e53b02e 100644 --- a/test/LEX/no_lex.py +++ b/test/LEX/no_lex.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,9 +22,6 @@ # 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__" """ Test Environments are functional and return None when no lex tool is found. @@ -35,7 +34,7 @@ test = TestSCons.TestSCons() test.write('SConstruct', """ import SCons -def no_lex(env, key_program, default_paths=[]): +def no_lex(env, key_program, default_paths=[], add_path=False): return None class TestEnvironment(SCons.Environment.Environment): @@ -44,11 +43,12 @@ class TestEnvironment(SCons.Environment.Environment): SCons.Tool.find_program_path = no_lex +DefaultEnvironment(tools=[]) foo = TestEnvironment(tools=['default', 'lex']) print(foo.Dictionary('LEX')) """ % locals()) -test.run(arguments = '-Q -s', stdout = 'None\n' ) +test.run(arguments='-Q -s', stdout='None\n') test.pass_test() diff --git a/test/YACC/BISONFLAGS.py b/test/YACC/BISONFLAGS.py new file mode 100644 index 0000000..f789339 --- /dev/null +++ b/test/YACC/BISONFLAGS.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python +# +# MIT License +# +# Copyright The SCons Foundation +# +# 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. + +""" +Test that detection of file-writing options in YACCFLAGS works. +""" + +import TestSCons +from TestCmd import IS_WINDOWS + +_python_ = TestSCons._python_ +_exe = TestSCons._exe + +if IS_WINDOWS: + compiler = 'msvc' + linker = 'mslink' +else: + compiler = 'gcc' + linker = 'gnulink' + +test = TestSCons.TestSCons() + +test.subdir('sub') + +test.dir_fixture('YACCFLAGS-fixture') + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +SConscript("sub/SConscript") +""") + +test.write(['sub', 'SConscript'], """\ +import sys + +env = Environment( + YACC=r'%(_python_)s myyacc.py', + YACCFLAGS='-x --header=header.h --graph=graph.g', + tools=['yacc', '%(linker)s', '%(compiler)s'], +) +targs = env.CFile(target='aaa', source='aaa.y') +t = [str(target) for target in targs] +# fail ourselves if the two extra files were not detected +if not all((len(t) == 3, "header.h" in t, "graph.g" in t)): + sys.exit(1) +""" % locals()) +test.write(['sub', 'aaa.y'], "aaa.y\nYACCFLAGS\n") +test.run('.', stderr=None) +test.must_match(['sub', 'aaa.c'], "aaa.y\n -x --header=header.h --graph=graph.g\n") + +# NOTE: this behavior is "wrong" but we're keeping it for compat: +# the generated files should go into 'sub'. +test.must_match(['header.h'], 'yacc header\n') +test.must_match(['graph.g'], 'yacc graph\n') + +# To confirm the files from the file-output options were tracked, +# do a clean and make sure they got removed. As noted, they currently +# don't go into the tracked location, so using the the SConscript check instead. +#test.run(arguments='-c .') +#test.must_not_exist(test.workpath(['sub', 'header.h'])) +#test.must_not_exist(test.workpath(['sub', 'graph.g'])) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/YACC/YACC-fixture/myyacc.py b/test/YACC/YACC-fixture/myyacc.py index d0ab95e..77f80ea 100644 --- a/test/YACC/YACC-fixture/myyacc.py +++ b/test/YACC/YACC-fixture/myyacc.py @@ -1,13 +1,19 @@ import getopt import sys + cmd_opts, args = getopt.getopt(sys.argv[1:], 'o:', []) opt_string = '' for opt, arg in cmd_opts: - if opt == '-o': out = arg - else: opt_string = opt_string + ' ' + opt + if opt == '-o': + out = arg + else: + opt_string = opt_string + ' ' + opt + with open(out, 'w') as ofp: for a in args: with open(a, 'r') as ifp: contents = ifp.read() - ofp.write(contents.replace('YACC', 'myyacc.py')) + contents = contents.replace('YACC', 'myyacc.py') + ofp.write(contents) + sys.exit(0) diff --git a/test/YACC/YACC.py b/test/YACC/YACC.py index d4cb40e..a5b290c 100644 --- a/test/YACC/YACC.py +++ b/test/YACC/YACC.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,19 +22,17 @@ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import os import sys +from TestCmd import IS_WINDOWS import TestSCons _python_ = TestSCons._python_ _exe = TestSCons._exe -if sys.platform == 'win32': +if IS_WINDOWS: compiler = 'msvc' linker = 'mslink' else: @@ -44,22 +44,26 @@ test = TestSCons.TestSCons() test.dir_fixture('YACC-fixture') test.write('SConstruct', """ -env = Environment(YACC = r'%(_python_)s myyacc.py', tools=['default', 'yacc']) -env.CFile(target = 'aaa', source = 'aaa.y') -env.CFile(target = 'bbb', source = 'bbb.yacc') -env.CXXFile(target = 'ccc', source = 'ccc.yy') -env.CFile(target = 'ddd', source = 'ddd.ym') +DefaultEnvironment(tools=[]) +env = Environment(YACC=r'%(_python_)s myyacc.py', tools=['default', 'yacc']) +env.CFile(target='aaa', source='aaa.y') +env.CFile(target='bbb', source='bbb.yacc') +env.CXXFile(target='ccc', source='ccc.yy') +env.CFile(target='ddd', source='ddd.ym') """ % locals()) -test.run(arguments = '.', stderr = None) +test.run(arguments='.', stderr=None) -test.must_match('aaa.c', "aaa.y" + os.linesep + "myyacc.py" + os.linesep) -test.must_match('bbb.c', "bbb.yacc" + os.linesep + "myyacc.py" + os.linesep) -test.must_match('ccc.cc', "ccc.yacc" + os.linesep + "myyacc.py" + os.linesep) -test.must_match('ddd.m', "ddd.yacc" + os.linesep + "myyacc.py" + os.linesep) +test.must_match('aaa.c', "aaa.y" + os.linesep + "myyacc.py" + os.linesep) +test.must_match('bbb.c', "bbb.yacc" + os.linesep + "myyacc.py" + os.linesep) +test.must_match('ccc.cc', "ccc.yacc" + os.linesep + "myyacc.py" + os.linesep) +test.must_match('ddd.m', "ddd.yacc" + os.linesep + "myyacc.py" + os.linesep) test.run(arguments="-n -f SConstruct_YACC_before") -test.fail_test('SOMETHING_DUMB' not in test.stdout(), "YACC is not overridden to be SOMETHING_DUMB") +test.fail_test( + 'SOMETHING_DUMB' not in test.stdout(), + "YACC is not overridden to be SOMETHING_DUMB" +) test.pass_test() diff --git a/test/YACC/YACCCOM.py b/test/YACC/YACCCOM.py index e9fb47f..cc30ca7 100644 --- a/test/YACC/YACCCOM.py +++ b/test/YACC/YACCCOM.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,9 +22,6 @@ # 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__" """ Test the ability to configure the $YACCCOM construction variable. @@ -37,16 +36,19 @@ test = TestSCons.TestSCons() test.file_fixture('mycompile.py') test.write('SConstruct', """ -env = Environment(tools=['default', 'yacc'], - YACCCOM = r'%(_python_)s mycompile.py yacc $TARGET $SOURCES') -env.CFile(target = 'aaa', source = 'aaa.y') -env.CFile(target = 'bbb', source = 'bbb.yacc') +DefaultEnvironment(tools=[]) +env = Environment( + tools=['default', 'yacc'], + YACCCOM=r'%(_python_)s mycompile.py yacc $TARGET $SOURCES', +) +env.CFile(target='aaa', source='aaa.y') +env.CFile(target='bbb', source='bbb.yacc') """ % locals()) test.write('aaa.y', 'aaa.y\n/*yacc*/\n') test.write('bbb.yacc', 'bbb.yacc\n/*yacc*/\n') -test.run(arguments = '.') +test.run(arguments='.') test.must_match('aaa.c', "aaa.y\n") test.must_match('bbb.c', "bbb.yacc\n") diff --git a/test/YACC/YACCCOMSTR.py b/test/YACC/YACCCOMSTR.py index bded560..de26025 100644 --- a/test/YACC/YACCCOMSTR.py +++ b/test/YACC/YACCCOMSTR.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,9 +22,6 @@ # 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__" """ Test that the $YACCCOMSTR construction variable allows you to customize @@ -38,17 +37,20 @@ test = TestSCons.TestSCons() test.file_fixture('mycompile.py') test.write('SConstruct', """ -env = Environment(tools=['default', 'yacc'], - YACCCOM = r'%(_python_)s mycompile.py yacc $TARGET $SOURCES', - YACCCOMSTR = 'Yaccing $TARGET from $SOURCE') -env.CFile(target = 'aaa', source = 'aaa.y') -env.CFile(target = 'bbb', source = 'bbb.yacc') +DefaultEnvironment(tools=[]) +env = Environment( + tools=['default', 'yacc'], + YACCCOM=r'%(_python_)s mycompile.py yacc $TARGET $SOURCES', + YACCCOMSTR='Yaccing $TARGET from $SOURCE', +) +env.CFile(target='aaa', source='aaa.y') +env.CFile(target='bbb', source='bbb.yacc') """ % locals()) test.write('aaa.y', 'aaa.y\n/*yacc*/\n') test.write('bbb.yacc', 'bbb.yacc\n/*yacc*/\n') -test.run(stdout = test.wrap_stdout("""\ +test.run(stdout=test.wrap_stdout("""\ Yaccing aaa.c from aaa.y Yaccing bbb.c from bbb.yacc """)) diff --git a/test/YACC/YACCFLAGS-fixture/myyacc.py b/test/YACC/YACCFLAGS-fixture/myyacc.py index 43024f1..afab95a 100644 --- a/test/YACC/YACCFLAGS-fixture/myyacc.py +++ b/test/YACC/YACCFLAGS-fixture/myyacc.py @@ -1,17 +1,65 @@ import getopt import sys -cmd_opts, args = getopt.getopt(sys.argv[1:], 'o:I:x', []) -opt_string = '' -i_arguments = '' -for opt, arg in cmd_opts: - if opt == '-o': out = arg - elif opt == '-I': i_arguments = i_arguments + ' ' + arg - else: opt_string = opt_string + ' ' + opt -with open(out, 'wb') as ofp: - for a in args: - with open(a, 'rb') as ifp: - contents = ifp.read() - contents = contents.replace(b'YACCFLAGS', opt_string.encode()) - contents = contents.replace(b'I_ARGS', i_arguments.encode()) - ofp.write(contents) -sys.exit(0) +from pathlib import Path + + +def make_side_effect(path, text): + p = Path(path) + if str(p.parent) != '.': + p.mkdir(parents=True, exist_ok=True) + with p.open(mode="wb") as f: + f.write(text) + + +def fake_yacc(): + make_header = None + make_graph = None + + longopts = ["defines=", "header=", "graph="] + cmd_opts, args = getopt.getopt(sys.argv[1:], 'o:I:x', longopts) + opt_string = '' + i_arguments = '' + + for opt, arg in cmd_opts: + if opt == '-o': + out = arg + elif opt == '-I': + i_arguments = f'{i_arguments} {arg}' + elif opt in ('--defines', '--header'): + make_header = arg + opt_string = f'{opt_string} {opt}={arg}' + elif opt == '--graph': + make_graph = arg + opt_string = f'{opt_string} {opt}={arg}' + else: + opt_string = f'{opt_string} {opt}' + + with open(out, 'wb') as ofp: + for a in args: + with open(a, 'rb') as ifp: + contents = ifp.read() + contents = contents.replace(b'YACCFLAGS', opt_string.encode()) + contents = contents.replace(b'YACC', b'myyacc.py') + contents = contents.replace(b'I_ARGS', i_arguments.encode()) + ofp.write(contents) + + # Extra bits: + if make_header: + make_side_effect(make_header, b"yacc header\n") + if make_graph: + make_side_effect(make_graph, b"yacc graph\n") + + +if __name__ == '__main__': + fake_yacc() + sys.exit(0) + +# If -d is specified on the command line, yacc will emit a .h +# or .hpp file with the same name as the .c or .cpp output file. + +# If -g is specified on the command line, yacc will emit a .vcg +# file with the same base name as the .y, .yacc, .ym or .yy file. + +# If -v is specified yacc will create the output debug file +# which is not really source for any process, but should +# be noted and also be cleaned (issue #2558) diff --git a/test/YACC/YACCFLAGS.py b/test/YACC/YACCFLAGS.py index 1ab8c0d..1f3ad78 100644 --- a/test/YACC/YACCFLAGS.py +++ b/test/YACC/YACCFLAGS.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,18 +22,16 @@ # 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 sys import TestSCons +from TestCmd import IS_WINDOWS _python_ = TestSCons._python_ _exe = TestSCons._exe -if sys.platform == 'win32': +if IS_WINDOWS: compiler = 'msvc' linker = 'mslink' else: @@ -45,19 +45,18 @@ test.subdir('in') test.dir_fixture('YACCFLAGS-fixture') test.write('SConstruct', """ -env = Environment(YACC = r'%(_python_)s myyacc.py', - YACCFLAGS = '-x -I${TARGET.dir} -I${SOURCE.dir}', - tools=['yacc', '%(linker)s', '%(compiler)s']) -env.CFile(target = 'out/aaa', source = 'in/aaa.y') +DefaultEnvironment(tools=[]) +env = Environment( + YACC=r'%(_python_)s myyacc.py', + YACCFLAGS='-x -I${TARGET.dir} -I${SOURCE.dir}', + tools=['yacc', '%(linker)s', '%(compiler)s'], +) +env.CFile(target='out/aaa', source='in/aaa.y') """ % locals()) -test.write(['in', 'aaa.y'], "aaa.y\nYACCFLAGS\nI_ARGS\n") - -test.run('.', stderr = None) - -test.must_match(['out', 'aaa.c'], "aaa.y\n -x\n out in\n") - - +test.write(['in', 'aaa.y'], "aaa.y\nYACCFLAGS\nI_ARGS\n") +test.run('.', stderr=None) +test.must_match(['out', 'aaa.c'], "aaa.y\n -x\n out in\n") test.pass_test() diff --git a/test/YACC/YACCHFILESUFFIX.py b/test/YACC/YACCHFILESUFFIX.py index da3416c..e37bd66 100644 --- a/test/YACC/YACCHFILESUFFIX.py +++ b/test/YACC/YACCHFILESUFFIX.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,9 +22,6 @@ # 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__" """ Test that setting the YACCHFILESUFFIX variable can reflect a yacc @@ -35,12 +34,11 @@ _python_ = TestSCons._python_ test = TestSCons.TestSCons() - - test.write('myyacc.py', """\ import getopt import os.path import sys + opts, args = getopt.getopt(sys.argv[1:], 'do:') for o, a in opts: if o == '-o': @@ -51,33 +49,34 @@ for f in args: outfile.write(l) outfile.close() base, ext = os.path.splitext(args[0]) -with open(base+'.hsuffix', 'wb') as outfile: +with open(base + '.hsuffix', 'wb') as outfile: outfile.write((" ".join(sys.argv) + '\\n').encode()) sys.exit(0) """) test.write('SConstruct', """ -env = Environment(tools=['default', 'yacc'], - YACC = r'%(_python_)s myyacc.py', - YACCFLAGS = '-d', - YACCHFILESUFFIX = '.hsuffix') -env.CFile(target = 'aaa', source = 'aaa.y') -env.CFile(target = 'bbb', source = 'bbb.yacc') +DefaultEnvironment(tools=[]) +env = Environment( + tools=['default', 'yacc'], + YACC=r'%(_python_)s myyacc.py', + YACCFLAGS='-d', + YACCHFILESUFFIX='.hsuffix', +) +env.CFile(target='aaa', source='aaa.y') +env.CFile(target='bbb', source='bbb.yacc') """ % locals()) test.write('aaa.y', "aaa.y\n/*yacc*/\n") test.write('bbb.yacc', "bbb.yacc\n/*yacc*/\n") -test.run(arguments = '.') +test.run(arguments='.') test.must_match('aaa.c', "aaa.y\n") test.must_contain('aaa.hsuffix', "myyacc.py -d -o aaa.c aaa.y\n") test.must_match('bbb.c', "bbb.yacc\n") test.must_contain('bbb.hsuffix', "myyacc.py -d -o bbb.c bbb.yacc\n") -test.up_to_date(arguments = '.') - - +test.up_to_date(arguments='.') test.pass_test() diff --git a/test/YACC/YACCHXXFILESUFFIX.py b/test/YACC/YACCHXXFILESUFFIX.py index 3ee70ee..3028d70 100644 --- a/test/YACC/YACCHXXFILESUFFIX.py +++ b/test/YACC/YACCHXXFILESUFFIX.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,9 +22,6 @@ # 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__" """ Test that setting the YACCHXXFILESUFFIX variable can reflect a yacc @@ -35,12 +34,11 @@ _python_ = TestSCons._python_ test = TestSCons.TestSCons() - - test.write('myyacc.py', """\ import getopt import os.path import sys + opts, args = getopt.getopt(sys.argv[1:], 'do:') for o, a in opts: if o == '-o': @@ -51,29 +49,30 @@ for f in args: outfile.write(l) outfile.close() base, ext = os.path.splitext(args[0]) -with open(base+'.hxxsuffix', 'wb') as outfile: - outfile.write((" ".join(sys.argv)+'\\n').encode()) +with open(base + '.hxxsuffix', 'wb') as outfile: + outfile.write((" ".join(sys.argv) + '\\n').encode()) sys.exit(0) """) test.write('SConstruct', """ -env = Environment(tools=['default', 'yacc'], - YACC = r'%(_python_)s myyacc.py', - YACCFLAGS = '-d', - YACCHXXFILESUFFIX = '.hxxsuffix') -env.CXXFile(target = 'aaa', source = 'aaa.yy') +DefaultEnvironment(tools=[]) +env = Environment( + tools=['default', 'yacc'], + YACC=r'%(_python_)s myyacc.py', + YACCFLAGS='-d', + YACCHXXFILESUFFIX='.hxxsuffix', +) +env.CXXFile(target='aaa', source='aaa.yy') """ % locals()) test.write('aaa.yy', "aaa.yy\n/*yacc*/\n") -test.run(arguments = '.') +test.run(arguments='.') test.must_match('aaa.cc', "aaa.yy\n") test.must_contain('aaa.hxxsuffix', "myyacc.py -d -o aaa.cc aaa.yy\n") -test.up_to_date(arguments = '.') - - +test.up_to_date(arguments='.') test.pass_test() diff --git a/test/YACC/YACCVCGFILESUFFIX.py b/test/YACC/YACCVCGFILESUFFIX.py index 32c3440..2fb953b 100644 --- a/test/YACC/YACCVCGFILESUFFIX.py +++ b/test/YACC/YACCVCGFILESUFFIX.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,9 +22,6 @@ # 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__" """ Test setting the YACCVCGFILESUFFIX variable. @@ -34,12 +33,11 @@ _python_ = TestSCons._python_ test = TestSCons.TestSCons() - - test.write('myyacc.py', """\ import getopt import os.path import sys + vcg = None opts, args = getopt.getopt(sys.argv[1:], 'go:') for o, a in opts: @@ -54,23 +52,26 @@ for f in args: outfile.close() if vcg: base, ext = os.path.splitext(args[0]) - with open(base+'.vcgsuffix', 'wb') as outfile: - outfile.write((" ".join(sys.argv)+'\\n').encode()) + with open(base + '.vcgsuffix', 'wb') as outfile: + outfile.write((" ".join(sys.argv) + '\\n').encode()) sys.exit(0) """) test.write('SConstruct', """ -env = Environment(tools=['default', 'yacc'], - YACC = r'%(_python_)s myyacc.py', - YACCVCGFILESUFFIX = '.vcgsuffix') -env.CXXFile(target = 'aaa', source = 'aaa.yy') -env.CXXFile(target = 'bbb', source = 'bbb.yy', YACCFLAGS = '-g') +DefaultEnvironment(tools=[]) +env = Environment( + tools=['default', 'yacc'], + YACC=r'%(_python_)s myyacc.py', + YACCVCGFILESUFFIX='.vcgsuffix', +) +env.CXXFile(target='aaa', source='aaa.yy') +env.CXXFile(target='bbb', source='bbb.yy', YACCFLAGS='-g') """ % locals()) test.write('aaa.yy', "aaa.yy\n/*yacc*/\n") test.write('bbb.yy', "bbb.yy\n/*yacc*/\n") -test.run(arguments = '.') +test.run(arguments='.') test.must_match('aaa.cc', "aaa.yy\n") test.must_not_exist('aaa.vcg') @@ -80,9 +81,7 @@ test.must_match('bbb.cc', "bbb.yy\n") test.must_not_exist('bbb.vcg') test.must_contain('bbb.vcgsuffix', "myyacc.py -g -o bbb.cc bbb.yy\n") -test.up_to_date(arguments = '.') - - +test.up_to_date(arguments='.') test.pass_test() diff --git a/test/YACC/live-check-output-cleaned.py b/test/YACC/live-check-output-cleaned.py index 9adaaf0..aef47de 100644 --- a/test/YACC/live-check-output-cleaned.py +++ b/test/YACC/live-check-output-cleaned.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,9 +22,6 @@ # 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__" """ Test that .output file is cleaned @@ -77,14 +76,14 @@ newline: '\n'; test.write('foo.y', yacc % 'foo.y') -test.run('.', stderr = None) +test.run('.', stderr=None) -test.up_to_date(arguments = 'foo.c') +test.up_to_date(arguments='foo.c') test.must_exist(test.workpath('foo.output')) test.must_exist(test.workpath('foo.c')) -test.run(arguments = '-c .') +test.run(arguments='-c .') test.must_not_exist(test.workpath('foo.output')) test.must_not_exist(test.workpath('foo.c')) diff --git a/test/YACC/live.py b/test/YACC/live.py index 6314e6c..9214369 100644 --- a/test/YACC/live.py +++ b/test/YACC/live.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,9 +22,6 @@ # 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__" """ Test YACC and YACCFLAGS with a live yacc compiler. @@ -44,14 +43,14 @@ test.file_fixture('wrapper.py') test.write('SConstruct', """ DefaultEnvironment(tools=[]) -foo = Environment(YACCFLAGS='-d', tools = ['default', 'yacc']) +foo = Environment(YACCFLAGS='-d', tools=['default', 'yacc']) yacc = foo.Dictionary('YACC') -bar = Environment(YACC = r'%(_python_)s wrapper.py ' + yacc, tools = ['default', 'yacc']) -foo.Program(target = 'foo', source = 'foo.y') -bar.Program(target = 'bar', source = 'bar.y') -foo.Program(target = 'hello', source = ['hello.cpp']) -foo.CXXFile(target = 'file.cpp', source = ['file.yy'], YACCFLAGS='-d') -foo.CFile(target = 'not_foo', source = 'foo.y') +bar = Environment(YACC=r'%(_python_)s wrapper.py ' + yacc, tools=['default', 'yacc']) +foo.Program(target='foo', source='foo.y') +bar.Program(target='bar', source='bar.y') +foo.Program(target='hello', source=['hello.cpp']) +foo.CXXFile(target='file.cpp', source=['file.yy'], YACCFLAGS='-d') +foo.CFile(target='not_foo', source='foo.y') """ % locals()) yacc = r""" diff --git a/test/fixture/mylex.py b/test/fixture/mylex.py index 8f47049..59d6dbd 100644 --- a/test/fixture/mylex.py +++ b/test/fixture/mylex.py @@ -4,25 +4,47 @@ Phony lex for testing SCons. Writes the contents of input file to stdout, after "substituting" $LEXFLAGS and $I_ARGS -Intended for use as $LEX +Needs to understand all the lex/flex options the testcases might use. """ import getopt import sys +from pathlib import Path + + +def make_side_effect(path, text): + p = Path(path) + if str(p.parent) != '.': + p.mkdir(parents=True, exist_ok=True) + with p.open(mode="wb") as f: + f.write(text) + def fake_lex(): + make_header = None + make_table = None + if sys.platform == 'win32': longopts = ['nounistd'] else: longopts = [] + longopts.extend(['header-file=', 'tables-file=']) cmd_opts, args = getopt.getopt(sys.argv[1:], 'I:tx', longopts) opt_string = '' i_arguments = '' + for opt, arg in cmd_opts: if opt == '-I': i_arguments = f'{i_arguments} {arg}' + elif opt == '--header-file': + make_header = arg + opt_string = f'{opt_string} {opt}={arg}' + elif opt == '--tables-file': + make_table = arg + opt_string = f'{opt_string} {opt}={arg}' else: opt_string = f'{opt_string} {opt}' + for arg in args: with open(arg, 'rb') as ifp: contents = ifp.read().decode(encoding='utf-8') @@ -31,6 +53,13 @@ def fake_lex(): contents = contents.replace('I_ARGS', i_arguments) sys.stdout.write(contents) + # Extra bits: + if make_header: + make_side_effect(make_header, b"lex header\n") + if make_table: + make_side_effect(make_table, b"lex table\n") + + if __name__ == '__main__': fake_lex() sys.exit(0) diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py index ec82102..4e8e93b 100644 --- a/testing/framework/TestSCons.py +++ b/testing/framework/TestSCons.py @@ -422,20 +422,35 @@ class TestSCons(TestCommon): return None - def wrap_stdout(self, build_str="", read_str="", error=0, cleaning=0): - """Wraps standard output string(s) in the normal - "Reading ... done" and "Building ... done" strings + def wrap_stdout(self, build_str="", read_str="", error=0, cleaning=0) -> str: + """Wraps "expect" strings in SCons boilerplate. + + Given strings of expected output specific to a test, + returns a string which includes the SCons wrapping such as + "Reading ... done", etc.: that is, adds the text that would + be left out by running SCons in quiet mode; + Makes a complete message to match against. + + Args: + read_str: the message for the execution part of the output. + If non-empty, needs to be newline-terminated. + read_str: the message for the reading-sconscript part of + the output. If non-empty, needs to be newline-terminated. + error: if true, expect a fail message rather than a done message. + cleaning (int): index into type messages, if 0 selects + build messages, if 1 selects clean messages. """ cap, lc = [('Build', 'build'), ('Clean', 'clean')][cleaning] if error: - term = "scons: %sing terminated because of errors.\n" % lc + term = f"scons: {lc}ing terminated because of errors.\n" else: - term = "scons: done %sing targets.\n" % lc + term = f"scons: done {lc}ing targets.\n" + return "scons: Reading SConscript files ...\n" + \ read_str + \ "scons: done reading SConscript files.\n" + \ - "scons: %sing targets ...\n" % cap + \ + f"scons: {cap}ing targets ...\n" + \ build_str + \ term @@ -718,12 +733,12 @@ class TestSCons(TestCommon): for p in patterns: result.extend(sorted(glob.glob(p))) return result - + def get_sconsignname(self): """Get the scons database name used, and return both the prefix and full filename. if the user left the options defaulted AND the default algorithm set by SCons is md5, then set the database name to be the special default name - + otherwise, if it defaults to something like 'sha1' or the user explicitly set 'md5' as the hash format, set the database name to .sconsign_<algorithm> eg .sconsign_sha1, etc. @@ -857,7 +872,7 @@ class TestSCons(TestCommon): Args: version: if set, match only that version - Returns: + Returns: path where JDK components live Bails out of the entire test (skip) if not found. """ |