This commit is contained in:
2025-12-07 13:50:57 -06:00
parent 14cf48931d
commit 16aba47782
22 changed files with 549 additions and 1291 deletions

View File

@@ -16,7 +16,7 @@ main = true
main = true
[compilation]
CFLAGS = "-Wno-incompatible-pointer-types -Wno-missing-braces -Wno-strict-prototypes -Wno-unused-function -Wno-int-conversion"
CFLAGS = "-Isource -Wno-incompatible-pointer-types -Wno-missing-braces -Wno-strict-prototypes -Wno-unused-function -Wno-int-conversion"
LDFLAGS = "-lstdc++ -lm"
[compilation.macOS]

140
Makefile
View File

@@ -1,90 +1,56 @@
debug: FORCE
# Development build: creates libcell_runtime.dylib + thin main wrapper
# This is the default target for working on cell itself
#
# If cell doesn't exist yet, use 'make bootstrap' first (requires meson)
# or manually build with meson once.
cell: libcell_runtime.dylib cell_main core.qop
cat cell_main > cell
cat core.qop >> cell
chmod +x cell
cp cell /opt/homebrew/bin/cell
cp libcell_runtime.dylib /opt/homebrew/lib/
# Build the shared runtime library (everything except main.c)
# Uses existing cell to run build -d
libcell_runtime.dylib: .cell/build/dynamic
cell build -d
cp .cell/build/dynamic/libcell_runtime.dylib .
# Build the thin main wrapper that links to libcell_runtime
cell_main: source/main.c libcell_runtime.dylib
cc -o cell_main source/main.c -L. -lcell_runtime -Wl,-rpath,@loader_path -Wl,-rpath,/opt/homebrew/lib
# Create core.qop from scripts folder
core.qop: scripts/*.cm scripts/*.c
cell qopconv -d scripts . core.qop
# Static build: creates a fully static cell binary (for distribution)
static:
cell build
cp .cell/build/static/cell .
cat core.qop >> cell
# Bootstrap: build cell from scratch using meson (only needed once)
bootstrap:
meson setup build_bootstrap -Dbuildtype=release
meson compile -C build_bootstrap
cp build_bootstrap/cell .
cp build_bootstrap/libcell_runtime.dylib .
@echo "Bootstrap complete. Now run 'make' to rebuild with cell itself."
# Clean build artifacts
clean:
rm -rf .cell/build build_bootstrap
rm -f cell cell_main libcell_runtime.dylib core.qop
# Ensure dynamic build directory exists
.cell/build/dynamic:
mkdir -p .cell/build/dynamic
# Legacy meson target
meson:
meson setup build_dbg -Dbuildtype=debugoptimized
meson install -C build_dbg
fast: FORCE
meson setup build_fast
meson install -C build_fast
release: FORCE
meson setup -Dbuildtype=release -Db_lto=true -Db_lto_mode=thin -Db_ndebug=true build_release
meson install -C build_release
sanitize: FORCE
meson setup -Db_sanitize=address -Db_sanitize=memory -Db_sanitize=leak build_sani
meson install -C build_sani
thread: FORCE
meson setup build_thread -Db_sanitize=thread -Dbuildtype=debugoptimized
meson install -C build_thread
small: FORCE
meson setup -Dbuildtype=minsize -Db_lto=true -Db_ndebug=true build_small
meson install -C build_small
single: FORCE
meson setup build_single -Dsingle_threaded=true -Dbuildtype=debugoptimized
meson install -C build_single
web: FORCE
meson setup -Deditor=false -Dbuildtype=minsize -Db_lto=true -Db_ndebug=true --cross-file emscripten.cross build_web
meson compile -C build_web
crosswin: FORCE
meson setup -Dbuildtype=debugoptimized --cross-file mingw32.cross build_win
meson compile -C build_win
playdate: FORCE
meson setup -Dbuildtype=debugoptimized --cross-file playdate.cross build_playdate
meson compile -C build_playdate
FORCE:
IMAGE_LINUX := prosperon/linux-builder:latest
IMAGE_MINGW := prosperon/mingw-builder:latest
IMAGE_EMSCRIPTEN := prosperon/emscripten-builder:latest
PWD := $(shell pwd)
ARTIFACTS_DIR := artifacts
build:
meson setup build -Dbuildtype=release -Db_lto=true -Db_lto_mode=thin -Db_ndebug=true
meson compile -C build
dockerclean:
rm -rf build build-win $(ARTIFACTS_DIR)
dockerlinux: build-linux-image run-linux
build-linux-image:
docker build -f .github/docker/Dockerfile.linux -t $(IMAGE_LINUX) .
run-linux:
@mkdir -p $(ARTIFACTS_DIR)/linux
docker run --rm -v $(PWD):/src -w /src $(IMAGE_LINUX) bash -lc 'meson setup build -Dbuildtype=release -Db_lto=true -Db_ndebug=true && meson compile -C build && cp build/cell $(ARTIFACTS_DIR)/linux/'
dockerwin: build-mingw-image run-win
build-mingw-image:
docker build -f .github/docker/Dockerfile.mingw -t $(IMAGE_MINGW) .
run-win:
@mkdir -p $(ARTIFACTS_DIR)/windows
docker run --rm -v $(PWD):/src -w /src $(IMAGE_MINGW) bash -lc 'meson setup build-win --cross-file mingw32.cross -Dbuildtype=release -Db_lto=true -Db_ndebug=true && meson compile -C build-win && cp build-win/cell.exe $(ARTIFACTS_DIR)/windows/'
dockeremc: build-emscripten-image run-emc
build-emscripten-image:
docker build -f .github/docker/Dockerfile.emscripten -t $(IMAGE_EMSCRIPTEN) .
run-emc:
@mkdir -p $(ARTIFACTS_DIR)/emscripten
docker run --rm -v $(PWD):/src -w /src $(IMAGE_EMSCRIPTEN) bash -lc 'meson setup build-emscripten --cross-file emscripten.cross -Dbuildtype=release -Db_ndebug=true -Ddefault_library=static -Dcpp_std=c++11 && meson compile -C build-emscripten && cp build-emscripten/cell.wasm build-emscripten/cell.js $(ARTIFACTS_DIR)/emscripten/'
WINFILES = cell.exe steam_api64.dll steam_appid.txt tangletart.bat
WINDIRS = scripts .cell accio prosperon
win_release: FORCE
make dockerwin
cp build-win/cell.exe .
cp sdk/redistributable_bin/win64/steam_api64.dll .
zip -r $@ $(WINFILES) $(WINDIRS) --exclude=*.git* --exclude=*_doc* --exclude=*_concepts*
.PHONY: cell static bootstrap clean meson

View File

@@ -70,6 +70,7 @@ scripts = [
'enet.c',
'wildstar.c',
'miniz.c',
'json.c'
]
foreach file: scripts

View File

@@ -1,245 +0,0 @@
Object.mixin = function (target, source) {
if (typeof source != "object") return target;
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
return target;
};
Object.defineProperty(Object.prototype, "mixin", {
value: function mixin(obj) {
if (typeof obj == "string") obj = use(obj);
if (obj) Object.mixin(this, obj);
},
});
/* STRING DEFS */
Object.defineProperty(String.prototype, "rm", {
value: function (index, endidx = index + 1) {
return this.slice(0, index) + this.slice(endidx);
},
});
Object.defineProperty(String.prototype, "tolast", {
value: function (val) {
var idx = this.lastIndexOf(val);
if (idx == -1) return this.slice();
return this.slice(0, idx);
},
});
Object.defineProperty(String.prototype, "dir", {
value: function () {
if (!this.includes("/")) return "";
return this.tolast("/");
},
});
Object.defineProperty(String.prototype, "next", {
value: function (char, from) {
if (!Array.isArray(char)) char = [char];
if (from > this.length - 1) return -1;
else if (!from) from = 0;
var find = this.slice(from).search(char[0]);
if (find == -1) return -1;
else return from + find;
var i = 0;
var c = this.charAt(from + i);
while (!char.includes(c)) {
i++;
if (from + i > this.length - 1) return -1;
c = this.charAt(from + i);
}
return from + i;
},
});
Object.defineProperty(String.prototype, "prev", {
value: function (char, from, count = 0) {
if (from > this.length - 1) return -1;
else if (!from) from = this.length - 1;
var find = this.slice(0, from).lastIndexOf(char);
while (count > 1) {
find = this.slice(0, find).lastIndexOf(char);
count--;
}
if (find == -1) return 0;
else return find;
},
});
Object.defineProperty(String.prototype, "strip_ext", {
value: function () {
return this.tolast(".");
},
});
Object.defineProperty(String.prototype, "ext", {
value: function () {
return this.fromlast(".");
},
});
Object.defineProperty(String.prototype, "up_path", {
value: function () {
var base = this.base();
var dirs = this.dir().split("/");
dirs.pop();
return dirs.join("/") + base;
},
});
Object.defineProperty(String.prototype, "fromlast", {
value: function (val) {
var idx = this.lastIndexOf(val);
if (idx == -1) return "";
return this.slice(idx + 1);
},
});
Object.defineProperty(String.prototype, "tofirst", {
value: function (val) {
var idx = this.indexOf(val);
if (idx == -1) return this.slice();
return this.slice(0, idx);
},
});
Object.defineProperty(String.prototype, "fromfirst", {
value: function (val) {
var idx = this.indexOf(val);
if (idx == -1) return this;
return this.slice(idx + val.length);
},
});
Object.defineProperty(String.prototype, "name", {
value: function () {
var idx = this.indexOf("/");
if (idx == -1) return this.tolast(".");
return this.fromlast("/").tolast(".");
},
});
Object.defineProperty(String.prototype, "set_name", {
value: function (name) {
var dir = this.dir();
return this.dir() + "/" + name + "." + this.ext();
},
});
Object.defineProperty(String.prototype, "base", {
value: function () {
return this.fromlast("/");
},
});
Object.defineProperty(String.prototype, "updir", {
value: function () {
if (this.lastIndexOf("/") == this.length - 1) return this.slice(0, this.length - 1);
var dir = (this + "/").dir();
return dir.dir();
},
});
/* ARRAY DEFS */
Object.defineProperty(Array.prototype, "filter!", {
value: function array_dofilter(fn) {
for (let i = 0; i < this.length; i++) {
if (!fn.call(this, this[i], i, this)) {
this.splice(i, 1);
i--;
}
}
return this;
},
});
Object.defineProperty(Array.prototype, "delete", {
value: function(item) {
var idx = this.indexOf(item);
if (idx > -1) this.splice(idx,1);
return null;
}
});
Object.defineProperty(Array.prototype, "copy", {
value: function () {
var c = [];
this.forEach(function (x, i) {
c[i] = deep_copy(x);
});
return c;
},
});
Object.defineProperty(Array.prototype, "equal", {
value: function equal(b) {
if (this.length != b.length) return false;
if (b == null) return false;
if (this == b) return true;
return JSON.stringify(this) == JSON.stringify(b);
for (var i = 0; i < this.length; i++) {
if (!this[i] == b[i]) return false;
}
return true;
},
});
Object.defineProperty(Array.prototype, "last", {
value: function () {
return this[this.length - 1];
},
});
Object.defineProperty(Array.prototype, "wrapped", {
value: function (x) {
var c = this.slice(0, this.length);
for (var i = 0; i < x; i++) c.push(this[i]);
return c;
},
});
Object.defineProperty(Array.prototype, "wrap_idx", {
value: function (x) {
while (x >= this.length) {
x -= this.length;
}
return x;
},
});
Object.defineProperty(Array.prototype, "mirrored", {
value: function (x) {
var c = this.slice(0);
if (c.length <= 1) return c;
for (var i = c.length - 2; i >= 0; i--) c.push(c[i]);
return c;
},
});

