#!/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

"""
Verify that use of --implicit-cache with the Python Value Nodes
used by the Configure subsystem generate the same .sconsign file
and don't cause it to grow without limit.

This was reported as issue 2033 in the tigris.org bug tracker, by the
Ardour project.  Prior to 0.98.4, the Value implementation would actually
return the repr() of its value as the str().  This was done because
it made saving a Value in a file and reading it back in kind of work,
because a print a string Value into a file (for example) would in fact
put quotes around it and be assignable in that file.

The problem is that this would get stored in a .sconsign file as its
repr(), with the specific problem being that Values with embedded newlines
would get stored as strings containing backslash+n digraphs *and* the
quotes at beginning and end of the string::

    '\n#include <math.h>\n\n': {<.sconsign info>}

Then, when we read that back in from the .sconsign file, we would store
that repr() as a string Value itself, escaping the backslashes and
including the quotes, so when we stored it the second time it would end
up looking like:

    "'\\n#include <math.h>\\n\\n'": {<.sconsign info>}

Every time that we would read this value and store it again (because
something else changed in the .sconf_temp directory), the string would
get longer and longer until it blew out the users's memory.
"""


import TestSConsign
from SCons.Util import get_hash_format, get_current_hash_algorithm_used

test = TestSConsign.TestSConsign()

test.write('SConstruct', """
DefaultEnvironment(tools=[])
env = Environment(CPPPATH=['.'])
conf = Configure(env)
conf.CheckHeader( 'math.h' )
if ARGUMENTS.get('USE_FOO'):
    conf.CheckHeader( 'foo.h' )
env = conf.Finish()
""")

test.write('foo.h', "#define FOO 1\n")

# First run:  Have the configure subsystem only look for math.h, and
# squirrel away the .sconsign info for the conftest_0.c file that's
# generated from the Python Value Node that we're using for our test.

test.run(arguments = '.')

# depending on which default hash function we're using, we'd expect one of the following filenames.
# The filenames are generated by the conftest changes in #3543 : https://github.com/SCons/scons/pull/3543/files
# this test is different than the other tests, as the database name is used here.
# when the database defaults to md5, that's a different name than when the user selects md5 directly.
possible_filenames = {
    'default': "conftest_5a3fa36d51dd2a28d521d6cc0e2e1d04_0.c",
    'md5': "conftest_5a3fa36d51dd2a28d521d6cc0e2e1d04_0.c",
    'sha1': "conftest_80e5b88f2c7427a92f0e6c7184f144f874f10e60_0.c",
    'sha256': "conftest_ba8270c26647ad00993cd7777f4c5d3751018372b97d16eb993563bea051c3df_0.c"
}
# user left algorithm default, it defaulted to md5, with the special database name
if get_hash_format() is None and get_current_hash_algorithm_used() == 'md5':
    test_filename = possible_filenames['default']
# either user selected something (like explicitly setting md5) or algorithm defaulted to something else.
# SCons can default to something else if it detects the hashlib doesn't support it, example md5 in FIPS
# mode prior to Python 3.9
else:
    test_filename = possible_filenames[get_current_hash_algorithm_used()]

database_name=test.get_sconsignname() + ".dblite"

test.run_sconsign('-d .sconf_temp -e {} --raw {}'.format(test_filename, database_name))
old_sconsign_dblite = test.stdout()

# Second run:  Have the configure subsystem also look for foo.h, so
# that there's a change in the .sconf_temp directory that will cause its
# .sconsign information to get rewritten from disk.  Squirrel away the
# .sconsign info for the conftest_0.c file.  The now-fixed bug would show
# up because the entry would change with the additional string-escaping
# described above.  The now-correct behavior is that the re-stored value
# for conftest_0.c doesn't change.

test.run(arguments = '--implicit-cache USE_FOO=1 .')

test.run_sconsign('-d .sconf_temp -e {} --raw {}'.format(test_filename, database_name))
new_sconsign_dblite = test.stdout()

if old_sconsign_dblite != new_sconsign_dblite:
    print("{} did not match:".format(database_name))
    print("FIRST RUN ==========")
    print(old_sconsign_dblite)
    print("SECOND RUN ==========")
    print(new_sconsign_dblite)
    test.fail_test()

test.pass_test()

# Local Variables:
# tab-width:4
# indent-tabs-mode:nil
# End:
# vim: set expandtab tabstop=4 shiftwidth=4:
