packing
This commit is contained in:
@@ -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
140
Makefile
@@ -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
|
||||
|
||||
@@ -70,6 +70,7 @@ scripts = [
|
||||
'enet.c',
|
||||
'wildstar.c',
|
||||
'miniz.c',
|
||||
'json.c'
|
||||
]
|
||||
|
||||
foreach file: scripts
|
||||
|
||||
245
scripts/base.cm
245
scripts/base.cm
@@ -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;
|
||||
},
|
||||
});
|
||||
|
||||
447
scripts/build.ce
447
scripts/build.ce
@@ -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 = '.'
|
||||
log.console("Building from cell source directory")
|
||||
} else {
|
||||
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")
|
||||
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)
|
||||
}
|
||||
var home_cell = os.getenv('HOME') + '/work/cell'
|
||||
if (fd.is_file(home_cell + '/source/cell.c')) {
|
||||
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,153 +196,273 @@ 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++) {
|
||||
var pkg = packages[p]
|
||||
var parsed = shop.parse_package(pkg)
|
||||
var pkg_dir = '.cell/modules/' + parsed.path
|
||||
|
||||
if (!fd.is_dir(pkg_dir)) continue
|
||||
|
||||
var pkg_files = build.list_files(pkg_dir)
|
||||
var pkg_c_files = build.select_c_files(pkg_files, target)
|
||||
|
||||
var pkg_config = build.load_config(pkg_dir)
|
||||
var pkg_ldflags = build.get_flags(pkg_config, platform, 'LDFLAGS')
|
||||
if (pkg_ldflags) {
|
||||
if (ldflags != '') ldflags += ' '
|
||||
ldflags += pkg_ldflags
|
||||
}
|
||||
|
||||
if (pkg_c_files.length > 0) {
|
||||
log.console("Compiling " + text(pkg_c_files.length) + " C files from " + parsed.path)
|
||||
// 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
|
||||
|
||||
var pkg_cflags = build.get_flags(pkg_config, platform, 'CFLAGS')
|
||||
if (!fd.is_dir(pkg_dir)) continue
|
||||
|
||||
// Create symbol prefix for package
|
||||
var use_prefix = 'js_' + parsed.path.replace(/\//g, '_').replace(/\./g, '_').replace(/-/g, '_') + '_'
|
||||
var pkg_files = build.list_files(pkg_dir)
|
||||
var pkg_c_files = build.select_c_files(pkg_files, target)
|
||||
|
||||
for (var f = 0; f < pkg_c_files.length; f++) {
|
||||
var src = pkg_dir + '/' + pkg_c_files[f]
|
||||
var obj = build_dir + '/packages/' + parsed.path + '/' + pkg_c_files[f] + '.o'
|
||||
var pkg_config = build.load_config(pkg_dir)
|
||||
var pkg_ldflags = build.get_flags(pkg_config, platform, 'LDFLAGS')
|
||||
if (pkg_ldflags) {
|
||||
if (ldflags != '') ldflags += ' '
|
||||
ldflags += pkg_ldflags
|
||||
}
|
||||
|
||||
if (pkg_c_files.length > 0) {
|
||||
log.console("Compiling " + text(pkg_c_files.length) + " C files from " + parsed.path)
|
||||
|
||||
var safe_name = pkg_c_files[f].substring(0, pkg_c_files[f].lastIndexOf('.')).replace(/\//g, '_').replace(/-/g, '_')
|
||||
var use_name = use_prefix + safe_name + '_use'
|
||||
var pkg_cflags = build.get_flags(pkg_config, platform, 'CFLAGS')
|
||||
|
||||
var pkg_options = {
|
||||
target: target,
|
||||
cflags: pkg_cflags,
|
||||
includes: [cell_dir + '/source', pkg_dir],
|
||||
defines: { CELL_USE_NAME: use_name },
|
||||
module_dir: pkg_dir
|
||||
}
|
||||
// Create symbol prefix for package
|
||||
var use_prefix = 'js_' + parsed.path.replace(/\//g, '_').replace(/\./g, '_').replace(/-/g, '_') + '_'
|
||||
|
||||
var needs_compile = true
|
||||
if (fd.is_file(obj)) {
|
||||
var src_stat = fd.stat(src)
|
||||
var obj_stat = fd.stat(obj)
|
||||
if (src_stat && obj_stat && src_stat.mtime <= obj_stat.mtime) {
|
||||
needs_compile = false
|
||||
for (var f = 0; f < pkg_c_files.length; f++) {
|
||||
var src = pkg_dir + '/' + pkg_c_files[f]
|
||||
var obj = build_dir + '/packages/' + parsed.path + '/' + pkg_c_files[f] + '.o'
|
||||
|
||||
var safe_name = pkg_c_files[f].substring(0, pkg_c_files[f].lastIndexOf('.')).replace(/\//g, '_').replace(/-/g, '_')
|
||||
var use_name = use_prefix + safe_name + '_use'
|
||||
|
||||
var pkg_options = {
|
||||
target: target,
|
||||
cflags: pkg_cflags,
|
||||
includes: [pkg_dir],
|
||||
defines: { CELL_USE_NAME: use_name },
|
||||
module_dir: pkg_dir
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_compile) {
|
||||
var result = build.compile_file(src, obj, pkg_options)
|
||||
if (!result) {
|
||||
log.error("Build failed")
|
||||
$_.stop()
|
||||
|
||||
var needs_compile = true
|
||||
if (fd.is_file(obj)) {
|
||||
var src_stat = fd.stat(src)
|
||||
var obj_stat = fd.stat(obj)
|
||||
if (src_stat && obj_stat && src_stat.mtime <= obj_stat.mtime) {
|
||||
needs_compile = false
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_compile) {
|
||||
var result = build.compile_file(src, obj, pkg_options)
|
||||
if (!result) {
|
||||
log.error("Build failed")
|
||||
$_.stop()
|
||||
}
|
||||
}
|
||||
objects.push(obj)
|
||||
}
|
||||
objects.push(obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if (ldflags != '') ldflags += ' '
|
||||
ldflags += local_ldflags
|
||||
}
|
||||
// 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) {
|
||||
if (ldflags != '') ldflags += ' '
|
||||
ldflags += local_ldflags
|
||||
}
|
||||
|
||||
var local_files = build.list_files('.')
|
||||
var local_c_files = build.select_c_files(local_files, target)
|
||||
var local_files = build.list_files('.')
|
||||
var local_c_files = build.select_c_files(local_files, target)
|
||||
|
||||
if (local_c_files.length > 0) {
|
||||
log.console("Compiling " + text(local_c_files.length) + " local C files")
|
||||
|
||||
var local_cflags = build.get_flags(local_config, platform, 'CFLAGS')
|
||||
|
||||
for (var f = 0; f < local_c_files.length; f++) {
|
||||
var src = local_c_files[f]
|
||||
var obj = build_dir + '/local/' + local_c_files[f] + '.o'
|
||||
if (local_c_files.length > 0) {
|
||||
log.console("Compiling " + text(local_c_files.length) + " local C files")
|
||||
|
||||
var safe_name = local_c_files[f].substring(0, local_c_files[f].lastIndexOf('.')).replace(/\//g, '_').replace(/-/g, '_')
|
||||
var use_name = 'js_local_' + safe_name + '_use'
|
||||
var local_cflags = build.get_flags(local_config, platform, 'CFLAGS')
|
||||
|
||||
var local_options = {
|
||||
target: target,
|
||||
cflags: local_cflags,
|
||||
includes: [cell_dir + '/source', '.'],
|
||||
defines: { CELL_USE_NAME: use_name }
|
||||
}
|
||||
|
||||
var needs_compile = true
|
||||
if (fd.is_file(obj)) {
|
||||
var src_stat = fd.stat(src)
|
||||
var obj_stat = fd.stat(obj)
|
||||
if (src_stat && obj_stat && src_stat.mtime <= obj_stat.mtime) {
|
||||
needs_compile = false
|
||||
for (var f = 0; f < local_c_files.length; f++) {
|
||||
var src = local_c_files[f]
|
||||
var obj = build_dir + '/local/' + local_c_files[f] + '.o'
|
||||
|
||||
var safe_name = local_c_files[f].substring(0, local_c_files[f].lastIndexOf('.')).replace(/\//g, '_').replace(/-/g, '_')
|
||||
var use_name = 'js_local_' + safe_name + '_use'
|
||||
|
||||
var local_options = {
|
||||
target: target,
|
||||
cflags: local_cflags,
|
||||
includes: ['.'],
|
||||
defines: { CELL_USE_NAME: use_name }
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_compile) {
|
||||
var result = build.compile_file(src, obj, local_options)
|
||||
if (!result) {
|
||||
log.error("Build failed")
|
||||
$_.stop()
|
||||
|
||||
var needs_compile = true
|
||||
if (fd.is_file(obj)) {
|
||||
var src_stat = fd.stat(src)
|
||||
var obj_stat = fd.stat(obj)
|
||||
if (src_stat && obj_stat && src_stat.mtime <= obj_stat.mtime) {
|
||||
needs_compile = false
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_compile) {
|
||||
var result = build.compile_file(src, obj, local_options)
|
||||
if (!result) {
|
||||
log.error("Build failed")
|
||||
$_.stop()
|
||||
}
|
||||
}
|
||||
objects.push(obj)
|
||||
}
|
||||
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_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: []
|
||||
var link_options = {
|
||||
target: target,
|
||||
ldflags: ldflags,
|
||||
libs: []
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
// Add platform-specific libraries
|
||||
if (!target || platform == 'macOS' || platform == 'darwin') {
|
||||
// macOS needs no extra libs for static build
|
||||
} 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) {
|
||||
log.error("Linking failed")
|
||||
$_.stop()
|
||||
}
|
||||
|
||||
// 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()
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
63
scripts/json.c
Normal 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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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.
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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
|
||||
125
scripts/shop.cm
125
scripts/shop.cm
@@ -609,25 +609,44 @@ function resolve_mod_fn(path, pkg)
|
||||
|
||||
function resolve_locator(path, ext, ctx)
|
||||
{
|
||||
var local_path
|
||||
if (ctx)
|
||||
local_path = `.cell/modules/${ctx}/${path}${ext}`
|
||||
else
|
||||
local_path = path + ext
|
||||
|
||||
if (fd.is_file(local_path)) {
|
||||
var fn = resolve_mod_fn(local_path, ctx)
|
||||
return {path: local_path, scope: SCOPE_LOCAL, symbol:fn}
|
||||
// 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}`
|
||||
else
|
||||
local_path = path + ext
|
||||
|
||||
if (fd.is_file(local_path)) {
|
||||
var fn = resolve_mod_fn(local_path, ctx)
|
||||
return {path: local_path, scope: SCOPE_LOCAL, symbol:fn}
|
||||
}
|
||||
|
||||
var canonical_pkg = get_normalized_package(path, ctx)
|
||||
var pkg_path = get_path_in_package(path, ctx)
|
||||
var mod_path = `.cell/modules/${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}
|
||||
}
|
||||
}
|
||||
|
||||
var canonical_pkg = get_normalized_package(path, ctx)
|
||||
var pkg_path = get_path_in_package(path, ctx)
|
||||
var mod_path = `.cell/modules/${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 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) {
|
||||
var form = script_forms[ext]
|
||||
@@ -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,24 +701,27 @@ function resolve_c_symbol(path, package_context)
|
||||
};
|
||||
}
|
||||
|
||||
// Then try dynamic library
|
||||
var build_dir = get_build_dir(package_context)
|
||||
var local_dl_name = build_dir + '/cellmod' + 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);
|
||||
// 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
|
||||
|
||||
if (open_dls[local_dl_name]) {
|
||||
var locals = package_context ? symbol_candidates(local_path, local_sym_base) : [local]
|
||||
for (var i = 0; i < locals.length; i++) {
|
||||
var candidate = locals[i]
|
||||
if (os.dylib_has_symbol(open_dls[local_dl_name], candidate))
|
||||
return {
|
||||
symbol: function() { return os.dylib_symbol(open_dls[local_dl_name], candidate); },
|
||||
scope: SCOPE_LOCAL,
|
||||
path: candidate
|
||||
};
|
||||
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]
|
||||
for (var i = 0; i < locals.length; i++) {
|
||||
var candidate = locals[i]
|
||||
if (os.dylib_has_symbol(open_dls[local_dl_name], candidate))
|
||||
return {
|
||||
symbol: function() { return os.dylib_symbol(open_dls[local_dl_name], candidate); },
|
||||
scope: SCOPE_LOCAL,
|
||||
path: candidate
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -708,8 +731,6 @@ function resolve_c_symbol(path, package_context)
|
||||
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,21 +747,25 @@ function resolve_c_symbol(path, package_context)
|
||||
};
|
||||
}
|
||||
|
||||
// Then try dynamic library for package
|
||||
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]) {
|
||||
for (var si = 0; si < sym_names.length; si++) {
|
||||
var sym_name = sym_names[si]
|
||||
if (os.dylib_has_symbol(open_dls[dl_path], sym_name))
|
||||
return {
|
||||
symbol: function() { return os.dylib_symbol(open_dls[dl_path], sym_name) },
|
||||
scope: SCOPE_PACKAGE,
|
||||
package: canon_pkg,
|
||||
path: sym_name
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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]) {
|
||||
for (var si = 0; si < sym_names.length; si++) {
|
||||
var sym_name = sym_names[si]
|
||||
if (os.dylib_has_symbol(open_dls[dl_path], sym_name))
|
||||
return {
|
||||
symbol: function() { return os.dylib_symbol(open_dls[dl_path], sym_name) },
|
||||
scope: SCOPE_PACKAGE,
|
||||
package: canon_pkg,
|
||||
path: sym_name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user