View File

@@ -1,11 +1,20 @@
// cell build [--target <target>] - Build a static cell binary for a project
// Collects all C files from cell source, scripts, and project packages,
// compiles them, and links into a single static executable.
// cell build [options] [actor] - Build cell binary
//
// Modes:
// cell build -d Build libcell_runtime.dylib (development mode)
// cell build [actor] Build static binary for project (release mode)
// cell build -s [actor] Build fully static binary (no external script lookup)
//
// The actor argument specifies the entry point (e.g., "accio" for accio.ce)
// If no actor is specified, builds cell itself.
var build = use('build')
var shop = use('shop')
var fd = use('fd')
var os = use('os')
var qop = use('qop')
var utf8 = use('utf8')
var json = use('json')
var targets = [
"arm64-macos",
@@ -18,6 +27,10 @@ var targets = [
// Parse arguments
var target = null
var dynamic_mode = false
var static_only = false // -s flag: don't look outside pack for scripts
var actor = null
for (var i = 0; i < args.length; i++) {
if (args[i] == '--target' || args[i] == '-t') {
if (i + 1 < args.length) {
@@ -28,13 +41,22 @@ for (var i = 0; i < args.length; i++) {
log.console("Available targets: " + targets.join(', '))
$_.stop()
}
} else if (args[i] == '-d' || args[i] == '--dynamic') {
dynamic_mode = true
} else if (args[i] == '-s' || args[i] == '--static-only') {
static_only = true
} else if (args[i] == '--help' || args[i] == '-h') {
log.console("Usage: cell build [--target <target>]")
log.console("Build a static cell binary for the current project.")
log.console("Usage: cell build [options] [actor]")
log.console("Build a cell binary for the current project.")
log.console("")
log.console("Options:")
log.console(" -d, --dynamic Build libcell_runtime.dylib (dev mode)")
log.console(" -s, --static-only Build fully static (no external scripts)")
log.console(" --target, -t <target> Cross-compile for target platform")
log.console("")
log.console("Arguments:")
log.console(" actor Entry point actor (e.g., 'accio' for accio.ce)")
log.console("")
log.console("Available targets: " + targets.join(', '))
$_.stop()
} else if (args[i] == '--list-targets') {
@@ -43,6 +65,8 @@ for (var i = 0; i < args.length; i++) {
log.console(" " + targets[t])
}
$_.stop()
} else if (!args[i].startsWith('-')) {
actor = args[i]
}
}
@@ -55,13 +79,13 @@ if (target) {
}
// Find cell package - it should be at ~/work/cell or a dependency
var cell_dir = null
// First check if we're in the cell directory itself
if (fd.is_file('source/cell.c') && fd.is_file('source/quickjs.c')) {
cell_dir = '.'
function find_cell_dir() {
// First check if we're in the cell directory itself
if (fd.is_file('source/cell.c') && fd.is_file('source/quickjs.c')) {
log.console("Building from cell source directory")
} else {
return '.'
}
// Check for cell as a local path dependency or linked package
var config = shop.load_config()
if (config && config.dependencies && config.dependencies.cell) {
@@ -69,21 +93,22 @@ if (fd.is_file('source/cell.c') && fd.is_file('source/quickjs.c')) {
var parsed = shop.parse_package(pkg)
var pkg_dir = '.cell/modules/' + parsed.path
if (fd.is_file(pkg_dir + '/source/cell.c')) {
cell_dir = pkg_dir
log.console("Using cell from dependency: " + pkg_dir)
return pkg_dir
}
}
// Fallback: try ~/work/cell
if (!cell_dir) {
var home_cell = os.getenv('HOME') + '/work/cell'
if (fd.is_file(home_cell + '/source/cell.c')) {
cell_dir = home_cell
log.console("Using cell from: " + cell_dir)
}
log.console("Using cell from: " + home_cell)
return home_cell
}
return null
}
var cell_dir = find_cell_dir()
if (!cell_dir) {
log.error("Could not find cell source. Add cell as a dependency or run from cell directory.")
$_.stop()
@@ -105,19 +130,18 @@ for (var i = 0; i < script_files.length; i++) {
// Select C files for target
var c_files = build.select_c_files(all_files, target)
// Debug: show which files were selected
if (target == 'playdate') {
for (var i = 0; i < c_files.length; i++) {
if (c_files[i].indexOf('fd') >= 0 || c_files[i].indexOf('main') >= 0) {
log.console(" Selected: " + c_files[i])
}
}
// For dynamic mode, exclude main.c
if (dynamic_mode) {
c_files = c_files.filter(function(f) {
return f.indexOf('main.c') < 0 && f.indexOf('main_') < 0
})
log.console("Dynamic mode: excluding main.c")
}
log.console("Found " + text(c_files.length) + " C files to compile")
// Get build directory
var build_dir = build.get_build_dir(target)
var build_dir = dynamic_mode ? '.cell/build/dynamic' : build.get_build_dir(target)
build.ensure_dir(build_dir)
// Load cell config for platform-specific flags
@@ -128,11 +152,16 @@ var platform = target_system || os.platform()
var cflags = build.get_flags(cell_config, platform, 'CFLAGS')
var ldflags = build.get_flags(cell_config, platform, 'LDFLAGS')
// For dynamic builds, add -fPIC
if (dynamic_mode) {
cflags = '-fPIC ' + cflags
}
// Compile options
var compile_options = {
target: target,
cflags: cflags,
includes: [cell_dir + '/source'],
includes: [],
defines: {}
}
@@ -167,9 +196,10 @@ for (var i = 0; i < c_files.length; i++) {
objects.push(obj)
}
// Collect C files from project packages
var packages = shop.list_packages()
for (var p = 0; p < packages.length; p++) {
// For static builds, also compile package C files
if (!dynamic_mode) {
var packages = shop.list_packages()
for (var p = 0; p < packages.length; p++) {
var pkg = packages[p]
var parsed = shop.parse_package(pkg)
var pkg_dir = '.cell/modules/' + parsed.path
@@ -204,7 +234,7 @@ for (var p = 0; p < packages.length; p++) {
var pkg_options = {
target: target,
cflags: pkg_cflags,
includes: [cell_dir + '/source', pkg_dir],
includes: [pkg_dir],
defines: { CELL_USE_NAME: use_name },
module_dir: pkg_dir
}
@@ -228,10 +258,10 @@ for (var p = 0; p < packages.length; p++) {
objects.push(obj)
}
}
}
}
// Collect C files from local project (only if not building from cell source directory)
if (cell_dir != '.') {
// Collect C files from local project (only if not building from cell source directory)
if (cell_dir != '.') {
var local_config = shop.load_config() || {}
var local_ldflags = build.get_flags(local_config, platform, 'LDFLAGS')
if (local_ldflags) {
@@ -257,7 +287,7 @@ if (cell_dir != '.') {
var local_options = {
target: target,
cflags: local_cflags,
includes: [cell_dir + '/source', '.'],
includes: ['.'],
defines: { CELL_USE_NAME: use_name }
}
@@ -280,40 +310,159 @@ if (cell_dir != '.') {
objects.push(obj)
}
}
}
}
log.console("Compiled " + text(objects.length) + " object files")
// Link into executable
var exe_ext = build.get_exe_ext(target)
var exe_name = build_dir + '/cell' + exe_ext
// Link
if (dynamic_mode) {
// Link into shared library
var dylib_ext = build.get_dylib_ext(target)
var lib_name = build_dir + '/libcell_runtime' + dylib_ext
var link_options = {
var link_flags = '-shared -fPIC'
if (platform == 'macOS' || platform == 'darwin') {
link_flags += ' -framework CoreFoundation -framework CFNetwork'
}
if (ldflags) link_flags += ' ' + ldflags
var objs_str = objects.join(' ')
var cmd = 'cc ' + link_flags + ' ' + objs_str + ' -o ' + lib_name
log.console("Linking " + lib_name)
var ret = os.system(cmd)
if (ret != 0) {
log.error("Linking failed")
$_.stop()
}
log.console("")
log.console("Build complete: " + lib_name)
log.console("")
log.console("To use: copy to /opt/homebrew/lib/ or set DYLD_LIBRARY_PATH")
} else {
// Link into executable
var exe_ext = build.get_exe_ext(target)
var exe_name = build_dir + '/cell' + exe_ext
var link_options = {
target: target,
ldflags: ldflags,
libs: []
}
}
// Add platform-specific libraries
if (!target || platform == 'macOS' || platform == 'darwin') {
// macOS needs no extra libs for static build
} else if (target == 'windows') {
// Add platform-specific libraries
if (!target || platform == 'macOS' || platform == 'darwin') {
link_options.ldflags += ' -framework CoreFoundation -framework CFNetwork'
} else if (target == 'windows') {
link_options.libs.push('ws2_32')
link_options.libs.push('winmm')
}
}
var result = build.link_executable(objects, exe_name, link_options)
if (!result) {
var result = build.link_executable(objects, exe_name, link_options)
if (!result) {
log.error("Linking failed")
$_.stop()
}
// Create the pack (core.qop + project scripts)
var pack_path = build_dir + '/pack.qop'
// Collect scripts to pack
var pack_files = []
// Always include core scripts from cell
var core_scripts = fd.readdir(cell_dir + '/scripts')
for (var i = 0; i < core_scripts.length; i++) {
var f = core_scripts[i]
if (f.endsWith('.cm') || f.endsWith('.ce')) {
pack_files.push({ path: f, source: cell_dir + '/scripts/' + f })
}
}
// If building a project (not cell itself), include project scripts
if (cell_dir != '.') {
// Include local scripts
var local_scripts = shop.list_modules()
for (var i = 0; i < local_scripts.length; i++) {
pack_files.push({ path: local_scripts[i], source: local_scripts[i] })
}
// Include package scripts
var packages = shop.list_packages()
for (var p = 0; p < packages.length; p++) {
var pkg = packages[p]
var parsed = shop.parse_package(pkg)
var pkg_dir = '.cell/modules/' + parsed.path
var pkg_scripts = shop.list_modules(parsed.path)
for (var i = 0; i < pkg_scripts.length; i++) {
var pack_name = parsed.path + '/' + pkg_scripts[i]
pack_files.push({ path: pack_name, source: pkg_dir + '/' + pkg_scripts[i] })
}
}
}
// Create the pack
log.console("Creating pack with " + text(pack_files.length) + " scripts")
var writer = qop.write(pack_path)
// Add entry point configuration if actor is specified
if (actor) {
var entry_config = {
entry: actor,
static_only: static_only
}
writer.add_file('__entry__.json', utf8.encode(json.encode(entry_config)))
}
for (var i = 0; i < pack_files.length; i++) {
var pf = pack_files[i]
var data = fd.slurp(pf.source)
if (data) {
writer.add_file(pf.path, data)
}
}
writer.finalize()
// Append pack to executable
var exe_data = fd.slurp(exe_name)
var pack_data = fd.slurp(pack_path)
fd.slurpwrite(exe_name, exe_data)
// Append pack data
var fh = fd.open(exe_name, 'a')
fd.write(fh, pack_data)
fd.close(fh)
// Determine final output name
var final_name
if (actor) {
// Named after the actor
var actor_base = actor
if (actor_base.endsWith('.ce')) actor_base = actor_base.substring(0, actor_base.length - 3)
if (actor_base.indexOf('/') >= 0) actor_base = actor_base.substring(actor_base.lastIndexOf('/') + 1)
final_name = actor_base + exe_ext
} else if (cell_dir != '.') {
// Named after the project directory
var cwd = fd.cwd()
var project_name = cwd.substring(cwd.lastIndexOf('/') + 1)
final_name = project_name + exe_ext
} else {
final_name = 'cell' + exe_ext
}
// Copy to project root
os.system('cp ' + exe_name + ' ' + final_name)
log.console("")
log.console("Build complete: " + final_name)
if (actor) {
log.console("Entry point: " + actor + ".ce")
} else {
log.console("Note: Run with an actor argument, e.g.: ./" + final_name + " main.ce")
}
}
// TODO: Append core.qop to executable
// For now, just report success
log.console("")
log.console("Build complete: " + exe_name)
log.console("")
log.console("Note: To create a fully functional cell binary, you need to append core.qop:")
log.console(" cat core.qop >> " + exe_name)
$_.stop()

View File

@@ -167,7 +167,7 @@ Build.get_target_ldflags = function(target) {
Build.get_target_system = function(target) {
var tc = Build.get_toolchain(target)
if (tc && tc.host_machine && tc.host_machine.system) return tc.host_machine.system
return null
return os_mod.platform()
}
// Get executable extension for target
@@ -282,9 +282,9 @@ Build.select_c_files = function(files, target) {
}
}
// Group key is just the generic name + extension (ignoring directory)
// This allows source/fd_playdate.c to override scripts/fd.c
var group_key = generic_name + ext
// Group key must include directory to avoid cross-directory collisions
// But still allow same-directory variants (e.g. source/scheduler.c vs source/scheduler_playdate.c)
var group_key = dir + generic_name + ext
if (!name_groups[group_key]) {
name_groups[group_key] = { generics: [], variants: {} }
}
@@ -489,7 +489,6 @@ Build.link_executable = function(objects, output, options) {
var ret = os_mod.system(cmd)
if (ret != 0) {
log.error("Linking failed")
log.error(cmd)
return null
}

View File

@@ -631,7 +631,29 @@ function report_to_overling(msg)
sys_msg(overling, {kind:'underling', message:msg, from: $_})
}
if (!cell.args.program)
// Check for embedded entry point configuration
var entry_config = null
var entry_data = core_qop.read('__entry__.json')
if (entry_data) {
try {
entry_config = json.decode(text(utf8.decode(entry_data)))
} catch(e) {}
}
// Determine the program to run
// Priority: 1. command line arg, 2. embedded entry point, 3. error
var program = cell.args.program
if (!program && entry_config && entry_config.entry) {
program = entry_config.entry
cell.args.program = program
}
// Store static_only mode for shop.cm to use
if (entry_config && entry_config.static_only) {
cell.static_only = true
}
if (!program)
os.exit(1)
function handle_actor_disconnect(id) {

63
scripts/json.c Normal file
View File

@@ -0,0 +1,63 @@
#include "cell.h"
static JSValue js_json_encode(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
if (argc < 1) return JS_ThrowTypeError(ctx, "json.encode requires at least 1 argument");
JSValue global = JS_GetGlobalObject(ctx);
JSValue json = JS_GetPropertyStr(ctx, global, "JSON");
JSValue stringify = JS_GetPropertyStr(ctx, json, "stringify");
JSValue args[3];
args[0] = argv[0]; // value
args[1] = (argc > 1) ? argv[1] : JS_NULL; // replacer
args[2] = (argc > 2) ? argv[2] : JS_NewInt32(ctx, 1); // space, default 1
JSValue result = JS_Call(ctx, stringify, json, 3, args);
JS_FreeValue(ctx, stringify);
JS_FreeValue(ctx, json);
JS_FreeValue(ctx, global);
if (argc <= 2) JS_FreeValue(ctx, args[2]);
return result;
}
static JSValue js_json_decode(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
if (argc < 1) return JS_ThrowTypeError(ctx, "json.decode requires at least 1 argument");
if (!JS_IsString(argv[0])) {
JSValue err = JS_NewError(ctx);
JS_DefinePropertyValueStr(ctx, err, "message",
JS_NewString(ctx, "couldn't parse text: not a string"),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
return JS_Throw(ctx, err);
}
JSValue global = JS_GetGlobalObject(ctx);
JSValue json = JS_GetPropertyStr(ctx, global, "JSON");
JSValue parse = JS_GetPropertyStr(ctx, json, "parse");
JSValue args[2];
args[0] = argv[0]; // text
args[1] = (argc > 1) ? argv[1] : JS_NULL; // reviver
JSValue result = JS_Call(ctx, parse, json, argc > 1 ? 2 : 1, args);
JS_FreeValue(ctx, parse);
JS_FreeValue(ctx, json);
JS_FreeValue(ctx, global);
return result;
}
static const JSCFunctionListEntry js_json_funcs[] = {
JS_CFUNC_DEF("encode", 1, js_json_encode),
JS_CFUNC_DEF("decode", 1, js_json_decode),
};
JSValue js_json_use(JSContext *js) {
JSValue export = JS_NewObject(js);
JS_SetPropertyFunctionList(js, export, js_json_funcs, sizeof(js_json_funcs)/sizeof(JSCFunctionListEntry));
return export;
}

View File

@@ -1,21 +0,0 @@
var json = {}
// Produce a JSON text from a Javascript object. If a record value, at any level, contains a json() method, it will be called, and the value it returns (usually a simpler record) will be JSONified.
// If the record does not have a json() method, and if whitelist is a record, then only the keys that are associated with true in the whitelist are included.
// If the space input is true, then line breaks and extra whitespace will be included in the text.
json.encode = function encode(val,replacer,space = 1,whitelist)
{
return JSON.stringify(val, replacer, space)
}
// The text text is parsed, and the resulting value (usually a record or an array) is returned.
// The optional reviver input is a method that will be called for every key and value at every level of the result. Each value will be replaced by the result of the reviver function. This can be used to reform data-only records into method-bearing records, or to transform date strings into seconds.
json.decode = function decode(text,reviver)
{
if (typeof text != 'string')
throw new Error("couldn't parse text: not a string")
return JSON.parse(text,reviver)
}
return json

View File

@@ -1,64 +0,0 @@
CELL-BUILD(1) Cell Manual CELL-BUILD(1)
NAME
cell build - Compile all modules to bytecode
SYNOPSIS
cell build
DESCRIPTION
The build command compiles all JavaScript modules in the modules/
directory to bytecode format. Compiled modules are stored in
.cell/build/ with a .jso extension and load faster than source files.
Cell's module system automatically uses compiled versions when
available, falling back to source files if needed.
OPERATION
1. Scans .cell/modules/ for all dependencies
2. Compiles each .js file to bytecode using QuickJS
3. Saves compiled bytecode with .jso extension in .cell/build/
4. Preserves module structure and naming
5. Updates module resolution to prefer compiled versions
COMPILATION PROCESS
For each module:
- Source: .cell/modules/chess@v1.0.0/main.js
- Output: .cell/build/chess@v1.0.0/main.jso
The compilation uses QuickJS bytecode format which:
- Loads faster than parsing source
- Provides some obfuscation
- Maintains full compatibility
EXAMPLES
Build all modules:
cell build
Typical workflow:
cell get git.world/jj/chess@v1.0.0
cell build
# Your code now uses compiled chess module
NOTES
- Compilation is optional - source modules work without building
- Compiled modules are preferred over source when available
- The .jso extension indicates QuickJS bytecode
- Bytecode is platform-independent
- TODO: Actual compilation implementation pending
FILES
.cell/build/
Contains all compiled modules
.cell/modules/
Source modules read from here
PERFORMANCE
Compiled modules provide:
- Faster load times (no parsing needed)
- Reduced memory usage during loading
- Same runtime performance as source
SEE ALSO
cell(1), cell-get(1), cell-vendor(1)

View File

@@ -1,124 +0,0 @@
CELL(1) Cell Manual CELL(1)
NAME
cell - The Cell module system for Prosperon game engine
SYNOPSIS
cell <command> [arguments]
DESCRIPTION
Cell is a module and dependency management system for Prosperon,
inspired by Go modules. It provides tools for managing dependencies,
building modules, and maintaining reproducible builds.
Cell uses a manifest file (cell.toml) to track dependencies and
project configuration. All Cell data is stored in the .cell/
directory within your project.
COMMANDS
init
Initialize a new Cell project with .cell/ directory structure
get <module> [alias]
Fetch a module and add it as a dependency
update <alias> [version]
Update a dependency to a new version
list
List installed modules and their status
vendor
Copy all dependencies into modules/ for hermetic builds
build
Compile all modules to bytecode in build/
patch <alias>
Create a patch file for local modifications to a dependency
help [command]
Display help information for Cell or a specific command
DIRECTORY STRUCTURE
.cell/
├── cell.toml Project manifest
├── lock.toml Dependency lock file with checksums
├── modules/ Vendored source modules
├── build/ Compiled bytecode modules
└── patches/ Local patches for dependencies
CONFIGURATION
The cell.toml file contains:
module = "my-game"
engine = "mist/prosperon@v0.9.3"
entrypoint = "main.js"
[dependencies]
alias = "git.world/user/module@version"
[aliases]
short = "alias"
[replace]
"git.world/user/module@v1.0.0" = "./local/path"
[patches]
module = "./patches/module.patch"
[mods]
enabled = ["mod1", "mod2"]
MODULE LOCATORS
Modules are identified by locators in the format:
host/owner/name@version
Examples:
- git.world/jj/mod@v0.6.3
- git.world/jj/mod@head
- git.world/jj/mod (defaults to @head)
IMPORT RESOLUTION
Cell supports multiple import styles:
1. Scheme-qualified: core://time, std://json
2. Relative paths: ./helper, ../utils
3. Bare imports: resolved via dependencies and aliases
MODULE PRECEDENCE
Modules are mounted in the following order (highest to lowest):
1. Mods (user modifications)
2. Self (project root)
3. Aliases (dependencies)
4. Compiled modules
5. Source modules
6. Core modules
EXAMPLES
Initialize a new project:
cell init
Add a dependency:
cell get git.world/jj/chess@v1.0.0
cell get git.world/jj/chess # uses latest
Update a dependency:
cell update chess v1.1.0
List installed modules:
cell list
Vendor dependencies:
cell vendor
Build all modules:
cell build
SEE ALSO
Run 'cell help <command>' for detailed information on each command.
AUTHORS
Cell is part of the Prosperon game engine project.

View File

@@ -1,102 +0,0 @@
.TH CELL-CONFIG 1 "2025" "Cell" "Cell Manual"
.SH NAME
cell config \- manage system and actor configurations
.SH SYNOPSIS
.B cell config
.I command
[options]
.SH DESCRIPTION
The
.B cell config
command manages configuration settings for the Cell system. It provides
functionality to view, set, and manage both system-wide and actor-specific
configuration values stored in
.IR .cell/cell.toml .
.SH COMMANDS
.TP
.B get \fIkey\fR
Get a configuration value. Keys use dot notation (e.g., system.ar_timer).
.TP
.B set \fIkey\fR \fIvalue\fR
Set a configuration value. Values are automatically parsed to appropriate types.
.TP
.B list
List all configuration values in a hierarchical format.
.TP
.B actor \fIname\fR get \fIkey\fR
Get an actor-specific configuration value.
.TP
.B actor \fIname\fR set \fIkey\fR \fIvalue\fR
Set an actor-specific configuration value.
.TP
.B actor \fIname\fR list
List all configuration values for a specific actor.
.SH SYSTEM KEYS
.TP
.B system.ar_timer
Seconds before idle actor reclamation (default: 60)
.TP
.B system.actor_memory
MB of memory an actor can use; 0 for unbounded (default: 0)
.TP
.B system.net_service
Seconds per network service pull (default: 0.1)
.TP
.B system.reply_timeout
Seconds to hold callback for reply messages; 0 for unbounded (default: 60)
.TP
.B system.actor_max
Maximum number of simultaneous actors (default: 10,000)
.TP
.B system.stack_max
MB of memory each actor's stack can grow to (default: 0)
.SH EXAMPLES
View a system configuration value:
.PP
.nf
cell config get system.ar_timer
.fi
.PP
Set a system configuration value:
.PP
.nf
cell config set system.net_service 0.2
.fi
.PP
Configure an actor-specific setting:
.PP
.nf
cell config actor prosperon/_sdl_video set resolution 1920x1080
cell config actor extramath/spline set precision high
.fi
.PP
List all configurations:
.PP
.nf
cell config list
.fi
.PP
List actor-specific configurations:
.PP
.nf
cell config actor prosperon/_sdl_video list
.fi
.SH FILES
.TP
.I .cell/cell.toml
The main configuration file containing all system and actor settings.
.SH NOTES
Configuration values are automatically parsed to appropriate types:
.IP \(bu 2
Boolean: true, false
.IP \(bu 2
Numbers: integers and floats (underscores allowed for readability)
.IP \(bu 2
Strings: everything else
.PP
Actor configurations are loaded automatically when an actor starts,
merging the actor-specific settings into the actor's args.
.SH SEE ALSO
.BR cell (1),
.BR cell-init (1),
.BR cell-get (1)

View File

@@ -1,70 +0,0 @@
CELL-GET(1) Cell Manual CELL-GET(1)
NAME
cell get - Fetch a module and add it as a dependency
SYNOPSIS
cell get <locator> [alias]
DESCRIPTION
The get command fetches a module from a repository and adds it to
your project's dependencies in cell.toml. The module is downloaded
to .cell/modules/ and can be imported using the specified alias.
If no version is specified in the locator, the command defaults to
using the head/master branch of the repository.
ARGUMENTS
locator
The module identifier in the format:
host/owner/name[@version]
Examples:
- git.world/jj/chess@v1.0.0
- git.world/jj/chess@head
- git.world/jj/chess (defaults to @head)
alias
Optional custom name for importing the module.
If not specified, uses the module name from the locator.
OPERATION
1. Parses the module locator
2. Initializes .cell/ if it doesn't exist
3. Adds the dependency to cell.toml
4. Creates the module directory in .cell/modules/
5. Downloads the module files (TODO: implementation pending)
6. Updates lock.toml with checksums (TODO: implementation pending)
EXAMPLES
Add a specific version:
cell get git.world/jj/chess@v1.0.0
Add latest version:
cell get git.world/jj/chess
Add with custom alias:
cell get git.world/jj/chess-engine@v2.0.0 chess
After adding, import in your code:
var chess = use('chess')
NOTES
- If a dependency with the same alias already exists, use the
update command instead
- The actual fetching from remote repositories is not yet
implemented; currently creates the directory structure only
- Module directories are named as: alias@version
FILES
.cell/cell.toml
Updated with the new dependency
.cell/modules/
Module files are stored here
.cell/lock.toml
Will contain checksums once implemented
SEE ALSO
cell(1), cell-update(1), cell-vendor(1)

View File

@@ -1,54 +0,0 @@
CELL-INIT(1) Cell Manual CELL-INIT(1)
NAME
cell init - Initialize a new Cell project
SYNOPSIS
cell init
DESCRIPTION
The init command creates the .cell/ directory structure for a new
Cell project. This includes all necessary subdirectories and a
default cell.toml configuration file.
If a .cell/ directory already exists, init will create any missing
subdirectories but will not overwrite existing files.
DIRECTORY STRUCTURE
Creates the following structure:
.cell/
├── cell.toml Project manifest with default configuration
├── lock.toml Empty lock file for dependency checksums
├── modules/ Directory for vendored source modules
├── build/ Directory for compiled bytecode modules
└── patches/ Directory for local dependency patches
DEFAULT CONFIGURATION
The generated cell.toml contains:
module = "my-game"
engine = "mist/prosperon@v0.9.3"
entrypoint = "main.js"
dependencies = {}
aliases = {}
replace = {}
patches = {}
[mods]
enabled = []
EXAMPLES
Initialize a new Cell project:
cell init
This will create the .cell/ directory if it doesn't exist and
populate it with the default structure.
NOTES
- The init command is idempotent - running it multiple times is safe
- Existing files are never overwritten
- Edit .cell/cell.toml after initialization to configure your project
SEE ALSO
cell(1), cell-get(1)

View File

@@ -1,77 +0,0 @@
CELL-LIST(1) Cell Manual CELL-LIST(1)
NAME
cell list - List installed modules and their status
SYNOPSIS
cell list
DESCRIPTION
The list command displays all modules currently installed in your
project, showing their aliases, locators, and status. It provides
an overview of which modules are downloaded, vendored, compiled,
or patched.
The command reads the cell.toml file and checks the filesystem to
determine the current state of each module.
OUTPUT FORMAT
For each module, the command displays:
- The alias used for importing
- The full module locator with version
- Download status in .cell/modules/
- Vendoring status in modules/
- Compilation status in .cell/build/
- Applied patches
STATUS INDICATORS
✓ Downloaded Module exists in .cell/modules/
✗ Not downloaded Module needs to be fetched with 'cell get'
✓ Vendored Module copied to modules/ directory
✓ Compiled Module bytecode exists in .cell/build/
✓ Patch exists Patch file found for this module
EXAMPLES
List all modules:
cell list
Example output:
Installed modules:
chess -> git.world/jj/chess@v1.0.0
✓ Downloaded to .cell/modules/chess@v1.0.0
✓ Vendored to modules/chess@v1.0.0
✓ Compiled to .cell/build/chess@v1.0.0
engine -> git.world/jj/engine@v2.1.0
✓ Downloaded to .cell/modules/engine@v2.1.0
✗ Not vendored
✗ Not compiled
Patches:
chess -> ./patches/chess.patch
✓ Patch file exists
NOTES
- The command only reads configuration, it doesn't modify anything
- Invalid locators are marked but don't stop the listing
- An empty dependencies section shows "No modules installed"
FILES
.cell/cell.toml
Read to get the list of dependencies
.cell/modules/
Checked for downloaded modules
modules/
Checked for vendored modules
.cell/build/
Checked for compiled modules
.cell/patches/
Checked for patch files
SEE ALSO
cell(1), cell-get(1), cell-update(1), cell-vendor(1)

View File

@@ -1,79 +0,0 @@
CELL-PATCH(1) Cell Manual CELL-PATCH(1)
NAME
cell patch - Create a patch file for local modifications
SYNOPSIS
cell patch <alias>
DESCRIPTION
The patch command creates a patch file capturing local modifications
made to a dependency. This allows you to maintain custom changes to
upstream modules that persist across updates.
Patches are stored in .cell/patches/ and automatically applied when
modules are fetched or updated.
ARGUMENTS
alias
The dependency alias to create a patch for
OPERATION
1. Compares the current module state with the original
2. Generates a unified diff of all changes
3. Saves the patch to .cell/patches/alias.patch
4. Updates cell.toml to reference the patch
PATCH APPLICATION
Patches are automatically applied:
- After 'cell get' fetches a module
- After 'cell update' downloads a new version
- During 'cell vendor' operations
Application order matches the order in cell.toml [patches] section.
EXAMPLES
Create a patch after modifying a dependency:
# Edit files in .cell/modules/chess@v1.0.0/
cell patch chess
This creates:
.cell/patches/chess.patch
And updates cell.toml:
[patches]
chess = "./patches/chess.patch"
PATCH FORMAT
Patches use unified diff format:
--- a/main.js
+++ b/main.js
@@ -10,3 +10,4 @@
function init() {
console.log("Starting chess engine");
+ console.log("With custom modifications");
}
NOTES
- Keep patches small and focused
- Document why each patch is needed
- Test patches with new versions during updates
- Patches may fail to apply if upstream changes conflict
- TODO: Actual implementation pending
FILES
.cell/patches/
Directory containing all patch files
.cell/cell.toml
Updated with patch references
WORKFLOW
1. cell get git.world/jj/chess@v1.0.0
2. Modify files in .cell/modules/chess@v1.0.0/
3. cell patch chess
4. Commit .cell/patches/chess.patch to version control
5. Future 'cell get' commands will apply the patch
SEE ALSO
cell(1), cell-get(1), cell-update(1)

View File

@@ -1,54 +0,0 @@
CELL-UPDATE(1) Cell Manual CELL-UPDATE(1)
NAME
cell update - Update a dependency to a new version
SYNOPSIS
cell update <alias> <version>
DESCRIPTION
The update command changes the version of an existing dependency
in your project. It updates the cell.toml file and prepares the
new version for download.
ARGUMENTS
alias
The dependency alias as defined in cell.toml
version
The new version to update to (e.g., v1.2.0, head)
OPERATION
1. Loads the current cell.toml configuration
2. Verifies the dependency exists
3. Updates the version in the dependencies section
4. Saves the updated configuration
5. Creates the new module directory
6. Downloads the new version (TODO: implementation pending)
EXAMPLES
Update to a specific version:
cell update chess v1.2.0
Update to latest:
cell update chess head
The dependency entry will be updated from:
chess = "git.world/jj/chess@v1.0.0"
To:
chess = "git.world/jj/chess@v1.2.0"
NOTES
- The old version remains in .cell/modules/ until manually removed
- Run 'cell build' after updating to recompile modules
- The update preserves the original module path, only changing version
FILES
.cell/cell.toml
Updated with the new version
.cell/modules/
New version directory created
SEE ALSO
cell(1), cell-get(1), cell-build(1)

View File

@@ -1,61 +0,0 @@
CELL-VENDOR(1) Cell Manual CELL-VENDOR(1)
NAME
cell vendor - Copy all dependencies locally for hermetic builds
SYNOPSIS
cell vendor
DESCRIPTION
The vendor command copies all dependencies from .cell/modules/ into
a top-level modules/ directory in your project. This creates a
hermetic build environment where all dependencies are committed
alongside your code.
Vendoring is useful for:
- Ensuring builds work without network access
- Committing exact dependency versions to version control
- Protecting against upstream repositories disappearing
- Creating fully reproducible builds
OPERATION
1. Reads all dependencies from cell.toml
2. Creates modules/ directory if it doesn't exist
3. Copies each dependency from .cell/modules/ to modules/
4. Preserves directory structure and all files
5. Optionally updates import paths (TODO: implementation pending)
DIRECTORY STRUCTURE
Before vendoring:
.cell/modules/
├── chess@v1.0.0/
└── engine@v2.1.0/
After vendoring:
modules/
├── chess@v1.0.0/
└── engine@v2.1.0/
EXAMPLES
Vendor all dependencies:
cell vendor
After vendoring, commit the modules directory:
git add modules/
git commit -m "Vendor dependencies"
NOTES
- Vendored modules take precedence over .cell/modules/
- The modules/ directory can be committed to version control
- Run vendor after adding or updating dependencies
- Removes the need for 'cell get' on fresh checkouts
FILES
modules/
Created and populated with all dependencies
.cell/cell.toml
Read to determine which modules to vendor
SEE ALSO
cell(1), cell-get(1), cell-build(1)

View File

@@ -299,6 +299,37 @@ JSValue nota2value(JSContext *js, void *nota) {
return ret;
}
static JSValue js_nota_tostring(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
size_t len;
void *nota = js_get_blob_data(ctx, &len, this_val);
if (nota == (void*)-1) return JS_EXCEPTION;
if (!nota) return JS_NULL;
JSValue decoded;
JSValue holder = JS_NewObject(ctx);
js_do_nota_decode(ctx, &decoded, (char*)nota, holder, JS_NewString(ctx, ""), JS_NULL);
JS_FreeValue(ctx, holder);
JSValue global = JS_GetGlobalObject(ctx);
JSValue json = JS_GetPropertyStr(ctx, global, "JSON");
JSValue stringify = JS_GetPropertyStr(ctx, json, "stringify");
JSValue args[3];
args[0] = decoded;
args[1] = JS_NULL;
args[2] = JS_NewInt32(ctx, 1);
JSValue result = JS_Call(ctx, stringify, json, 3, args);
JS_FreeValue(ctx, stringify);
JS_FreeValue(ctx, json);
JS_FreeValue(ctx, global);
JS_FreeValue(ctx, decoded);
JS_FreeValue(ctx, args[2]);
return result;
}
static JSValue js_nota_encode(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
if (argc < 1) return JS_ThrowTypeError(ctx, "nota.encode requires at least 1 argument");

View File

@@ -1,23 +0,0 @@
var nota = this
var json = use('json')
var encode = nota.encode
function nota_tostring()
{
return json.encode(nota.decode(this))
}
var nota_obj = {
toString: nota_tostring
}
nota.encode = function(obj, replacer)
{
var result = encode(obj,replacer)
result.toString = nota_tostring
return result
}
return nota

View File

@@ -609,6 +609,10 @@ function resolve_mod_fn(path, pkg)
function resolve_locator(path, ext, ctx)
{
// In static_only mode, only look in the embedded pack
var static_only = cell.static_only
if (!static_only) {
var local_path
if (ctx)
local_path = `.cell/modules/${ctx}/${path}${ext}`
@@ -627,6 +631,21 @@ function resolve_locator(path, ext, ctx)
var fn = resolve_mod_fn(mod_path, canonical_pkg)
return {path: mod_path, scope: SCOPE_PACKAGE, symbol:fn}
}
}
// Check embedded pack (core_qop)
// For packages, try the full package path first
if (ctx) {
var pkg_qop_path = ctx + '/' + path + ext
var pkg_core = core_qop.read(pkg_qop_path)
if (pkg_core != null) {
var form = script_forms[ext]
if (!form) throw new Error(`No script form for extension ${ext}`)
var script = form(null, text(pkg_core), ctx)
var fn = js.compile(pkg_qop_path, script)
return {path: pkg_qop_path, scope: SCOPE_CORE, symbol:js.eval_compile(fn)};
}
}
var core = core_qop.read(path + ext)
if (core != null) {
@@ -647,6 +666,7 @@ 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
@@ -681,6 +701,8 @@ function resolve_c_symbol(path, package_context)
};
}
// 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 + '/cellmod' + dylib_ext
@@ -702,14 +724,13 @@ function resolve_c_symbol(path, package_context)
}
}
}
}
// If 'path' has a package alias (e.g. 'prosperon/sprite'), try to resolve it
var pkg_alias = get_import_package(path)
if (pkg_alias) {
var canon_pkg = get_normalized_package(path, package_context)
if (canon_pkg) {
var pkg_build_dir = get_build_dir(canon_pkg)
var dl_path = pkg_build_dir + '/cellmod' + dylib_ext
var mod_name = get_import_name(path)
var mod_sym = mod_name.replace(/\//g, '_').replace(/-/g, '_').replace(/\./g, '_')
var sym_names = symbol_candidates(canon_pkg, mod_sym)
@@ -726,7 +747,10 @@ function resolve_c_symbol(path, package_context)
};
}
// Then try dynamic library for package
// Then try dynamic library for package (skip in static_only mode)
if (!static_only) {
var pkg_build_dir = get_build_dir(canon_pkg)
var dl_path = pkg_build_dir + '/cellmod' + dylib_ext
if (fd.is_file(dl_path)) {
if (!open_dls[dl_path]) open_dls[dl_path] = os.dylib_open(dl_path)
if (open_dls[dl_path]) {
@@ -744,6 +768,7 @@ function resolve_c_symbol(path, package_context)
}
}
}
}
var core_sym = `js_${path.replace(/\//g, '_')}_use`;
if (os.internal_exists(core_sym))

View File

@@ -1,24 +0,0 @@
// wota
var wota = this
var json = use('json')
var encode = wota.encode
function wota_tostring()
{
return json.encode(wota.decode(this))
}
var wota_obj = {
toString: wota_tostring
}
wota.encode = function(obj, replacer)
{
var result = encode(obj, replacer)
result.toString = wota_tostring
return result
}
return wota