Skip to content
Open
131 changes: 118 additions & 13 deletions SCons/Tool/msvs.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import pickle
import re
import sys
import textwrap

import SCons.Builder
import SCons.Node.FS
Expand Down Expand Up @@ -152,27 +153,131 @@ def msvs_parse_version(s):
num, suite = version_re.match(s).groups()
return float(num), suite

# This is how we re-invoke SCons from inside MSVS Project files.
# The problem is that we might have been invoked as either scons.bat
# or scons.py. If we were invoked directly as scons.py, then we could
# use sys.argv[0] to find the SCons "executable," but that doesn't work
# if we were invoked as scons.bat, which uses "python -c" to execute
# things and ends up with "-c" as sys.argv[0]. Consequently, we have
# the MSVS Project file invoke SCons the same way that scons.bat does,
# which works regardless of how we were invoked.
_exec_script_main_template = None

def getExecScriptMain(env, xml=None):
"""
This is how we re-invoke SCons from inside MSVS Project files.
The problem is that we might have been invoked as either scons.bat
or scons.py. If we were invoked directly as scons.py, then we could
use sys.argv[0] to find the SCons "executable," but that doesn't work
if we were invoked as scons.bat, which uses "python -c" to execute
things and ends up with "-c" as sys.argv[0]. Consequently, we have
the MSVS Project file invoke SCons the same way that scons.bat does,
which works regardless of how we were invoked.

:param env: Environment to operate on
:param xml: Extra XML to add to generated MSVS project file
"""
global _exec_script_main_template

if 'SCONS_HOME' not in env:
env['SCONS_HOME'] = os.environ.get('SCONS_HOME')
scons_home = env.get('SCONS_HOME')
if not scons_home and 'SCONS_LIB_DIR' in os.environ:
scons_home = os.environ['SCONS_LIB_DIR']
if scons_home:
exec_script_main = "from os.path import join; import sys; sys.path = [ r'%s' ] + sys.path; import SCons.Script; SCons.Script.main()" % scons_home
else:
version = SCons.__version__
exec_script_main = "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-%(version)s'), join(sys.prefix, 'scons-%(version)s'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons') ] + sys.path; import SCons.Script; SCons.Script.main()" % locals()

if scons_home is None:
scons_home = ''

scons_abspath = os.path.abspath(os.path.dirname(os.path.dirname(SCons.__file__)))

def _in_pytree(scons_abspath):
scons_norm = os.path.normcase(scons_abspath)
for py_prefix in [sys.prefix]: # sys.exec_prefix also on windows?
py_norm = os.path.normcase(os.path.abspath(py_prefix))
try:
common = os.path.commonpath([py_norm, scons_norm])
if common == py_norm:
return True
break
except ValueError:
pass
return False

in_pytree = _in_pytree(scons_abspath)
# print(f"in_pytree={in_pytree}, scons_abspath=`{scons_abspath}', sys.prefix='{sys.prefix}', sys.exec_prefix='{sys.exec_prefix}'")

if in_pytree:
scons_abspath = ''

