From 93a0eacd47f89db2c414a3e3688365f41865dac6 Mon Sep 17 00:00:00 2001 From: pmp-p Date: Wed, 21 Feb 2024 18:14:05 +0100 Subject: [PATCH 01/17] fix cache deletion logic on osx --- src/pygbag/app.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/pygbag/app.py b/src/pygbag/app.py index 48e5c95..0defc8b 100644 --- a/src/pygbag/app.py +++ b/src/pygbag/app.py @@ -161,17 +161,21 @@ def make_cache_dirs(): if clear_cache: win32 = sys.platform == "win32" - if shutil.rmtree.avoids_symlink_attacks or win32: - if cache_dir.is_dir(): + if cache_dir.is_dir(): + if shutil.rmtree.avoids_symlink_attacks or win32: if win32: warnings.warn("clear cache : rmtree is not safe on that system (win32)") shutil.rmtree(cache_dir.as_posix()) - else: - print( - "115: cannot clear cache : rmtree is not safe on that system", - file=sys.stderr, - ) - raise SystemExit(115) + else: + print( + "171: cannot clear cache : rmtree is not safe on that system", + file=sys.stderr, + ) + print( + "175: Please remove build folder manually", + file=sys.stderr, + ) + raise SystemExit(115) # rebuild make_cache_dirs() From cc62dfc4251e78bd2ab2f9c733896d2da8638d30 Mon Sep 17 00:00:00 2001 From: pmp-p Date: Sat, 24 Feb 2024 11:25:37 +0100 Subject: [PATCH 02/17] fix pypad_git --- src/pygbag/support/cpythonrc.py | 3 ++- src/pygbag/support/cross/aio/pep0723.py | 2 +- static/pythons.js | 13 ++++++++++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/pygbag/support/cpythonrc.py b/src/pygbag/support/cpythonrc.py index 97c7114..90f7b80 100644 --- a/src/pygbag/support/cpythonrc.py +++ b/src/pygbag/support/cpythonrc.py @@ -1475,6 +1475,7 @@ def log(*argv, **kw): async def import_site(__file__, run=True): import builtins + if builtins.LOCK: platform.window.console.error("1473: import_site IS NOT RE ENTRANT") return @@ -1643,4 +1644,4 @@ async def main(): shell.interactive(prompt=True) return None finally: - LOCK = False + builtins.LOCK = False diff --git a/src/pygbag/support/cross/aio/pep0723.py b/src/pygbag/support/cross/aio/pep0723.py index 7b740a2..f4d113d 100644 --- a/src/pygbag/support/cross/aio/pep0723.py +++ b/src/pygbag/support/cross/aio/pep0723.py @@ -238,7 +238,7 @@ async def async_repos(): print( f""" -=============== REDIRECTION TO DEV HOST {repo['-CDN-']} ================ +====== REDIRECT TO DEV HOST {repo['-CDN-']} ======== {abitag=} {apitag=} diff --git a/static/pythons.js b/static/pythons.js index dae8af1..cd9c551 100644 --- a/static/pythons.js +++ b/static/pythons.js @@ -1036,15 +1036,22 @@ async function feat_vtx(debug_hidden) { document.body.appendChild(terminal) } + var console_divider = 1 const cols = get_terminal_cols() - const cons = get_terminal_console() + var cons = get_terminal_console() + if (cons<0) { + console_divider = -cons + cons = 0 + } + const { WasmTerminal } = await import("./vtx.js") const lines = get_terminal_lines() + cons // including virtual get_terminal_console() const py = window.document.body.clientHeight var fntsize = Math.floor(py/lines) - 1 - if (lines<=33) - fntsize = fntsize - 5 + if (lines<=33) { + fntsize = ( fntsize - 5 ) / console_divider + } console.warn("fnt:",window.document.body.clientHeight ,"/", lines,"=", fntsize, " Cols:", cols, "Cons:", cons) vm.vt = new WasmTerminal( From a373d8b96ee3c4c90b53ac33742d630427092412 Mon Sep 17 00:00:00 2001 From: pmp-p Date: Mon, 26 Feb 2024 08:17:19 +0100 Subject: [PATCH 03/17] drop 3.11 bump sdk --- .github/workflows/ci.yml | 26 +++++++++++++------------- src/pygbag/app.py | 5 +++-- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6eb3f92..c92dd03 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,12 +6,12 @@ jobs: build: runs-on: ubuntu-22.04 env: - SDK_VERSION: 3.1.54.1bi + SDK_VERSION: 3.1.55.0bi SYS_PYTHON: /usr/bin/python3 PACKAGES: emsdk hpy pygame BUILD_STATIC: emsdk hpy STATIC: false - BUILDS: 3.11 3.12 3.13 + BUILDS: 3.12 3.13 CYTHON: Cython-3.0.8-py2.py3-none-any.whl LD_VENDOR: -sUSE_GLFW=3 @@ -36,17 +36,17 @@ jobs: sudo apt-get update 2>&1 >/dev/null sudo apt-get install -y bash git curl wget lz4 pv 2>&1 >/dev/null - - - name: build 3.11 packages - run: | - cd $GITHUB_WORKSPACE - PYBUILD=3.11 PACKAGES="emsdk hpy _ctypes pygame" BUILD_STATIC="emsdk _ctypes hpy" bash ./scripts/build-pkg.sh - - - name: build 3.11 loader - run: | - cd $GITHUB_WORKSPACE - PYBUILD=3.11 PACKAGES="emsdk hpy _ctypes pygame" BUILD_STATIC="emsdk _ctypes hpy" bash ./scripts/build-loader.sh - +# +# - name: build 3.11 packages +# run: | +# cd $GITHUB_WORKSPACE +# PYBUILD=3.11 PACKAGES="emsdk hpy _ctypes pygame" BUILD_STATIC="emsdk _ctypes hpy" bash ./scripts/build-pkg.sh +# +# - name: build 3.11 loader +# run: | +# cd $GITHUB_WORKSPACE +# PYBUILD=3.11 PACKAGES="emsdk hpy _ctypes pygame" BUILD_STATIC="emsdk _ctypes hpy" bash ./scripts/build-loader.sh +# - name: build 3.12 packages run: | diff --git a/src/pygbag/app.py b/src/pygbag/app.py index 0defc8b..06e7120 100644 --- a/src/pygbag/app.py +++ b/src/pygbag/app.py @@ -36,6 +36,7 @@ DEFAULT_CONSOLE = 25 DEFAULT_LINES = 57 DEFAULT_COLUMNS = 132 +DEFAULT_PYBUILD = "3.12" CACHE_ROOT = Path("build") CACHE_PATH = CACHE_ROOT / "web-cache" @@ -214,8 +215,8 @@ async def main_run(app_folder, mainscript, cdn=DEFAULT_CDN): parser.add_argument( "--PYBUILD", - default="3.11", - help="Specify python version [default:%s]" % "3.11", + default=DEFAULT_PYBUILD, + help="Specify python version [default:%s]" % DEFAULT_PYBUILD, ) parser.add_argument( From 9f124f85e4d02a5b068bdbb2af8170d0d131d7c7 Mon Sep 17 00:00:00 2001 From: pmp-p Date: Mon, 26 Feb 2024 08:57:53 +0100 Subject: [PATCH 04/17] update hpy build script for 3.12 --- packages.d/hpy/hpy.sh | 85 +++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/packages.d/hpy/hpy.sh b/packages.d/hpy/hpy.sh index e30cdb8..af37029 100755 --- a/packages.d/hpy/hpy.sh +++ b/packages.d/hpy/hpy.sh @@ -57,52 +57,57 @@ popd pushd external/hpy -# build for host simulator +# build and install for host simulator ${HPY} setup.py install -# build for wasm -${SDKROOT}/python3-wasm setup.py install - +# ${SDKROOT}/python3-wasm setup.py install # link static . ${SDKROOT}/emsdk/emsdk_env.sh - $SDKROOT/emsdk/upstream/emscripten/emar rcs /opt/python-wasm-sdk/prebuilt/emsdk/libhpy${PYMAJOR}.${PYMINOR}.a \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/debug/src/_debugmod.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/debug/src/autogen_debug_wrappers.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/debug/src/debug_ctx.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/debug/src/debug_ctx_cpython.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/debug/src/debug_handles.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/debug/src/dhqueue.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/debug/src/memprotect.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/debug/src/stacktrace.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/devel/src/runtime/argparse.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/devel/src/runtime/buildvalue.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/devel/src/runtime/ctx_bytes.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/devel/src/runtime/ctx_call.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/devel/src/runtime/ctx_capsule.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/devel/src/runtime/ctx_contextvar.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/devel/src/runtime/ctx_err.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/devel/src/runtime/ctx_eval.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/devel/src/runtime/ctx_listbuilder.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/devel/src/runtime/ctx_long.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/devel/src/runtime/ctx_module.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/devel/src/runtime/ctx_object.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/devel/src/runtime/ctx_tracker.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/devel/src/runtime/ctx_tuple.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/devel/src/runtime/ctx_tuplebuilder.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/devel/src/runtime/ctx_type.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/devel/src/runtime/format.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/devel/src/runtime/helpers.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/devel/src/runtime/structseq.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/trace/src/_tracemod.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/trace/src/autogen_trace_func_table.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/trace/src/autogen_trace_wrappers.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/trace/src/trace_ctx.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/universal/src/ctx.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/universal/src/ctx_meth.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/universal/src/ctx_misc.o \ - build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-311/hpy/universal/src/hpymodule.o + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/debug/src/_debugmod.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/debug/src/autogen_debug_wrappers.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/debug/src/debug_ctx.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/debug/src/debug_ctx_cpython.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/debug/src/debug_handles.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/debug/src/dhqueue.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/debug/src/memprotect.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/debug/src/stacktrace.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/devel/src/runtime/argparse.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/devel/src/runtime/buildvalue.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/devel/src/runtime/ctx_bytes.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/devel/src/runtime/ctx_call.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/devel/src/runtime/ctx_capsule.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/devel/src/runtime/ctx_contextvar.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/devel/src/runtime/ctx_err.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/devel/src/runtime/ctx_eval.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/devel/src/runtime/ctx_listbuilder.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/devel/src/runtime/ctx_long.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/devel/src/runtime/ctx_module.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/devel/src/runtime/ctx_object.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/devel/src/runtime/ctx_tracker.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/devel/src/runtime/ctx_tuple.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/devel/src/runtime/ctx_tuplebuilder.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/devel/src/runtime/ctx_type.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/devel/src/runtime/format.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/devel/src/runtime/helpers.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/devel/src/runtime/structseq.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/trace/src/_tracemod.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/trace/src/autogen_trace_func_table.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/trace/src/autogen_trace_wrappers.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/trace/src/trace_ctx.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/universal/src/ctx.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/universal/src/ctx_meth.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/universal/src/ctx_misc.o \ + build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/universal/src/hpymodule.o + +if [ -f /pp ] +then + # build wheel for wasm + ${SDKROOT}/python3-wasm -m build --no-isolation . + + mv dist/*wasm*whl /data/git/archives/repo/pkg/ +fi popd From 3e60e1c9208ace0966160c83f68f7c9cd9b7f533 Mon Sep 17 00:00:00 2001 From: pmp-p Date: Mon, 26 Feb 2024 09:04:46 +0100 Subject: [PATCH 05/17] missing build cmd --- packages.d/hpy/hpy.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages.d/hpy/hpy.sh b/packages.d/hpy/hpy.sh index af37029..3e65780 100755 --- a/packages.d/hpy/hpy.sh +++ b/packages.d/hpy/hpy.sh @@ -61,9 +61,11 @@ pushd external/hpy ${HPY} setup.py install # ${SDKROOT}/python3-wasm setup.py install +${SDKROOT}/python3-wasm setup.py build # link static . ${SDKROOT}/emsdk/emsdk_env.sh + $SDKROOT/emsdk/upstream/emscripten/emar rcs /opt/python-wasm-sdk/prebuilt/emsdk/libhpy${PYMAJOR}.${PYMINOR}.a \ build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/debug/src/_debugmod.o \ build/temp.wasm32-${WASM_FLAVOUR}-emscripten-cpython-${PYMAJOR}${PYMINOR}/hpy/debug/src/autogen_debug_wrappers.o \ From c9eb5b77ff8a5369e4dc34a17c1629aedfea16a6 Mon Sep 17 00:00:00 2001 From: pmp-p Date: Sat, 2 Mar 2024 10:31:32 +0100 Subject: [PATCH 06/17] 0.9pre --- src/pygbag/optimizing.py | 14 ++++++++++---- src/pygbag/support/cpythonrc.py | 9 +++------ src/pygbag/support/cross/aio/pep0723.py | 2 +- static/default.tmpl | 2 +- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/pygbag/optimizing.py b/src/pygbag/optimizing.py index ba8a7ad..a13012c 100644 --- a/src/pygbag/optimizing.py +++ b/src/pygbag/optimizing.py @@ -44,7 +44,10 @@ def optimize(folder, filenames, **kw): def optimize(folder, filenames, **kw): global BAD - print("optimizing", folder) + if "--no_opt" in sys.argv: + print("NOT optimizing", folder) + else: + print("optimizing", folder) png_quality = 50 done_list = [] @@ -57,10 +60,13 @@ def optimize(folder, filenames, **kw): except ImportError: warnings.warn(f"Black not found for processing {folder=}") - if os.popen("pngquant 2>&1").read().count("pngfile"): - print(f" -> with pngquant --quality {png_quality}", folder) + if "--no_opt" in sys.argv: + pass else: - png_quality = -1 + if os.popen("pngquant 2>&1").read().count("pngfile"): + print(f" -> with pngquant --quality {png_quality}", folder) + else: + png_quality = -1 has_ffmpeg = os.popen("ffmpeg -version").read().count("version") diff --git a/src/pygbag/support/cpythonrc.py b/src/pygbag/support/cpythonrc.py index 90f7b80..eb701ff 100644 --- a/src/pygbag/support/cpythonrc.py +++ b/src/pygbag/support/cpythonrc.py @@ -960,7 +960,6 @@ def __eq__(self, other): platform.fopen = aio.filelike.fopen platform.sopen = aio.filelike.sopen - if not aio.cross.simulator: def fix_url(maybe_url): @@ -1130,8 +1129,6 @@ class TopLevel_async_handler(aio.toplevel.AsyncInteractiveConsole): from pathlib import Path - # repodata = "repodata.json" - def eval(self, source): for count, line in enumerate(source.split("\n")): if not count: @@ -1143,7 +1140,7 @@ def eval(self, source): if count: self.line = None self.buffer.insert(0, "#") - # self.buffer.append("") + DBG(f"1039: {count} lines queued for async eval") @classmethod @@ -1208,13 +1205,13 @@ def list_imports(cls, code=None, file=None, hint=""): if not len(aio.pep0723.Config.pkg_repolist): print( """ -1170: pep0723 REPOSITORY MISSING +1208: pep0723 REPOSITORY MISSING """ ) else: DBG( f""" -1175: list_imports {len(code)=} {file=} {hint=}") +1214: list_imports {len(code)=} {file=} {hint=}") {aio.pep0723.Config.pkg_repolist[0]['-CDN-']=} """ diff --git a/src/pygbag/support/cross/aio/pep0723.py b/src/pygbag/support/cross/aio/pep0723.py index f4d113d..d5732c2 100644 --- a/src/pygbag/support/cross/aio/pep0723.py +++ b/src/pygbag/support/cross/aio/pep0723.py @@ -198,7 +198,7 @@ async def async_repos(): if apitag.find("mvp") > 0: idx = f"{repo}index.json" else: - idx = f"{repo}index-bi.json" + idx = f"{repo}index-090bi.json" async with fopen(idx, "r", encoding="UTF-8") as index: try: data = index.read() diff --git a/static/default.tmpl b/static/default.tmpl index 2205ea5..bb67319 100644 --- a/static/default.tmpl +++ b/static/default.tmpl @@ -1,4 +1,4 @@ -