From 5c5427fdd9797f3fc95843fe0783ad515fee694a Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Mon, 8 Dec 2025 19:50:09 -0600 Subject: [PATCH] reduce shop --- help/cellstructure.md | 35 +- engine.cm => internal/engine.cm | 43 ++- internal/text.c | 2 +- meson.build | 54 +-- shop.cm | 382 ++++++++------------ source/cell.c | 2 +- internal/time.c => time.c | 8 +- internal/time.cm => time.cm | 0 internal/time_playdate.c => time_playdate.c | 0 9 files changed, 229 insertions(+), 297 deletions(-) rename engine.cm => internal/engine.cm (96%) rename internal/time.c => time.c (96%) rename internal/time.cm => time.cm (100%) rename internal/time_playdate.c => time_playdate.c (100%) diff --git a/help/cellstructure.md b/help/cellstructure.md index 7e45d7b7..d85f4897 100644 --- a/help/cellstructure.md +++ b/help/cellstructure.md @@ -35,6 +35,7 @@ all source for every single part of a cell program are located in the cell shop. the cell shop looks like this: .cell + shop.toml <---- shop configuration packages gitea.pockle.world/john/cell <--- this is the root cell gitea.pockle.world/john/prosperon @@ -61,4 +62,36 @@ When a module is requested, from a within package .. 1) cell looks within package for a folder and loads , if present 2) cell looks for a package with a top level -3) cell looks in the core for \ No newline at end of file +3) cell looks in the core for + +The core can be set and have something else linked into it, if you want to break your cell build + +## cell build +cell build compiles all c into the appropriate binaries. All object files are stored in the .cell/cache + +there are two ways to build a cell program: as a shared library, or as a static binary. + +Cell script files are simply compiled into built objects + +Then, all C files in the package are compiled. + +If you have a file like fd.c and fd_playdate.c, that is a signal to cell to compile fd.c usually, but then for the playdate target, compile fd_playdate.c instead. + +files name "main.c" are not compiled. + +each cell.toml in a package + +### shared library +this is more suitable for development. firstly, the cell core must build a shared library for the platform. + +Then, each package compiles, linking to the cell core shared library. Modules are written to include the cell core headers. + +Shared libraries are stored + +### bootstrapping cell +after the cell shared libraries are all compiled, the main.c of the cell core is compiled against the cell core shared library, creating a thin cell runner. However, even on subsequent updates to cell, this runner is not updated; only for bootstrapping. + +### static binary +For static binaries, rather than going through and compiling each dynamic library for a package, a static binary is created for a particular package, based on its dependencies. All C symbols, plus the main.c runner from the cell core, are packaged into one binary. It works just like the basic cell program. + +However, you can also specify an entry point for this binary, for example, a particular actor. When the binary is run, that particular actor runs. \ No newline at end of file diff --git a/engine.cm b/internal/engine.cm similarity index 96% rename from engine.cm rename to internal/engine.cm index 8d1ccb96..9cdd8bb4 100644 --- a/engine.cm +++ b/internal/engine.cm @@ -9,6 +9,8 @@ cell.os = null var dylib_ext +cell.id ??= "newguy" + switch(os.platform()) { case 'Windows': dylib_ext = '.dll'; break; case 'macOS': dylib_ext = '.dylib'; break; @@ -54,20 +56,16 @@ function use_core(path) { var cache_path = `2::${path}`; if (use_cache[cache_path]) return use_cache[cache_path]; - - var sym = use_embed(path) + + var sym = use_embed(path.replace('/','_')) + // Core scripts are now in .cell/core/scripts - var file_path = core_path + '/scripts/' + path + MOD_EXT - - // Fallback check for root (optional, for backward compat if needed, but not strictly necessary if we are consistent) - if (!fd.is_file(file_path)) { - file_path = core_path + '/' + path + MOD_EXT - } - + var file_path = core_path + '/' + path + MOD_EXT + if (fd.is_file(file_path)) { var script_blob = fd.slurp(file_path) var script = utf8.decode(script_blob) - var mod = `(function setup_${path}_module($_){${script}})` + var mod = `(function setup_module($_){${script}})` var fn = js.eval(path, mod) var result = fn.call(sym); use_cache[cache_path] = result; @@ -114,7 +112,7 @@ stone.p = function(object) var actor_mod = use('actor') var wota = use('wota') var nota = use('nota') -globalThis.text = use('text') +globalThis.text = use('internal/text') var ENETSERVICE = 0.1 var REPLYTIMEOUT = 60 // seconds before replies are ignored @@ -231,7 +229,6 @@ function get_package_from_path(path) { globalThis.json = use('json') var time = use('time') -var st_now = time.number() var default_config = { ar_timer: 60, @@ -653,16 +650,18 @@ if (!program) { // Find the package containing the program // The program path is resolved relative to current directory -var package_dir = shop.set_current_package(program) -if (package_dir) { - // Reload config from the package - config = shop.load_config() - if (config) { - config.system = config.system || {} - config.system.__proto__ = default_config - cell.config = config - } -} +// Find the package containing the program +// The program path is resolved relative to current directory +// var package_dir = shop.set_current_package(program) +// if (package_dir) { +// // Reload config from the package +// config = shop.load_config() +// if (config) { +// config.system = config.system || {} +// config.system.__proto__ = default_config +// cell.config = config +// } +// } function handle_actor_disconnect(id) { var greeter = greeters[id] diff --git a/internal/text.c b/internal/text.c index 6ecff687..7318b39f 100644 --- a/internal/text.c +++ b/internal/text.c @@ -298,7 +298,7 @@ static const JSCFunctionListEntry js_text_funcs[] = { MIST_FUNC_DEF(text, base64url_to_blob, 1), }; -JSValue js_text_use(JSContext *js) +JSValue js_internal_text_use(JSContext *js) { JSValue mod = JS_NewObject(js); JS_SetPropertyFunctionList(js, mod, js_text_funcs, countof(js_text_funcs)); diff --git a/meson.build b/meson.build index ffb796fc..b9691203 100644 --- a/meson.build +++ b/meson.build @@ -52,34 +52,33 @@ src += [ # core src += ['scheduler.c'] scripts = [ - 'nota.c', - 'js.c', + 'internal/nota.c', + 'debug/js.c', 'qop.c', 'wildstar.c', 'fit.c', 'crypto.c', - 'text.c', + 'internal/text.c', 'utf8.c', - 'kim.c', + 'internal/kim.c', 'time.c', - 'nota.c', - 'debug.c', - 'os.c', + 'internal/nota.c', + 'debug/debug.c', + 'internal/os.c', 'fd.c', - 'http.c', - 'enet.c', + 'net/http.c', + 'net/enet.c', 'wildstar.c', - 'miniz.c', - 'json.c' + 'archive/miniz.c', + 'internal/json.c' ] foreach file: scripts - full_path = join_paths('scripts', file) - sources += files(full_path) + sources += files(file) endforeach srceng = 'source' -includes = [srceng] +includes = [srceng, 'internal', 'debug', 'net', 'archive'] foreach file : src full_path = join_paths(srceng, file) @@ -99,7 +98,6 @@ else link += '-Wl,-export_dynamic' endif - cell_so = shared_library( 'cell_runtime', sources, @@ -108,14 +106,6 @@ cell_so = shared_library( install : true, ) -# Create core.zip from scripts folder -qop_target = custom_target('core.qop', - output: 'core.qop', - command: ['sh', '-c', ' qopconv -d ' + meson.project_source_root() / 'scripts . core.qop'], - build_by_default: true, - build_always_stale: true -) - # Determine RPATH based on OS # MacOS uses @loader_path, Linux uses $ORIGIN # We include both the current directory (for build) and ../lib (for install) @@ -127,28 +117,14 @@ elif host_machine.system() == 'linux' endif # Create main executable linked against the cell library -cell_exe = executable('cell_exe', +cell_exe = executable('cell', 'source/main.c', dependencies: deps, include_directories: includers, link_with: cell_so, link_args: link, build_rpath: ':'.join(rpath_dirs), - install: false -) - -# Create final cell executable by appending core.zip to cell_exe -cell = custom_target('cell', - input: [cell_exe, qop_target], - output: 'cell' + exe_ext, - command: [ - 'sh', '-c', - 'cp "$1" "$3" && cat "$2" >> "$3" && chmod +x "$3"', - 'cell-cat', '@INPUT0@', '@INPUT1@', '@OUTPUT@' - ], - build_by_default: true, - install: true, - install_dir: get_option('bindir') + install: true ) # Install headers for building dynamic libraries using Cell diff --git a/shop.cm b/shop.cm index 9aa15d67..aa651df6 100644 --- a/shop.cm +++ b/shop.cm @@ -10,13 +10,13 @@ var utf8 = use('utf8') var blob = use('blob') var build_utils = use('build') +var core = "gitea.pockle.world/john/cell" + function content_hash(content) { return text(crypto.blake2(utf8.encode(content)), 'h') } -log.console(content_hash("hello")) - // a package string is what is used to import a module, like prosperon/sprite // in prosperon/sprite, sprite is the module, and prosperon is the package (usually, an alias) // a canonical package name relates prosperon to its source, like gitea.pockle.world/john/prosperon @@ -27,9 +27,6 @@ var Shop = {} // Located at ~/.cell on the user's machine var global_shop_path = null -// Current package context - the package we're running from (resolved from program path) -var current_package_path = null - var SCOPE_LOCAL = 0 var SCOPE_PACKAGE = 1 var SCOPE_CORE = 2 @@ -165,21 +162,7 @@ function update_local_lock(abs_path) { Shop.ensure_package_link = ensure_package_link // Set the current package context from a program path -Shop.set_current_package = function(program_path) { - current_package_path = Shop.find_package_dir(program_path) - - if (current_package_path && current_package_path.startsWith('/')) { - // It's a local package, ensure it is linked in the shop - ensure_package_link(current_package_path) - } - - return current_package_path -} -// Get the current package path -Shop.get_current_package = function() { - return current_package_path -} Shop.set_os = function(o, $guy) { @@ -203,9 +186,6 @@ function get_config_path(package_path) { if (package_path) { return package_path + '/cell.toml' } - if (current_package_path) { - return current_package_path + '/cell.toml' - } // Fallback to current directory return 'cell.toml' } @@ -351,6 +331,10 @@ function get_path_in_package(path, ctx) return canon_pkg + "/" + mod_name } +// Given a path like 'prosperon/sprite', extract the package alias ('prosperon') +// and resolve it to its canonical path using the dependencies in ctx's config. +// Returns the canonical package path (e.g., 'gitea.pockle.world/john/prosperon') +// or null if the path has no package prefix or the package is not found. function get_normalized_package(path, ctx) { var pkg = get_import_package(path) @@ -773,35 +757,13 @@ Shop.check_cache = function(pkg) { var open_dls = {} // for script forms, path is the canonical path of the module -var script_forms = [] - -// Construct a descriptive compile name with extension preserved. -// Formats: -// core: -// : (package is the full canonical package name) -// local: -// package: (fallback when pkg isn't provided but path is under modules) -function make_compile_name(path, rel_path, pkg, scope) { - if (scope == SCOPE_CORE) return 'core:' + rel_path - if (pkg) return pkg + ':' + rel_path - var modules_dir = get_modules_dir() - if (path && path.startsWith(modules_dir + '/')) return 'package:' + rel_path - return 'local:' + rel_path -} - -script_forms['.cm'] = function(path, script, pkg) { +var script_form = function(path, script, pkg) { var pkg_arg = pkg ? `'${pkg}'` : 'null' var relative_use_fn = `def use = function(path) { return globalThis.use(path, ${pkg_arg});}` var fn = `(function setup_module($_){ ${relative_use_fn}; ${script}})` return fn } -script_forms['.ce'] = function(path, script, pkg) { - var pkg_arg = pkg ? `'${pkg}'` : 'null' - var relative_use_fn = `def use = function(path) { return globalThis.use(path, ${pkg_arg});}` - return `(function start($_, arg) { ${relative_use_fn}; var args = arg; ${script} ; })` -} - // Get flags from config function get_flags(config, platform, key) { var flags = '' @@ -817,170 +779,108 @@ function get_flags(config, platform, key) { Shop.get_flags = get_flags -function get_build_dir(pkg) { - if (!pkg) pkg = current_package_path - if (!pkg) return get_global_build_dir() + '/local' // Fallback for non-package scripts - // If pkg is absolute path, mirror it - if (pkg.startsWith('/')) { - // Accio folder should be .cell/packages/Users/... - // But build should be .cell/build/packages/Users/... - return get_global_build_dir() + '/packages' + pkg - } - - // Otherwise it's a relative package path (from packages dir) - return get_global_build_dir() + '/packages/' + pkg -} -Shop.get_build_dir = get_build_dir -Shop.get_global_build_dir = get_global_build_dir - -function get_rel_path(path, pkg) { - if (!pkg) { - // For local files, strip the current package path prefix if present - if (current_package_path && path.startsWith(current_package_path + '/')) { - return path.substring(current_package_path.length + 1) - } - return path - } - - var prefix - if (pkg.startsWith('/')) { - prefix = pkg + '/' - } else { - prefix = get_modules_dir() + '/' + pkg + '/' - } - - if (path.startsWith(prefix)) { - return path.substring(prefix.length) - } - return path -} - -function resolve_mod_fn(path, pkg) -{ +// Resolve module function +function resolve_mod_fn(path, pkg) { if (!fd.is_file(path)) throw new Error(`path ${path} is not a file`) - var rel_path = get_rel_path(path, pkg) - - // Determine build directory based on context - var build_dir - if (pkg) { - build_dir = get_build_dir(pkg) - } else if (current_package_path) { - // Local file in current package - use package path (strip leading /) - var pkg_id = current_package_path.substring(1) // Remove leading / - build_dir = get_global_build_dir() + '/' + pkg_id - } else { - build_dir = get_build_dir('local') - } - - var cache_path = build_dir + '/' + rel_path + '.o' - - if (fd.is_file(cache_path) && fd.stat(path).mtime <= fd.stat(cache_path).mtime) { - var obj = fd.slurp(cache_path) + + var content = fd.slurp(path) + var hash = content_hash(content) + var hash_path = get_global_build_dir() + '/' + hash + + if (fd.is_file(hash_path)) { + var obj = fd.slurp(hash_path) var fn = js.compile_unblob(obj) return js.eval_compile(fn) } - var ext = path.substring(path.lastIndexOf('.')) - var script_form = script_forms[ext] - if (!script_form) throw new Error(`No script form for extension ${ext}`) - - var compile_name = make_compile_name(path, rel_path, pkg, pkg ? SCOPE_PACKAGE : SCOPE_LOCAL) - - var script = script_form(path, text(fd.slurp(path)), pkg) + var form = script_form + + // We don't really use pkg scope for compilation anymore since it's just hash based cache + // But we need to pass a package context for the 'use' function inside the module + var script = form(path, text(content), pkg); + + // Compile name is just for debug/stack traces + var compile_name = pkg ? pkg + ':' + path : 'local:' + path + var fn = js.compile(compile_name, script) - ensure_dir(cache_path.substring(0, cache_path.lastIndexOf('/'))) - fd.slurpwrite(cache_path, js.compile_blob(fn)) + + // Ensure build dir exists + ensure_dir(get_global_build_dir()) + fd.slurpwrite(hash_path, js.compile_blob(fn)) + return js.eval_compile(fn) } -// Resolve and cache a core module -function resolve_core_mod_fn(core_path, rel_path) { - var build_dir = get_global_build_dir() + '/core' - var cache_path = build_dir + '/' + rel_path + '.o' - - if (fd.is_file(cache_path) && fd.stat(core_path).mtime <= fd.stat(cache_path).mtime) { - var obj = fd.slurp(cache_path) - var fn = js.compile_unblob(obj) - return js.eval_compile(fn) - } - - var ext = core_path.substring(core_path.lastIndexOf('.')) - var form = script_forms[ext] - if (!form) throw new Error(`No script form for extension ${ext}`) - - var compile_name = make_compile_name(core_path, rel_path, null, SCOPE_CORE) - var script = form(null, text(fd.slurp(core_path))) - var fn = js.compile(compile_name, script) - ensure_dir(cache_path.substring(0, cache_path.lastIndexOf('/'))) - fd.slurpwrite(cache_path, js.compile_blob(fn)) - return js.eval_compile(fn) -} +// resolve_core_mod_fn is no longer needed as core modules are just modules in a package (or local) + + function resolve_locator(path, ext, ctx) { - // In static_only mode, only look in the embedded pack - var static_only = cell.static_only + if (path.endsWith(ext)) ext = '' + + // 1. Check local file (relative to current directory if no context, or relative to package 'ctx' if provided) + // If ctx is provided, it's a package alias or path. + var local_path + if (ctx) { + var mod_dir = get_modules_dir() + // Check if ctx is an absolute path (local package) + if (ctx.startsWith('/')) { + local_path = ctx + '/' + path + ext + } else { + local_path = mod_dir + '/' + ctx + '/' + path + ext + } + } else { + // No context, just check simple local path + local_path = path + ext + } - if (!static_only) { - // First, check if file exists in current package directory - if (current_package_path) { - var pkg_local_path = current_package_path + '/' + path + ext - if (fd.is_file(pkg_local_path)) { - var fn = resolve_mod_fn(pkg_local_path, null) - return {path: pkg_local_path, scope: SCOPE_LOCAL, symbol:fn} - } - } - - // Check CWD for local file - var local_path - if (ctx) - local_path = get_modules_dir() + '/' + ctx + '/' + path + ext - else - local_path = path + ext - - if (fd.is_file(local_path)) { + if (fd.is_file(local_path)) { var fn = resolve_mod_fn(local_path, ctx) return {path: local_path, scope: SCOPE_LOCAL, symbol:fn} - } - - // Check installed packages - var canonical_pkg = get_normalized_package(path, ctx) - var pkg_path = get_path_in_package(path, ctx) - var mod_path = get_modules_dir() + '/' + pkg_path + ext - if (fd.is_file(mod_path)) { - var fn = resolve_mod_fn(mod_path, canonical_pkg) - return {path: mod_path, scope: SCOPE_PACKAGE, symbol:fn} - } + } else { + // Fallback: check if the path itself is a file (ignoring required extension) + // This allows running scripts like 'test.cm' even if engine asks for '.ce' + if (ext && path != local_path && fd.is_file(path)) { + var fn = resolve_mod_fn(path, ctx) + return {path: path, scope: SCOPE_LOCAL, symbol:fn} + } } - // Check core directory for core modules - var core_dir = Shop.get_core_dir() - - // For packages, try the full package path first in core - if (ctx) { - var pkg_rel = ctx + '/' + path + ext - var pkg_core_path = core_dir + '/' + pkg_rel - if (fd.is_file(pkg_core_path)) { - var fn = resolve_core_mod_fn(pkg_core_path, pkg_rel) - return {path: pkg_rel, scope: SCOPE_CORE, symbol:fn}; - } + + // 2. Check installed packages (if path suggests a package import) + // This handles imports like 'prosperon/sprite' + var canonical_pkg = get_normalized_package(path, ctx) + if (canonical_pkg) { + var pkg_path = get_path_in_package(path, ctx) + var mod_path = get_modules_dir() + '/' + pkg_path + ext + if (fd.is_file(mod_path)) { + var fn = resolve_mod_fn(mod_path, canonical_pkg) + return {path: mod_path, scope: SCOPE_PACKAGE, symbol:fn} + } } - - // Check core directory for the module - // Core scripts are now in .cell/core/scripts + + // 3. Check core (as a fallback) + // "core" is now just another package, potentially. + // But if the user really wants to load "time", "js", etc, which are in core. + // We can try to resolve them in the core package. + // Ideally 'core' is defined in dependencies if needed, or we hardcode a fallback. + // Hardcoded fallback for now to match behavior: var core_dir = Shop.get_core_dir() - var core_file_path = core_dir + '/scripts/' + path + ext - if (path == 'text') log.console("Checking core mod: " + core_file_path + " exists: " + fd.is_file(core_file_path)) + var core_file_path = core_dir + '/' + path + ext + if (fd.is_file(core_file_path)) { - var fn = resolve_core_mod_fn(core_file_path, path + ext) + // Core is treated as a package now, essentially + var fn = resolve_mod_fn(core_file_path, 'core') // using 'core' string as package for now return {path: path + ext, scope: SCOPE_CORE, symbol:fn}; } return null; } + function c_sym_path(path) { return path.replace(/\//g, '_').replace(/\\/g, '_').replace(/\./g, '_').replace(/-/g, '_') @@ -989,14 +889,16 @@ function c_sym_path(path) function resolve_c_symbol(path, package_context) { var static_only = cell.static_only - var local_path = package_context ? package_context : 'local' var local_sym_base = c_sym_path(path) - var local + // Candidates for symbol names function symbol_candidates(pkg_path, mod_sym) { var variants = [] + // if pkg_path is 'gitea.pockle.world/john/prosperon', we want: + // js_gitea_pockle_world_john_prosperon_mod_use + // and maybe with leading slash? var paths = [pkg_path] - if (!pkg_path.startsWith('/')) paths.push('/' + pkg_path) + // if (!pkg_path.startsWith('/')) paths.push('/' + pkg_path) // unlikely to need slash variant for standard pkgs for (var i = 0; i < paths.length; i++) { var candidate = `js_${c_sym_path(paths[i])}_${mod_sym}_use` if (variants.indexOf(candidate) < 0) @@ -1004,37 +906,40 @@ function resolve_c_symbol(path, package_context) } return variants } - - if (!package_context) { - local = `js_local_${local_sym_base}_use` - } else { - local = null // handled via candidates below - } - - // First check for statically linked/internal symbols - var local_candidates = package_context ? symbol_candidates(local_path, local_sym_base) : [local] - for (var li = 0; li < local_candidates.length; li++) { - var lc = local_candidates[li] - if (os.internal_exists(lc)) - return { - symbol: function() { return os.load_internal(lc); }, - scope: SCOPE_LOCAL, - path: lc - }; - } - // In static_only mode, skip dynamic library lookups + // 1. Check internal symbols (statically linked) + // If we have a package context, we check package-prefixed symbols + // If not, we look for 'js_local_...' or 'js_path_use' directly? + + if (package_context) { + // Check package internal symbols + var variants = symbol_candidates(package_context, local_sym_base) + for (var i = 0; i < variants.length; i++) { + if (os.internal_exists(variants[i])) { + return { + symbol: function() { return os.load_internal(variants[i]); }, + scope: SCOPE_PACKAGE, + path: variants[i] + } + } + } + + // In static_only mode, skip dynamic library lookups if (!static_only) { // Then try dynamic library - var build_dir = get_build_dir(package_context) - var local_dl_name = build_dir + '/libcellmod' + dylib_ext + // Use ./libcellmod for local? + var local_dl_name = './libcellmod' + dylib_ext + // Or check package context dir if it's a local package path? + if (package_context && package_context.startsWith('/')) { + local_dl_name = package_context + '/libcellmod' + dylib_ext + } if (fd.is_file(local_dl_name)) { if (!open_dls[local_dl_name]) open_dls[local_dl_name] = os.dylib_open(local_dl_name); if (open_dls[local_dl_name]) { - var locals = package_context ? symbol_candidates(local_path, local_sym_base) : [local] + var locals = package_context ? symbol_candidates(package_context, local_sym_base) : [`js_local_${local_sym_base}_use`] for (var i = 0; i < locals.length; i++) { var candidate = locals[i] if (os.dylib_has_symbol(open_dls[local_dl_name], candidate)) @@ -1047,8 +952,19 @@ function resolve_c_symbol(path, package_context) } } } + } else { + // Local context + var local_sym = `js_local_${local_sym_base}_use` + if (os.internal_exists(local_sym)) { + return { + symbol: function() { return os.load_internal(local_sym); }, + scope: SCOPE_LOCAL, + path: local_sym + } + } + } - // If 'path' has a package alias (e.g. 'prosperon/sprite'), try to resolve it + // 2. Check if valid package import (e.g. 'prosperon/sprite') var pkg_alias = get_import_package(path) if (pkg_alias) { var canon_pkg = get_normalized_package(path, package_context) @@ -1057,21 +973,20 @@ function resolve_c_symbol(path, package_context) var mod_sym = mod_name.replace(/\//g, '_').replace(/-/g, '_').replace(/\./g, '_') var sym_names = symbol_candidates(canon_pkg, mod_sym) - // First check internal/static symbols for package - for (var sii = 0; sii < sym_names.length; sii++) { - var sym_name = sym_names[sii] - if (os.internal_exists(sym_name)) - return { - symbol: function() { return os.load_internal(sym_name) }, - scope: SCOPE_PACKAGE, - package: canon_pkg, - path: sym_name - }; + for (var i = 0; i < sym_names.length; i++) { + if (os.internal_exists(sym_names[i])) { + return { + symbol: function() { return os.load_internal(sym_names[i]); }, + scope: SCOPE_PACKAGE, + package: canon_pkg, + path: sym_names[i] + } + } } - // Then try dynamic library for package (skip in static_only mode) if (!static_only) { - var pkg_build_dir = get_build_dir(canon_pkg) + // Check package dir for libcellmod + var pkg_build_dir = get_modules_dir() + '/' + canon_pkg var dl_path = pkg_build_dir + '/libcellmod' + dylib_ext if (fd.is_file(dl_path)) { if (!open_dls[dl_path]) open_dls[dl_path] = os.dylib_open(dl_path) @@ -1092,6 +1007,7 @@ function resolve_c_symbol(path, package_context) } } + // 3. Check Core/General fallback (js_path_use) var core_sym = `js_${path.replace(/\//g, '_')}_use`; if (os.internal_exists(core_sym)) return { @@ -1150,14 +1066,20 @@ function execute_module(info) var used - if (c_resolve.scope < mod_resolve.scope) - used = c_resolve.symbol(null, $_) - else if (mod_resolve.scope < c_resolve.scope) - used = mod_resolve.symbol.call(null, $_) - else - used = mod_resolve.symbol.call(c_resolve.symbol(), $_) - - if (!used) + // If we have a script, it always takes precedence (containing the C symbol if available) + // This supports "Hybrid" modules where JS wraps C. + if (mod_resolve.scope < 900) { + var context = null + if (c_resolve.scope < 900) { + context = c_resolve.symbol(null, $_) + } + used = mod_resolve.symbol.call(context, $_) + } else if (c_resolve.scope < 900) { + // C only + used = c_resolve.symbol(null, $_) + } else { + throw new Error(`Module ${info.path} could not be found`) + } if (!used) throw new Error(`Module ${json.encode(info)} returned null`) return used @@ -1172,10 +1094,6 @@ function get_module(path, package_context) { return execute_module(info) } -// first looks in local -// then in dependencies -// then in core -// package_context: optional package context to resolve relative paths within Shop.use = function(path, package_context) { var info = resolve_module_info(path, package_context) if (!info) @@ -1585,7 +1503,7 @@ Shop.remove_replacement = function(alias) { // List all files in a package Shop.list_files = function(pkg) { var dir - if (!pkg) dir = current_package_path || '.' + if (!pkg) dir = '.' else dir = get_modules_dir() + '/' + pkg var files = [] diff --git a/source/cell.c b/source/cell.c index 0112c816..8c4cb79f 100644 --- a/source/cell.c +++ b/source/cell.c @@ -11,7 +11,7 @@ #include "cell.h" #include "cell_internal.h" -#define ENGINE "scripts/engine.cm" +#define ENGINE "internal/engine.cm" #define CELL_SHOP_DIR ".cell" #define CELL_CORE_DIR "core" diff --git a/internal/time.c b/time.c similarity index 96% rename from internal/time.c rename to time.c index 9833023b..0dbffaf1 100644 --- a/internal/time.c +++ b/time.c @@ -69,7 +69,7 @@ static const JSCFunctionListEntry js_time_funcs[] = { }; JSValue -js_time_use(JSContext *ctx) +js_internal_time_use(JSContext *ctx) { JSValue obj = JS_NewObject(ctx); JS_SetPropertyFunctionList(ctx, obj, @@ -78,3 +78,9 @@ js_time_use(JSContext *ctx) sizeof(js_time_funcs[0])); return obj; } + +JSValue +js_time_use(JSContext *ctx) +{ + return js_internal_time_use(ctx); +} diff --git a/internal/time.cm b/time.cm similarity index 100% rename from internal/time.cm rename to time.cm diff --git a/internal/time_playdate.c b/time_playdate.c similarity index 100% rename from internal/time_playdate.c rename to time_playdate.c