reduce shop
This commit is contained in:
@@ -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 <name/mod> is requested, from a within package <a> ..
|
||||
|
||||
1) cell looks within package <a> for a folder <name> and loads <name/mod>, if present
|
||||
2) cell looks for a package <name> with a top level <mod>
|
||||
3) cell looks in the core for <name/mod>
|
||||
3) cell looks in the core for <name/mod>
|
||||
|
||||
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.
|
||||
@@ -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]
|
||||
@@ -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));
|
||||
|
||||
54
meson.build
54
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
|
||||
|
||||
382
shop.cm
382
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:<module path>
|
||||
// <package>:<module path> (package is the full canonical package name)
|
||||
// local:<module path>
|
||||
// package:<module path> (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 = []
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user