if _exec_script_main_template is None:
_exec_script_main_template = "; ".join([line for block in [textwrap.dedent(s).splitlines() for s in [
# Import libraries and functions.
"""\
import importlib.util
import sys
from os.path import abspath, dirname, isdir, isfile, join, normcase, realpath
""",
# Initialize the generated paths:
# * convert scons_home to an absolute path,
# * clear scons_abspath when equal to the scons_home absolute path.
"""\
usrinit = lambda p: abspath(p) if p else p
geninit = lambda p, u: '' if (p and u and normcase(p) == normcase(u)) else p
usr_path = usrinit(r'{scons_home}')
gen_path = geninit(r'{scons_abspath}', usr_path)
""",
# Evaluate candidate lists:
# 1. If scons_home is defined:
# * Record scons_home as found iff the path contains SCons.
# * Stop evaluating remaining alternatives.
# 2. If scons_abspath is defined:
# * Record scons_abspath as found iff the path contains SCons.
# * Stop evaluating remaining alternatives.
# 3. Evaluate known library locations:
# * Record the first library path that contains SCons as found.
"""\
state = {{}}
isvalid = lambda p: p and isdir(p) and isfile(join(p, 'SCons', '__init__.py'))
store = lambda k, l, s: {{k: l[0], 'Found': l[0], 'Stop': True}} if l else {{k: '', 'Stop': s}}
check = lambda k, l, s: state.update(store(k, [p for p in l if isvalid(p)], s)) if not state.get('Stop') else None
_ = [check(k, l, s) for k, l, s in [('usr', [usr_path], bool(usr_path)), ('gen', [gen_path], bool(gen_path)), ('lib', [join(sys.prefix, *t) for t in [('Lib', 'site-packages', 'scons-{scons_version}'), ('scons-{scons_version}',), ('Lib', 'site-packages', 'scons'), ('scons',), ('Lib', 'site-packages')]], False)]]
""",
# If an SCons module path was found, add the path to the front of
# the sys.path list.
"""\
path = state.get('Found', '')
_ = sys.path.insert(0, path) if path else None
""",
# Use importlib to find the SCons module path prior to import.
# Add a valid module spec origin path to the front of the sys.path list if:
# * a module path was not found earlier, or
# * the module spec origin path is different than the module
# path found earlier.
"""\
spec = importlib.util.find_spec('SCons')
orig = dirname(dirname(abspath(spec.origin))) if (spec and spec.origin) else ''
syspath = orig and (not path or normcase(abspath(path)) != normcase(orig))
_ = sys.path.insert(0, orig) if syspath else None
""",
# Display diagnostic messages.
"""\
_ = print(f'proj: *** SCons not found at user path \\\'{{usr_path}}\\\'. ***') if (usr_path and state.get('usr') == '') else None
_ = print(f'proj: *** SCons not found at generated path \\\'{{gen_path}}\\\' ***.') if (gen_path and state.get('gen') == '') else None
_ = print( 'proj: *** SCons not found. ***') if (not orig) else None
""",
# Display SCons module path (if found).
"""\
_ = print(f'proj: Using SCons path \\\'{{orig}}\\\' (realpath=\\\'{{realpath(orig)}}\\\', syspath={{syspath}}).') if (orig) else None
""",
# Import SCons and run main script.
"""\
import SCons.Script
SCons.Script.main()
""",
]] for line in block])

exec_script_main = _exec_script_main_template.format(
scons_home=scons_home,
scons_abspath=scons_abspath,
scons_version=SCons.__version__,
)
# print("exec_script_main:\n", ' ' + '\n '.join(exec_script_main.split("; ")))

if xml:
exec_script_main = xmlify(exec_script_main)

return exec_script_main

# The string for the Python executable we tell the Project file to use
Expand Down
1 change: 1 addition & 0 deletions runtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,7 @@ def footer(self, f):
# Because SCons is really aggressive about finding its modules,
# it sometimes finds SCons modules elsewhere on the system.
# This forces SCons to use the modules that are being tested.
testenv['SCONS_HOME'] = scons_lib_dir
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why add this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prior to this PR (i.e., current master):

  • If SCONS_HOME is set in the environment when the test is run, it has to match the SCONS_LIB_DIR string exactly.
  • In msvs.py, SCONS_HOME is used before SCONS_LIB_DIR.
  • In TestSConsMSVS.py, only SCONS_LIB_DIR is used.
  • SCONS_LIB_DIR is added to the testenv.
  • SCONS_HOME is not added to the testenv.

When forcing SCONS_LIB_DIR into the testenv, SCONS_HOME should be populated since it's used in the source code but not in the test suite.

SCONS_HOME was added to TestSConsMSVS.py for consistency with the source code.

Note: I'm testing significantly revised versions of msvs.py and TestSConsMSVS.py which I intend to push later today or in the morning tomorrow pending testing insights.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I say that probably neither should be set by runtest.py anymore SCONS_LIB_DIR was ancient and needed prior to re-org-ing the code and the packaging.
Since it's unlikely than any user would have either set, we should be testing without them..

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With regards to the current code, I believe that might require a user to set SCONS_HOME or SCONS_LIB_DIR in their environment when running the test suite with a source distribution.

For example, testing an SCons source branch with a python that does not have SCons installed. If there are any tests that actually run devenv against the generated project files would have a problem.

The scoop issue reported is effectively the same problem. A standalone SCons distribution as opposed to SCons installed as a library. SCONS_HOME is the solution with current master.

I thought at one point in time, we had to set SCONS_HOME before running the test suite but I could be wrong.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just commented out setting SCONS_LIB_DIR and SCONS_HOME from elif clause here and tests ran fine, with and without setting --baseline to another dir and with and without installing scons in the activated virtualenv.

diff --git a/runtest.py b/runtest.py
index ee264ecac..7cd4e9127 100755
--- a/runtest.py
+++ b/runtest.py
@@ -537,11 +537,12 @@ if scons:
     # its own modules.
     testenv['SCONS'] = scons
 elif scons_lib_dir:
+    pass
     # Because SCons is really aggressive about finding its modules,
     # it sometimes finds SCons modules elsewhere on the system.
     # This forces SCons to use the modules that are being tested.
-    testenv['SCONS_HOME'] = scons_lib_dir
-    testenv['SCONS_LIB_DIR'] = scons_lib_dir
+    # testenv['SCONS_HOME'] = scons_lib_dir
+    # testenv['SCONS_LIB_DIR'] = scons_lib_dir

 if args.scons_exec:
     testenv['SCONS_EXEC'] = '1'```

This used to be important, but hasn't been for a while as far as I'm aware.

testenv['SCONS_LIB_DIR'] = scons_lib_dir

if args.scons_exec:
Expand Down
4 changes: 2 additions & 2 deletions test/MSVS/common-prefix.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@
test.must_exist(test.workpath('work1', 'Test.vcproj'))
vcproj = test.read(['work1', 'Test.vcproj'], 'r')
expected_vcprojfile = vcproj_template % locals()
expect = test.msvs_substitute(expected_vcprojfile, '8.0', 'work1', 'SConstruct')
expect = test.msvs_substitute(expected_vcprojfile, '8.0', subdir='work1', sconscript='SConstruct')
# don't compare the pickled data
assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj)

Expand All @@ -163,7 +163,7 @@
test.must_exist(test.workpath('work2', 'Test.vcproj'))
vcproj = test.read(['work2', 'Test.vcproj'], 'r')
expected_vcprojfile = vcproj_template % locals()
expect = test.msvs_substitute(expected_vcprojfile, '8.0', 'work2', 'SConstruct')
expect = test.msvs_substitute(expected_vcprojfile, '8.0', subdir='work2', sconscript='SConstruct')
# don't compare the pickled data
assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj)

Expand Down
2 changes: 1 addition & 1 deletion test/MSVS/runfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@

test.must_exist(test.workpath('work1', 'Test.vcproj'))
vcproj = test.read(['work1', 'Test.vcproj'], 'r')
expect = test.msvs_substitute(expected_vcprojfile, '8.0', 'work1', 'SConstruct')
expect = test.msvs_substitute(expected_vcprojfile, '8.0', subdir='work1', sconscript='SConstruct')
# don't compare the pickled data
assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj)

Expand Down
4 changes: 2 additions & 2 deletions test/MSVS/vs-6.0-clean.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,13 @@

test.must_exist(test.workpath('Test.dsp'))
dsp = test.read('Test.dsp', 'r')
expect = test.msvs_substitute(expected_dspfile, '6.0', None, 'SConstruct')
expect = test.msvs_substitute(expected_dspfile, '6.0', sconscript='SConstruct')
# don't compare the pickled data
assert dsp[:len(expect)] == expect, test.diff_substr(expect, dsp)

test.must_exist(test.workpath('Test.dsw'))
dsw = test.read('Test.dsw', 'r')
expect = test.msvs_substitute(expected_dswfile, '6.0', None, 'SConstruct')
expect = test.msvs_substitute(expected_dswfile, '6.0', sconscript='SConstruct')
assert dsw == expect, test.diff_substr(expect, dsw)

test.run(arguments='-c .')
Expand Down
4 changes: 2 additions & 2 deletions test/MSVS/vs-6.0-files.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,13 @@

test.must_exist(test.workpath('Test.dsp'))
dsp = test.read('Test.dsp', 'r')
expect = test.msvs_substitute(expected_dspfile, '6.0', None, 'SConstruct')
expect = test.msvs_substitute(expected_dspfile, '6.0', sconscript='SConstruct')
# don't compare the pickled data
assert dsp[:len(expect)] == expect, test.diff_substr(expect, dsp)

test.must_exist(test.workpath('Test.dsw'))
dsw = test.read('Test.dsw', 'r')
expect = test.msvs_substitute(expected_dswfile, '6.0', None, 'SConstruct')
expect = test.msvs_substitute(expected_dswfile, '6.0', sconscript='SConstruct')
assert dsw == expect, test.diff_substr(expect, dsw)

test.run(arguments='-c .')
Expand Down
6 changes: 3 additions & 3 deletions test/MSVS/vs-6.0-variant_dir.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@
test.run(arguments=".")

dsp = test.read(['src', 'Test.dsp'], 'r')
expect = test.msvs_substitute(expected_dspfile, '6.0', None, 'SConstruct')
expect = test.msvs_substitute(expected_dspfile, '6.0', sconscript='SConstruct')
# don't compare the pickled data
assert dsp[:len(expect)] == expect, test.diff_substr(expect, dsp)

test.must_exist(test.workpath('src', 'Test.dsw'))
dsw = test.read(['src', 'Test.dsw'], 'r')
expect = test.msvs_substitute(expected_dswfile, '6.0', 'src')
assert dsw == expect, test.diff_substr(expect, dsw)
expect = test.msvs_substitute(expected_dswfile, '6.0', subdir='src')
assert dsw == expect, test.diff_substr(expect, subdir=dsw)

test.must_match(['build', 'Test.dsp'], """\
This is just a placeholder file.
Expand Down
4 changes: 2 additions & 2 deletions test/MSVS/vs-7.0-clean.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,13 @@

test.must_exist(test.workpath('Test.vcproj'))
vcproj = test.read('Test.vcproj', 'r')
expect = test.msvs_substitute(expected_vcprojfile, '7.0', None, 'SConstruct')
expect = test.msvs_substitute(expected_vcprojfile, '7.0', sconscript='SConstruct')
# don't compare the pickled data
assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj)

test.must_exist(test.workpath('Test.sln'))
sln = test.read('Test.sln', 'r')
expect = test.msvs_substitute(expected_slnfile, '7.0', None, 'SConstruct')
expect = test.msvs_substitute(expected_slnfile, '7.0', sconscript='SConstruct')
# don't compare the pickled data
assert sln[:len(expect)] == expect, test.diff_substr(expect, sln)

Expand Down
7 changes: 3 additions & 4 deletions test/MSVS/vs-7.0-files.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@

test.must_exist(test.workpath('Test.vcproj'))
vcproj = test.read('Test.vcproj', 'r')
expect = test.msvs_substitute(expected_vcprojfile, '7.0', None, 'SConstruct')
expect = test.msvs_substitute(expected_vcprojfile, '7.0', sconscript='SConstruct')
# don't compare the pickled data
assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj)

test.must_exist(test.workpath('Test.sln'))
sln = test.read('Test.sln', 'r')
expect = test.msvs_substitute(expected_slnfile, '7.0', None, 'SConstruct')
expect = test.msvs_substitute(expected_slnfile, '7.0', sconscript='SConstruct')
# don't compare the pickled data
assert sln[:len(expect)] == expect, test.diff_substr(expect, sln)

Expand Down Expand Up @@ -83,8 +83,7 @@

test.must_exist(test.workpath('Test.vcproj'))
vcproj = test.read('Test.vcproj', 'r')
expect = test.msvs_substitute(expected_vcprojfile, '7.0', None, 'SConstruct',
python=python)
expect = test.msvs_substitute(expected_vcprojfile, '7.0', sconscript='SConstruct', python=python)
# don't compare the pickled data
assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj)

Expand Down
6 changes: 2 additions & 4 deletions test/MSVS/vs-7.0-scc-files.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,13 @@

test.must_exist(test.workpath('Test.vcproj'))
vcproj = test.read('Test.vcproj', 'r')
expect = test.msvs_substitute(expected_vcprojfile, '7.0', None, 'SConstruct',
vcproj_sccinfo=expected_vcproj_sccinfo)
expect = test.msvs_substitute(expected_vcprojfile, '7.0', sconscript='SConstruct', vcproj_sccinfo=expected_vcproj_sccinfo)
# don't compare the pickled data
assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj)

test.must_exist(test.workpath('Test.sln'))
sln = test.read('Test.sln', 'r')
expect = test.msvs_substitute(expected_slnfile, '7.0', None, 'SConstruct',
sln_sccinfo=expected_sln_sccinfo)
expect = test.msvs_substitute(expected_slnfile, '7.0', sconscript='SConstruct', sln_sccinfo=expected_sln_sccinfo)
# don't compare the pickled data
assert sln[:len(expect)] == expect, test.diff_substr(expect, sln)

Expand Down
5 changes: 2 additions & 3 deletions test/MSVS/vs-7.0-scc-legacy-files.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,13 @@

test.must_exist(test.workpath('Test.vcproj'))
vcproj = test.read('Test.vcproj', 'r')
expect = test.msvs_substitute(expected_vcprojfile, '7.0', None, 'SConstruct',
vcproj_sccinfo=expected_vcproj_sccinfo)
expect = test.msvs_substitute(expected_vcprojfile, '7.0', sconscript='SConstruct', vcproj_sccinfo=expected_vcproj_sccinfo)
# don't compare the pickled data
assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj)

test.must_exist(test.workpath('Test.sln'))
sln = test.read('Test.sln', 'r')
expect = test.msvs_substitute(expected_slnfile, '7.0', None, 'SConstruct')
expect = test.msvs_substitute(expected_slnfile, '7.0', sconscript='SConstruct')
# don't compare the pickled data
assert sln[:len(expect)] == expect, test.diff_substr(expect, sln)

Expand Down
4 changes: 2 additions & 2 deletions test/MSVS/vs-7.0-variant_dir.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@
test.run(arguments=".")

vcproj = test.read(['src', 'Test.vcproj'], 'r')
expect = test.msvs_substitute(expected_vcprojfile, '7.0', None, 'SConstruct')
expect = test.msvs_substitute(expected_vcprojfile, '7.0', sconscript='SConstruct')
# don't compare the pickled data
assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj)

test.must_exist(test.workpath('src', 'Test.sln'))
sln = test.read(['src', 'Test.sln'], 'r')
expect = test.msvs_substitute(expected_slnfile, '7.0', 'src')
expect = test.msvs_substitute(expected_slnfile, '7.0', subdir='src')
# don't compare the pickled data
assert sln[:len(expect)] == expect, test.diff_substr(expect, sln)

Expand Down
4 changes: 2 additions & 2 deletions test/MSVS/vs-7.1-clean.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,13 @@

test.must_exist(test.workpath('Test.vcproj'))
vcproj = test.read('Test.vcproj', 'r')
expect = test.msvs_substitute(expected_vcprojfile, '7.1', None, 'SConstruct')
expect = test.msvs_substitute(expected_vcprojfile, '7.1', sconscript='SConstruct')
# don't compare the pickled data
assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj)

test.must_exist(test.workpath('Test.sln'))
sln = test.read('Test.sln', 'r')
expect = test.msvs_substitute(expected_slnfile, '7.1', None, 'SConstruct')
expect = test.msvs_substitute(expected_slnfile, '7.1', sconscript='SConstruct')
# don't compare the pickled data
assert sln[:len(expect)] == expect, test.diff_substr(expect, sln)

Expand Down
7 changes: 3 additions & 4 deletions test/MSVS/vs-7.1-files.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@

test.must_exist(test.workpath('Test.vcproj'))
vcproj = test.read('Test.vcproj', 'r')
expect = test.msvs_substitute(expected_vcprojfile, '7.1', None, 'SConstruct')
expect = test.msvs_substitute(expected_vcprojfile, '7.1', sconscript='SConstruct')
# don't compare the pickled data
assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj)

test.must_exist(test.workpath('Test.sln'))
sln = test.read('Test.sln', 'r')
expect = test.msvs_substitute(expected_slnfile, '7.1', None, 'SConstruct')
expect = test.msvs_substitute(expected_slnfile, '7.1', sconscript='SConstruct')
# don't compare the pickled data
assert sln[:len(expect)] == expect, test.diff_substr(expect, sln)

Expand Down Expand Up @@ -83,8 +83,7 @@

test.must_exist(test.workpath('Test.vcproj'))
vcproj = test.read('Test.vcproj', 'r')
expect = test.msvs_substitute(expected_vcprojfile, '7.1', None, 'SConstruct',
python=python)
expect = test.msvs_substitute(expected_vcprojfile, '7.1', sconscript='SConstruct', python=python)
# don't compare the pickled data
assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj)

Expand Down
6 changes: 2 additions & 4 deletions test/MSVS/vs-7.1-scc-files.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,13 @@

test.must_exist(test.workpath('Test.vcproj'))
vcproj = test.read('Test.vcproj', 'r')
expect = test.msvs_substitute(expected_vcprojfile, '7.1', None, 'SConstruct',
vcproj_sccinfo=expected_vcproj_sccinfo)
expect = test.msvs_substitute(expected_vcprojfile, '7.1', sconscript='SConstruct', vcproj_sccinfo=expected_vcproj_sccinfo)
# don't compare the pickled data
assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj)

test.must_exist(test.workpath('Test.sln'))
sln = test.read('Test.sln', 'r')
expect = test.msvs_substitute(expected_slnfile, '7.1', None, 'SConstruct',
sln_sccinfo=expected_sln_sccinfo)
expect = test.msvs_substitute(expected_slnfile, '7.1', sconscript='SConstruct', sln_sccinfo=expected_sln_sccinfo)
# don't compare the pickled data
assert sln[:len(expect)] == expect, test.diff_substr(expect, sln)

Expand Down
Loading
Loading