This commit is contained in:
2025-12-09 23:19:25 -06:00
parent 657e342159
commit 37f2cff6ec
20 changed files with 1716 additions and 3139 deletions

View File

@@ -7,9 +7,17 @@
# The cell shop is at ~/.cell and core scripts are installed to ~/.cell/core # The cell shop is at ~/.cell and core scripts are installed to ~/.cell/core
CELL_SHOP = $(HOME)/.cell CELL_SHOP = $(HOME)/.cell
CELL_CORE = $(CELL_SHOP)/core
CELL_CORE_PACKAGE = $(CELL_SHOP)/packages/core CELL_CORE_PACKAGE = $(CELL_SHOP)/packages/core
# Install core: symlink this directory to ~/.cell/core
install: bootstrap $(CELL_SHOP)
@echo "Linking cell core to $(CELL_CORE_PACKAGE)"
rm -rf $(CELL_CORE_PACKAGE)
ln -s $(PWD) $(CELL_CORE_PACKAGE)
cp cell /opt/homebrew/bin/
cp libcell_runtime.dylib /opt/homebrew/lib/
@echo "Core installed."
cell: libcell_runtime.dylib cell_main cell: libcell_runtime.dylib cell_main
cp cell_main cell cp cell_main cell
chmod +x cell chmod +x cell
@@ -26,17 +34,6 @@ libcell_runtime.dylib: $(CELL_SHOP)/build/dynamic
cell_main: source/main.c libcell_runtime.dylib 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 cc -o cell_main source/main.c -L. -lcell_runtime -Wl,-rpath,@loader_path -Wl,-rpath,/opt/homebrew/lib
# Install core: symlink this directory to ~/.cell/core
install: bootstrap $(CELL_SHOP)
@echo "Linking cell core to $(CELL_CORE)"
rm -rf $(CELL_CORE)
rm -rf $(CELL_CORE_PACKAGE)
ln -s $(PWD) $(CELL_CORE)
ln -s $(PWD) $(CELL_CORE_PACKAGE)
cp cell /opt/homebrew/bin/
cp libcell_runtime.dylib /opt/homebrew/lib/
@echo "Core installed."
# Create the cell shop directories # Create the cell shop directories
$(CELL_SHOP): $(CELL_SHOP):
mkdir -p $(CELL_SHOP) mkdir -p $(CELL_SHOP)

View File

@@ -217,6 +217,7 @@ static const JSCFunctionListEntry js_writer_funcs[] = {
JSValue js_reader_mod(JSContext *js, JSValue self, int argc, JSValue *argv) JSValue js_reader_mod(JSContext *js, JSValue self, int argc, JSValue *argv)
{ {
#ifndef MINIZ_NO_TIME
const char *file = JS_ToCString(js,argv[0]); const char *file = JS_ToCString(js,argv[0]);
if (!file) if (!file)
return JS_EXCEPTION; return JS_EXCEPTION;
@@ -243,6 +244,9 @@ JSValue js_reader_mod(JSContext *js, JSValue self, int argc, JSValue *argv)
} }
return JS_NewFloat64(js, pstat.m_time); return JS_NewFloat64(js, pstat.m_time);
#else
return JS_ThrowInternalError(js, "MINIZ_NO_TIME is defined");
#endif
} }
JSValue js_reader_exists(JSContext *js, JSValue self, int argc, JSValue *argv) JSValue js_reader_exists(JSContext *js, JSValue self, int argc, JSValue *argv)

View File

@@ -6,7 +6,7 @@ var time = use('time')
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Test configurations // Test configurations
const iterations = { def iterations = {
simple: 10000000, simple: 10000000,
medium: 1000000, medium: 1000000,
complex: 100000 complex: 100000
@@ -154,7 +154,7 @@ function benchObjectCreation() {
this.y = y; this.y = y;
} }
var constructorTime = measureTime(function() { var defructorTime = measureTime(function() {
for (var i = 0; i < iterations.medium; i++) { for (var i = 0; i < iterations.medium; i++) {
var p = new Point(i, i * 2); var p = new Point(i, i * 2);
} }
@@ -179,7 +179,7 @@ function benchObjectCreation() {
return { return {
literalTime: literalTime, literalTime: literalTime,
constructorTime: constructorTime, defructorTime: defructorTime,
prototypeTime: prototypeTime prototypeTime: prototypeTime
}; };
} }
@@ -342,9 +342,9 @@ var objResults = benchObjectCreation();
log.console(" Literal: " + objResults.literalTime.toFixed(3) + "s => " + log.console(" Literal: " + objResults.literalTime.toFixed(3) + "s => " +
(iterations.medium / objResults.literalTime).toFixed(1) + " creates/sec [" + (iterations.medium / objResults.literalTime).toFixed(1) + " creates/sec [" +
(objResults.literalTime / iterations.medium * 1e9).toFixed(1) + " ns/op]"); (objResults.literalTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
log.console(" Constructor: " + objResults.constructorTime.toFixed(3) + "s => " + log.console(" Constructor: " + objResults.defructorTime.toFixed(3) + "s => " +
(iterations.medium / objResults.constructorTime).toFixed(1) + " creates/sec [" + (iterations.medium / objResults.defructorTime).toFixed(1) + " creates/sec [" +
(objResults.constructorTime / iterations.medium * 1e9).toFixed(1) + " ns/op]"); (objResults.defructorTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
log.console(" Prototype: " + objResults.prototypeTime.toFixed(3) + "s => " + log.console(" Prototype: " + objResults.prototypeTime.toFixed(3) + "s => " +
(iterations.medium / objResults.prototypeTime).toFixed(1) + " creates/sec [" + (iterations.medium / objResults.prototypeTime).toFixed(1) + " creates/sec [" +
(objResults.prototypeTime / iterations.medium * 1e9).toFixed(1) + " ns/op]"); (objResults.prototypeTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");

877
build.ce
View File

@@ -1,845 +1,112 @@
// cell build [options] [actor] - Build cell binary // cell build [options] - Build dynamic libraries locally for the current machine
//
// Modes:
// cell build -d Build dynamic libraries for all packages
// cell build -d --target <tar> Build dynamic libraries for a specific target
// cell build -o <name> Build static binary for current project
// cell build --target <tar> -o <n> Build static binary for target platform
// //
// The actor argument specifies the entry point (e.g., "accio" for accio.ce) // Usage:
// If no actor is specified for static builds, builds cell itself. // cell build Build dynamic libraries for all packages
// cell build -p <pkg> Build dynamic library for specific package
// cell build -t <target> Cross-compile dynamic libraries for target platform
var build = use('build') var build = use('build')
var shop = use('shop') var shop = use('shop')
var pkg_tools = use('package')
var fd = use('fd') var fd = use('fd')
var os = use('os')
var qop = use('qop')
var utf8 = use('utf8')
var json = use('json')
var targets = [
"macos_arm64",
"macos_x86_64",
"linux",
"linux_arm64",
"windows",
"windows_i686",
"playdate",
"emscripten"
]
// Parse arguments
var target = null var target = null
var dynamic_mode = false
var static_only = false // -s flag: don't look outside pack for scripts
var actor = null
var output_name = null
var target_package = null var target_package = null
var buildtype = 'release'
for (var i = 0; i < args.length; i++) { for (var i = 0; i < args.length; i++) {
if (args[i] == '--target' || args[i] == '-t') { if (args[i] == '-t' || args[i] == '--target') {
if (i + 1 < args.length) { if (i + 1 < args.length) {
target = args[i + 1] target = args[++i]
i++
} else { } else {
log.error("--target requires an argument") log.error('-t requires a target')
log.console("Available targets: " + targets.join(', '))
$_.stop() $_.stop()
} }
} else if (args[i] == '-p' || args[i] == '--package') { } else if (args[i] == '-p' || args[i] == '--package') {
if (i + 1 < args.length) { if (i + 1 < args.length) {
target_package = args[i + 1] target_package = args[++i]
dynamic_mode = true
i++
} else { } else {
log.error("-p requires a package name") log.error('-p requires a package name')
$_.stop() $_.stop()
} }
} else if (args[i] == '-d' || args[i] == '--dynamic') { } else if (args[i] == '-b' || args[i] == '--buildtype') {
dynamic_mode = true
} else if (args[i] == '-s' || args[i] == '--static-only') {
static_only = true
} else if (args[i] == '-o' || args[i] == '--output') {
if (i + 1 < args.length) { if (i + 1 < args.length) {
output_name = args[i + 1] buildtype = args[++i]
i++ if (buildtype != 'release' && buildtype != 'debug' && buildtype != 'minsize') {
log.error('Invalid buildtype: ' + buildtype + '. Must be release, debug, or minsize')
$_.stop()
}
} else { } else {
log.error("-o requires an output name") log.error('-b requires a buildtype (release, debug, minsize)')
$_.stop() $_.stop()
} }
} else if (args[i] == '--help' || args[i] == '-h') { log.console('Usage: cell build [options]')
log.console("Usage: cell build [options] [actor]") log.console('')
log.console("Build cell binaries or dynamic libraries.") log.console('Options:')
log.console("") log.console(' -p, --package <pkg> Build specific package only')
log.console("Options:") log.console(' -t, --target <target> Cross-compile for target platform')
log.console(" -d, --dynamic Build dynamic libraries for all packages") log.console(' -b, --buildtype <type> Build type: release, debug, minsize (default: release)')
log.console(" -p, --package <pkg> Build dynamic library for specific package") log.console('')
log.console(" -o, --output <name> Output name for static binary") log.console('Available targets: ' + build.list_targets().join(', '))
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("Examples:")
log.console(" cell build -d Build dynamic libs for host")
log.console(" cell build -d --target windows Build dynamic libs for Windows")
log.console(" cell build -o myapp Build static binary 'myapp'")
log.console(" cell build --target windows -o myapp Cross-compile static binary")
log.console("")
log.console("Available targets: " + targets.join(', '))
$_.stop() $_.stop()
} else if (args[i] == '--list-targets') { } else if (args[i] == '--list-targets') {
log.console("Available targets:") log.console('Available targets:')
var targets = build.list_targets()
for (var t = 0; t < targets.length; t++) { for (var t = 0; t < targets.length; t++) {
log.console(" " + targets[t]) log.console(' ' + targets[t])
} }
$_.stop() $_.stop()
} else if (!args[i].startsWith('-')) {
actor = args[i]
} }
} }
// Resolve target - if not specified for dynamic mode, detect host // Detect target if not specified
if (!target) { if (!target) {
var host_platform = os.platform() target = build.detect_host_target()
var host_arch = os.arch ? os.arch() : 'arm64' // Default to arm64 if not available if (target) log.console('Target: ' + target)
if (host_platform == 'macOS' || host_platform == 'darwin') {
target = host_arch == 'x86_64' ? 'macos_x86_64' : 'macos_arm64'
} else if (host_platform == 'Linux') {
target = host_arch == 'x86_64' ? 'linux' : 'linux_arm64'
} else if (host_platform == 'Windows') {
target = 'windows'
}
if (target) log.console("Detected host target: " + target)
} }
if (target && !build.has_target(target)) { if (target && !build.has_target(target)) {
log.console("Available targets: " + targets.join(', ')) log.error('Invalid target: ' + target)
throw new Error("Invalid target: " + target) log.console('Available targets: ' + build.list_targets().join(', '))
}
var target_system = build.get_target_system(target)
var platform = target_system || os.platform()
var dylib_ext = build.get_dylib_ext(target)
// Get build directory for a target (with subdirectory for cross-compilation)
function get_target_build_dir() {
var base = shop.get_shop_path() + '/build'
if (target) return base + '/' + target
return base + '/host'
}
// Filter out main*.c files (only for static builds)
function filter_main_files(files) {
return files.filter(function(f) {
var basename = f
var slash = f.lastIndexOf('/')
if (slash >= 0) basename = f.substring(slash + 1)
// Exclude main.c and main_*.c files
if (basename == 'main.c') return false
if (basename.startsWith('main_') && basename.endsWith('.c')) return false
return true
})
}
// Get the appropriate main file for a target (for static builds)
function get_main_file(cell_dir, target_name) {
// Check for target-specific main first
if (target_name) {
var target_main = cell_dir + '/source/main_' + target_name + '.c'
if (fd.is_file(target_main)) return target_main
}
// Fall back to generic main.c
var generic_main = cell_dir + '/source/main.c'
if (fd.is_file(generic_main)) return generic_main
return null
}
// Resolve relative paths in LDFLAGS to absolute paths relative to a base directory
// This handles -L and -Wl,-rpath flags with relative paths
function resolve_ldflags(flags, base_dir) {
if (!flags || !base_dir) return flags
var parts = flags.split(' ')
var result = []
for (var i = 0; i < parts.length; i++) {
var part = parts[i]
if (!part) continue
// Handle -L<path>
if (part.startsWith('-L') && part.length > 2) {
var path = part.substring(2)
if (!path.startsWith('/') && !path.startsWith('$')) {
part = '-L' + base_dir + '/' + path
}
}
// Handle -L <path> (space separated)
else if (part == '-L' && i + 1 < parts.length) {
result.push(part)
i++
var path = parts[i]
if (!path.startsWith('/') && !path.startsWith('$')) {
path = base_dir + '/' + path
}
result.push(path)
continue
}
result.push(part)
}
return result.join(' ')
}
// Find cell package (core)
function find_cell_dir() {
// Check Shop Core Dir first (this is where cell is linked/installed)
var core_dir = shop.get_core_dir()
if (fd.is_file(core_dir + '/source/cell.c')) {
return core_dir
}
// Check if we're in the cell directory itself
if (fd.is_file('source/cell.c') && fd.is_file('source/quickjs.c')) {
return fd.realpath('.')
}
// Check for cell as a dependency
var config = shop.load_config()
if (config && config.dependencies && config.dependencies.cell) {
var pkg = config.dependencies.cell
var parsed = shop.parse_package(pkg)
var pkg_dir = shop.get_shop_path() + '/packages/' + parsed.path
if (fd.is_file(pkg_dir + '/source/cell.c')) {
return pkg_dir
}
}
// Fallback: try ~/work/cell
var home_cell = os.getenv('HOME') + '/work/cell'
if (fd.is_file(home_cell + '/source/cell.c')) {
return home_cell
}
return null
}
// Build core cellmod.dylib (must be done first for dynamic builds)
function build_core_cellmod(cell_dir, build_dir) {
log.console("Building core cellmod" + dylib_ext + "...")
var source_files = build.list_files(cell_dir + '/source')
var script_files = build.list_files(cell_dir + '/scripts')
var all_files = []
for (var i = 0; i < source_files.length; i++) {
all_files.push('source/' + source_files[i])
}
for (var i = 0; i < script_files.length; i++) {
all_files.push('scripts/' + script_files[i])
}
// Select C files for target, then filter out main*.c
var c_files = build.select_c_files(all_files, target)
c_files = filter_main_files(c_files)
log.console("Found " + text(c_files.length) + " C files for core")
var cell_config = build.load_config(cell_dir)
var cflags = '-fPIC ' + build.get_flags(cell_config, platform, 'CFLAGS', target)
var ldflags = build.get_flags(cell_config, platform, 'LDFLAGS', target)
var compile_options = {
target: target,
cflags: cflags,
includes: [cell_dir, cell_dir + '/source'],
defines: {},
module_dir: cell_dir
}
if (target == 'playdate') {
compile_options.defines.TARGET_PLAYDATE = true
}
var objects = []
for (var i = 0; i < c_files.length; i++) {
var src_rel = c_files[i]
var src_abs = cell_dir + '/' + src_rel
var obj = build_dir + '/core/' + src_rel + '.o'
var needs_compile = true
if (fd.is_file(obj)) {
var src_stat = fd.stat(src_abs)
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_abs, obj, compile_options)
if (!result) {
log.error("Failed to compile " + src_abs)
return null
}
}
objects.push(obj)
}
// Link into shared library
var lib_name = build_dir + '/core/libcellmod' + dylib_ext
build.ensure_dir(build_dir + '/core')
var link_flags = '-shared -fPIC'
if (ldflags) link_flags += ' ' + ldflags
var objs_str = objects.join(' ')
var cc = build.get_cpp(target)
var cmd = cc + ' ' + link_flags + ' ' + objs_str + ' -o ' + lib_name
log.console(cmd)
log.console("Linking " + lib_name)
var ret = os.system(cmd)
if (ret != 0) {
log.error("Failed to link core cellmod")
return null
}
log.console("Built " + lib_name)
return lib_name
}
// Build a package's cellmod.dylib
function build_package_cellmod(pkg_path, pkg_dir, build_dir, core_lib) {
var pkg_files = build.list_files(pkg_dir)
var c_files = build.select_c_files(pkg_files, target)
c_files = filter_main_files(c_files)
if (c_files.length == 0) {
return null // No C files to compile
}
log.console("Building " + pkg_path + " (" + text(c_files.length) + " C files)...")
var pkg_config = build.load_config(pkg_dir)
var cflags = '-fPIC ' + build.get_flags(pkg_config, platform, 'CFLAGS', target)
var ldflags = build.get_flags(pkg_config, platform, 'LDFLAGS', target)
var use_prefix = 'js_' + pkg_path.replace(/\//g, '_').replace(/\./g, '_').replace(/-/g, '_') + '_'
var objects = []
for (var i = 0; i < c_files.length; i++) {
var src_rel = c_files[i]
var src_abs = pkg_dir + '/' + src_rel
var obj = build_dir + '/packages/' + pkg_path + '/' + src_rel + '.o'
var safe_name = src_rel.substring(0, src_rel.lastIndexOf('.')).replace(/\//g, '_').replace(/-/g, '_')
var use_name = use_prefix + safe_name + '_use'
var compile_options = {
target: target,
cflags: cflags,
includes: [pkg_dir],
defines: { CELL_USE_NAME: use_name },
module_dir: pkg_dir
}
var needs_compile = true
if (fd.is_file(obj)) {
var src_stat = fd.stat(src_abs)
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_abs, obj, compile_options)
if (!result) {
log.error("Failed to compile " + src_abs)
return false
}
}
objects.push(obj)
}
// Link into shared library, linking against core cellmod
var lib_dir = build_dir + '/packages/' + pkg_path
build.ensure_dir(lib_dir)
var lib_name = lib_dir + '/libcellmod' + dylib_ext
var link_flags = '-shared -fPIC'
if (platform == 'macOS' || platform == 'darwin') {
// Link against core cellmod
link_flags += ' -L' + build_dir + '/core -Wl,-rpath,@loader_path/../../core'
} else if (platform == 'Linux' || platform == 'linux') {
link_flags += ' -L' + build_dir + '/core -Wl,-rpath,$ORIGIN/../../core'
} else if (platform == 'Windows' || platform == 'windows') {
link_flags += ' -L' + build_dir + '/core'
}
link_flags += ' -lcellmod'
if (ldflags) link_flags += ' ' + ldflags
var objs_str = objects.join(' ')
var cc = build.get_cpp(target)
var cmd = cc + ' ' + link_flags + ' ' + objs_str + ' -o ' + lib_name
log.console("Linking " + lib_name)
// Run from package directory so relative paths in LDFLAGS work
var ret = os.system('cd "' + pkg_dir + '" && ' + cmd)
if (ret != 0) {
log.error("Failed to link " + pkg_path)
return false
}
return lib_name
}
// ============================================================================
// MAIN BUILD LOGIC
// ============================================================================
var cell_dir = find_cell_dir()
if (!cell_dir) {
log.error("Could not find cell source. Link cell to core or add as dependency.")
$_.stop() $_.stop()
} }
log.console("Using cell from: " + cell_dir)
if (dynamic_mode) { var packages = shop.list_packages()
// ======================================================================== log.console('Preparing packages...')
// DYNAMIC MODE: Build cellmod.dylib for all packages for (var package of packages) {
// ======================================================================== if (package == 'core') continue
var build_dir = get_target_build_dir() shop.extract(package)
build.ensure_dir(build_dir)
// 1. Build core cellmod first (everything else links to it)
var core_lib = build_core_cellmod(cell_dir, build_dir)
if (!core_lib) {
log.error("Failed to build core cellmod")
$_.stop()
}
// 2. Build cellmod for each installed package
var packages = shop.list_shop_packages()
var built_count = 1 // core
for (var i = 0; i < packages.length; i++) {
var info = packages[i]
var pkg = info.package
if (!pkg || pkg == 'core') continue
if (target_package && pkg != target_package) continue
var parsed = shop.parse_package(pkg)
var pkg_dir = shop.get_shop_path() + '/packages/' + parsed.path
if (!fd.is_dir(pkg_dir)) {
log.console("Skipping " + pkg + " (not found)")
continue
}
var result = build_package_cellmod(parsed.path, pkg_dir, build_dir, core_lib)
if (result) built_count++
}
// 3. Also build local project if we're in one
var local_config = shop.load_config()
if (!target_package && local_config && cell_dir != fd.realpath('.')) {
var local_files = build.list_files('.')
var local_c_files = build.select_c_files(local_files, target)
local_c_files = filter_main_files(local_c_files)
if (local_c_files.length > 0) {
log.console("Building local project...")
var local_cflags = '-fPIC ' + build.get_flags(local_config, platform, 'CFLAGS', target)
var local_ldflags = build.get_flags(local_config, platform, 'LDFLAGS', target)
var objects = []
for (var i = 0; i < local_c_files.length; i++) {
var src = local_c_files[i]
var obj = build_dir + '/local/' + src + '.o'
var safe_name = src.substring(0, src.lastIndexOf('.')).replace(/\//g, '_').replace(/-/g, '_')
var use_name = 'js_local_' + safe_name + '_use'
var compile_options = {
target: target,
cflags: local_cflags,
includes: ['.'],
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
}
}
if (needs_compile) {
var result = build.compile_file(src, obj, compile_options)
if (!result) {
log.error("Failed to compile " + src)
$_.stop()
}
}
objects.push(obj)
}
// Link local cellmod
var local_lib = build_dir + '/local/libcellmod' + dylib_ext
build.ensure_dir(build_dir + '/local')
var link_flags = '-shared -fPIC'
if (platform == 'macOS' || platform == 'darwin') {
link_flags += ' -L' + build_dir + '/core -Wl,-rpath,@loader_path/../core'
} else {
link_flags += ' -L' + build_dir + '/core -Wl,-rpath,$ORIGIN/../core'
}
link_flags += ' -lcellmod'
if (local_ldflags) link_flags += ' ' + local_ldflags
var objs_str = objects.join(' ')
var cc = build.get_cc(target)
var cmd = cc + ' ' + link_flags + ' ' + objs_str + ' -o ' + local_lib
log.console("Linking " + local_lib)
var ret = os.system(cmd)
if (ret != 0) {
log.error("Failed to link local cellmod")
$_.stop()
}
built_count++
}
}
// Copy libraries to standard build location for runtime loading
// The cell binary expects libraries at ~/.cell/build/packages/... not ~/.cell/build/<target>/packages/...
var standard_build_dir = shop.get_shop_path() + '/build'
// Copy core
var core_src = build_dir + '/core/libcellmod' + dylib_ext
var core_dst_dir = standard_build_dir + '/core'
build.ensure_dir(core_dst_dir)
var core_dst = core_dst_dir + '/libcellmod' + dylib_ext
if (fd.is_file(core_src)) {
os.system('cp "' + core_src + '" "' + core_dst + '"')
}
// Copy package libraries
for (var i = 0; i < packages.length; i++) {
var info = packages[i]
var pkg = info.package
if (!pkg || pkg == 'core') continue
var parsed = shop.parse_package(pkg)
var pkg_src = build_dir + '/packages/' + parsed.path + '/libcellmod' + dylib_ext
if (fd.is_file(pkg_src)) {
var pkg_dst_dir = standard_build_dir + '/packages/' + parsed.path
build.ensure_dir(pkg_dst_dir)
var pkg_dst = pkg_dst_dir + '/libcellmod' + dylib_ext
os.system('cp "' + pkg_src + '" "' + pkg_dst + '"')
}
}
// Copy local library if built
var local_src = build_dir + '/local/libcellmod' + dylib_ext
if (fd.is_file(local_src)) {
var local_dst_dir = standard_build_dir + '/local'
build.ensure_dir(local_dst_dir)
var local_dst = local_dst_dir + '/libcellmod' + dylib_ext
os.system('cp "' + local_src + '" "' + local_dst + '"')
}
if (target_package && built_count == 1) {
log.error("Package '" + target_package + "' not found in shop or failed to build.")
}
log.console("")
log.console("Dynamic build complete: " + text(built_count) + " libraries built")
log.console("Build directory: " + build_dir)
log.console("Libraries copied to: " + standard_build_dir)
} else {
// ========================================================================
// STATIC MODE: Build a static executable
// ========================================================================
// Must be run from a directory with cell.toml
var local_config = shop.load_config()
if (!local_config && cell_dir != fd.realpath('.')) {
log.error("Static builds must be run from a project directory with cell.toml")
log.error("Or run from the cell source directory itself")
$_.stop()
}
var build_dir = get_target_build_dir() + '/static'
build.ensure_dir(build_dir)
// Collect all C files from cell (including main.c for static builds)
var source_files = build.list_files(cell_dir + '/source')
var script_files = build.list_files(cell_dir + '/scripts')
var all_files = []
for (var i = 0; i < source_files.length; i++) {
all_files.push('source/' + source_files[i])
}
for (var i = 0; i < script_files.length; i++) {
all_files.push('scripts/' + script_files[i])
}
var c_files = build.select_c_files(all_files, target)
// For static builds, we keep main.c (or target-specific main)
// But we need to filter out wrong main files
c_files = c_files.filter(function(f) {
var basename = f
var slash = f.lastIndexOf('/')
if (slash >= 0) basename = f.substring(slash + 1)
// Keep main.c, filter out main_*.c unless it matches our target
if (basename.startsWith('main_') && basename.endsWith('.c')) {
var main_target = basename.substring(5, basename.length - 2)
return target && target.indexOf(main_target) >= 0
}
return true
})
var cell_config = build.load_config(cell_dir)
var cflags = build.get_flags(cell_config, platform, 'CFLAGS', target)
var ldflags = build.get_flags(cell_config, platform, 'LDFLAGS', target)
var compile_options = {
target: target,
cflags: cflags,
includes: [cell_dir, cell_dir + '/source'],
defines: {},
module_dir: cell_dir
}
if (target == 'playdate') {
compile_options.defines.TARGET_PLAYDATE = true
}
log.console("Compiling " + text(c_files.length) + " cell C files...")
var objects = []
for (var i = 0; i < c_files.length; i++) {
var src_rel = c_files[i]
var src_abs = cell_dir + '/' + src_rel
var obj = build_dir + '/cell/' + src_rel + '.o'
var needs_compile = true
if (fd.is_file(obj)) {
var src_stat = fd.stat(src_abs)
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_abs, obj, compile_options)
if (!result) {
log.error("Build failed")
$_.stop()
}
}
objects.push(obj)
}
// Compile package C files
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 = shop.get_shop_path() + '/packages/' + 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)
pkg_c_files = filter_main_files(pkg_c_files)
if (pkg_c_files.length == 0) continue
log.console("Compiling " + text(pkg_c_files.length) + " C files from " + parsed.path)
var pkg_config = build.load_config(pkg_dir)
var pkg_cflags = build.get_flags(pkg_config, platform, 'CFLAGS', target)
var pkg_ldflags = build.get_flags(pkg_config, platform, 'LDFLAGS', target)
if (pkg_ldflags) {
// Resolve relative paths in LDFLAGS to absolute paths
pkg_ldflags = resolve_ldflags(pkg_ldflags, pkg_dir)
if (ldflags != '') ldflags += ' '
ldflags += pkg_ldflags
}
var use_prefix = 'js_' + parsed.path.replace(/\//g, '_').replace(/\./g, '_').replace(/-/g, '_') + '_'
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
}
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)
}
}
// Compile local C files (if not building from cell directory)
if (cell_dir != fd.realpath('.')) {
var local_files = build.list_files('.')
var local_c_files = build.select_c_files(local_files, target)
local_c_files = filter_main_files(local_c_files)
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', target)
var local_ldflags = build.get_flags(local_config, platform, 'LDFLAGS', target)
if (local_ldflags) {
// Resolve relative paths - local project is in current directory
local_ldflags = resolve_ldflags(local_ldflags, fd.realpath('.'))
if (ldflags != '') ldflags += ' '
ldflags += local_ldflags
}
for (var f = 0; f < local_c_files.length; f++) {
var src = local_c_files[f]
var obj = build_dir + '/local/' + src + '.o'
var safe_name = src.substring(0, src.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 }
}
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)
}
}
}
log.console("Compiled " + text(objects.length) + " object files")
// Link into executable
var exe_ext = build.get_exe_ext(target)
// Use 'cell_bin' to avoid conflict with 'cell' directory containing object files
var exe_name = build_dir + '/cell_bin' + exe_ext
var link_options = {
target: target,
ldflags: ldflags,
libs: []
}
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 (scripts)
var pack_path = build_dir + '/pack.qop'
var pack_files = []
// Include core scripts
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 })
}
}
// Include package scripts
for (var p = 0; p < packages.length; p++) {
var pkg = packages[p]
var parsed = shop.parse_package(pkg)
var pkg_dir = shop.get_shop_path() + '/packages/' + 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] })
}
}
// Include local scripts
if (cell_dir != fd.realpath('.')) {
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] })
}
}
var final_name = output_name || 'app' + exe_ext
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")
}
} }
$_.stop() if (target_package) {
// Build single package
log.console('Building ' + target_package + '...')
try {
var lib = build.build_dynamic(target_package, target, buildtype)
if (lib) {
log.console('Built: ' + lib)
}
} catch (e) {
log.error('Build failed: ' + e)
$_.stop()
}
} else {
// Build all packages
log.console('Building all packages...')
var results = build.build_all_dynamic(target, buildtype)
var success = 0
var failed = 0
for (var i = 0; i < results.length; i++) {
if (results[i].library) {
success++
} else if (results[i].error) {
failed++
}
}
log.console('')
log.console('Build complete: ' + success + ' libraries built' + (failed > 0 ? ', ' + failed + ' failed' : ''))
}
$_.stop()

1016
build.cm

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ CFLAGS = "-x objective-c"
LDFLAGS = "-framework CoreFoundation -framework CFNetwork" LDFLAGS = "-framework CoreFoundation -framework CFNetwork"
[compilation.playdate] [compilation.playdate]
CFLAGS = "-DMINIZ_NO_TIME -DTARGET_EXTENSION -DTARGET_PLAYDATE -I$PLAYDATE_SDK_PATH/C_API" CFLAGS = "-DMINIZ_NO_TIME -DTARGET_EXTENSION -DTARGET_PLAYDATE -I$LOCAL/PlaydateSDK/C_API"
[compilation.windows] [compilation.windows]
LDFLAGS = "-lws2_32 -lwinmm -liphlpapi -lbcrypt -lwinhttp -static-libgcc -static-libstdc++" LDFLAGS = "-lws2_32 -lwinmm -liphlpapi -lbcrypt -lwinhttp -static-libgcc -static-libstdc++"

View File

@@ -1,7 +1,7 @@
// cell config - Manage system and actor configurations // cell config - Manage system and actor configurations
var toml = use('toml') var toml = use('toml')
var shop = use('shop') var pkg = use('package')
var text = use('text') var text = use('text')
function print_help() { function print_help() {
@@ -104,7 +104,7 @@ if (args.length == 0) {
return return
} }
var config = shop.load_config() var config = pkg.load_config()
if (!config) { if (!config) {
log.error("Failed to load cell.toml") log.error("Failed to load cell.toml")
$_.stop() $_.stop()
@@ -171,7 +171,7 @@ switch (command) {
} }
set_nested(config, path, value) set_nested(config, path, value)
shop.save_config(config) pkg.save_config(config)
log.console("Set " + key + " = " + format_value(value)) log.console("Set " + key + " = " + format_value(value))
break break
@@ -230,7 +230,7 @@ switch (command) {
var value = parse_value(value_str) var value = parse_value(value_str)
set_nested(config.actors[actor_name], path, value) set_nested(config.actors[actor_name], path, value)
shop.save_config(config) pkg.save_config(config)
log.console("Set actors." + actor_name + "." + key + " = " + format_value(value)) log.console("Set actors." + actor_name + "." + key + " = " + format_value(value))
break break

4
fd.c
View File

@@ -568,13 +568,13 @@ JSC_CCALL(fd_slurpwrite,
int fd = open(str, O_WRONLY | O_CREAT | O_TRUNC, 0644); int fd = open(str, O_WRONLY | O_CREAT | O_TRUNC, 0644);
JS_FreeCString(js, str); JS_FreeCString(js, str);
if (fd < 0) if (fd < 0)
return JS_ThrowInternalError(js, "open failed: %s", strerror(errno)); return JS_ThrowInternalError(js, "open failed for %s: %s", str, strerror(errno));
ssize_t written = write(fd, data, len); ssize_t written = write(fd, data, len);
close(fd); close(fd);
if (written != (ssize_t)len) if (written != (ssize_t)len)
return JS_ThrowInternalError(js, "write failed: %s", strerror(errno)); return JS_ThrowInternalError(js, "write failed for %s: %s", str, strerror(errno));
return JS_NULL; return JS_NULL;
) )

86
fetch.ce Normal file
View File

@@ -0,0 +1,86 @@
// cell fetch - Fetch package zips from remote sources
//
// This command ensures that the zip files on disk match what's in the lock file.
// For local packages, this is a no-op.
// For remote packages, downloads the zip if not present or hash mismatch.
//
// Usage:
// cell fetch - Fetch all packages
// cell fetch <package> - Fetch a specific package
var shop = use('shop')
// Parse arguments
var target_pkg = null
for (var i = 0; i < args.length; i++) {
if (args[i] == '--help' || args[i] == '-h') {
log.console("Usage: cell fetch [package]")
log.console("Fetch package zips from remote sources.")
log.console("")
log.console("Arguments:")
log.console(" package Optional package name to fetch. If omitted, fetches all.")
log.console("")
log.console("This command ensures that the zip files on disk match what's in")
log.console("the lock file. For local packages, this is a no-op.")
$_.stop()
} else if (!args[i].startsWith('-')) {
target_pkg = args[i]
}
}
var all_packages = shop.list_packages()
var lock = shop.load_lock()
var packages_to_fetch = []
if (target_pkg) {
// Fetch specific package
if (!all_packages.includes(target_pkg)) {
log.error("Package not found: " + target_pkg)
$_.stop()
}
packages_to_fetch.push(target_pkg)
} else {
// Fetch all packages
packages_to_fetch = all_packages
}
log.console("Fetching " + text(packages_to_fetch.length) + " package(s)...")
var success_count = 0
var skip_count = 0
var fail_count = 0
for (var pkg of packages_to_fetch) {
var entry = lock[pkg]
// Skip local packages
if (entry && entry.type == 'local') {
skip_count++
continue
}
// Skip core (handled separately)
if (pkg == 'core') {
skip_count++
continue
}
var result = shop.fetch(pkg)
if (result.ok) {
if (result.zip_blob) {
log.console("Fetched: " + pkg)
success_count++
} else {
skip_count++
}
} else {
log.error("Failed to fetch: " + pkg)
fail_count++
}
}
log.console("")
log.console("Fetch complete: " + text(success_count) + " fetched, " + text(skip_count) + " skipped, " + text(fail_count) + " failed")
$_.stop()

View File

@@ -50,6 +50,8 @@ the cell shop looks like this:
... ...
link.toml - temporary links for this cell shop link.toml - temporary links for this cell shop
lock.toml - manifest of installed packages lock.toml - manifest of installed packages
lib
<dynamic libraries>
cache cache
<downloaded packages> <downloaded packages>
build - content addressed hash build - content addressed hash

View File

@@ -43,36 +43,44 @@ var utf8 = use_embed('utf8')
var js = use_embed('js') var js = use_embed('js')
var fd = use_embed('fd') var fd = use_embed('fd')
// Get the core path from C runtime // Get the shop path from HOME environment
var core_path = hidden.core_path var home = os.getenv('HOME') || os.getenv('USERPROFILE')
if (!core_path) { if (!home) {
throw new Error('Core path not set - cell shop not properly initialized') throw new Error('Could not determine home directory')
}
var shop_path = home + '/.cell'
var packages_path = shop_path + '/packages'
var core_path = packages_path + '/core'
if (!fd.is_dir(core_path)) {
throw new Error('Cell shop not found at ' + shop_path + '. Run "cell install" to set up.')
} }
var use_cache = {} var use_cache = {}
use_cache['core/os'] = os
// Load a core module from the file system // Load a core module from the file system
function use_core(path) { function use_core(path) {
var cache_path = `2::${path}`; var cache_key = 'core/' + path
if (use_cache[cache_path]) if (use_cache[cache_key])
return use_cache[cache_path]; return use_cache[cache_key];
var sym = use_embed(path.replace('/','_')) var sym = use_embed(path.replace('/','_'))
// Core scripts are now in .cell/core/scripts // Core scripts are in packages/core/
var file_path = core_path + '/' + path + MOD_EXT var file_path = core_path + '/' + path + MOD_EXT
if (fd.is_file(file_path)) { if (fd.is_file(file_path)) {
var script_blob = fd.slurp(file_path) var script_blob = fd.slurp(file_path)
var script = utf8.decode(script_blob) var script = utf8.decode(script_blob)
var mod = `(function setup_module($_){${script}})` var mod = `(function setup_module($_){${script}})`
var fn = js.eval(path, mod) var fn = js.eval('core:' + path, mod)
var result = fn.call(sym); var result = fn.call(sym);
use_cache[cache_path] = result; use_cache[cache_key] = result;
return result; return result;
} }
use_cache[cache_path] = sym; use_cache[cache_key] = sym;
return sym; return sym;
} }
@@ -205,47 +213,27 @@ function create_actor(desc = {id:guid()}) {
var $_ = create_actor() var $_ = create_actor()
var shop = use('shop')
os.use_cache = use_cache os.use_cache = use_cache
shop.set_os(os, $_) os.global_shop_path = shop_path
var shop = use('shop')
globalThis.use = shop.use globalThis.use = shop.use
var config = shop.load_config()
// Get package name from a resolved path
function get_package_from_path(path) {
if (!path) return null
var modules_prefix = shop.get_shop_path() + '/modules/'
if (path.startsWith(modules_prefix)) {
var rest = path.substring(modules_prefix.length)
var slash_idx = rest.indexOf('/')
if (slash_idx > 0) {
return rest.substring(0, slash_idx)
}
}
return null
}
globalThis.json = use('json') globalThis.json = use('json')
var time = use('time') var time = use('time')
var default_config = { var config = {
ar_timer: 60, ar_timer: 60,
actor_memory:0, actor_memory:0,
net_service:0.1, net_service:0.1,
reply_timeout:60, reply_timeout:60,
main: false, main: true
} }
config ??= {}
config.system ??= {}
config.system.__proto__ = default_config
cell.config = config cell.config = config
ENETSERVICE = config.system.net_service ENETSERVICE = config.net_service
REPLYTIMEOUT = config.system.reply_timeout REPLYTIMEOUT = config.reply_timeout
/* /*
When handling a message, the message appears like this: When handling a message, the message appears like this:
@@ -609,12 +597,12 @@ function turn(msg)
//log.console(`FIXME: need to get main from config, not just set to true`) //log.console(`FIXME: need to get main from config, not just set to true`)
//log.console(`FIXME: remove global access (ie globalThis.use)`) //log.console(`FIXME: remove global access (ie globalThis.use)`)
//log.console(`FIXME: add freeze/unfreeze at this level, so we can do it (but scripts cannot)`) //log.console(`FIXME: add freeze/unfreeze at this level, so we can do it (but scripts cannot)`)
actor_mod.register_actor(cell.id, turn, true, config.system.ar_timer) actor_mod.register_actor(cell.id, turn, true, config.ar_timer)
if (config.system.actor_memory) if (config.actor_memory)
js.mem_limit(config.system.actor_memory) js.mem_limit(config.actor_memory)
if (config.system.stack_max) if (config.stack_max)
js.max_stacksize(config.system.stack_max); js.max_stacksize(config.system.stack_max);
overling = cell.args.overling overling = cell.args.overling
@@ -648,21 +636,6 @@ if (!program) {
os.exit(1) os.exit(1)
} }
// Find the package containing the program
// The program path is resolved relative to current directory
// Find the package containing the program
// The program path is resolved relative to current directory
// var package_dir = shop.set_current_package(program)
// if (package_dir) {
// // Reload config from the package
// config = shop.load_config()
// if (config) {
// config.system = config.system || {}
// config.system.__proto__ = default_config
// cell.config = config
// }
// }
function handle_actor_disconnect(id) { function handle_actor_disconnect(id) {
var greeter = greeters[id] var greeter = greeters[id]
if (greeter) { if (greeter) {
@@ -751,9 +724,15 @@ actor_mod.setname(cell.args.program)
var prog = cell.args.program var prog = cell.args.program
// Resolve the main program path var package = use('package')
var locator = shop.resolve_locator(cell.args.program + ".ce", null) var locator = shop.resolve_locator(cell.args.program + ".ce", null)
if (!locator) {
var pkg = package.find_package_dir(cell.args.program + ".ce")
locator = shop.resolve_locator(cell.args.program + ".ce", pkg)
}
if (!locator) if (!locator)
throw new Error(`Main program ${cell.args.program} could not be found`) throw new Error(`Main program ${cell.args.program} could not be found`)

18
list.ce
View File

@@ -4,6 +4,7 @@
// cell list package <name> -> list the packages for the package <name> // cell list package <name> -> list the packages for the package <name>
var shop = use('shop') var shop = use('shop')
var pkg = use('package')
var mode = 'local' var mode = 'local'
var target_pkg = null var target_pkg = null
@@ -56,23 +57,16 @@ if (mode == 'local') {
if (all.length == 0) log.console(" (none)") if (all.length == 0) log.console(" (none)")
} else if (mode == 'shop') { } else if (mode == 'shop') {
log.console("Shop Packages:") log.console("Shop Packages:")
var all = shop.list_shop_packages() var all = shop.list_packages()
// Sort by package name or something
if (all.length == 0) { if (all.length == 0)
log.console(" (none)") log.console(" (none)")
} else { else
for (var i = 0; i < all.length; i++) { all.forEach(package => log.console(" " + package))
var item = all[i]
var name = item.package || "unknown"
var ver = item.commit || item.type || "unknown"
log.console(" " + name + " [" + ver + "]")
}
}
} }
function print_deps(ctx) { function print_deps(ctx) {
var deps = shop.dependencies(ctx) var deps = pkg.dependencies(ctx)
var aliases = [] var aliases = []
for (var k in deps) aliases.push(k) for (var k in deps) aliases.push(k)
aliases.sort() aliases.sort()

32
ls.ce
View File

@@ -3,26 +3,15 @@
// otherwise, list the local one // otherwise, list the local one
var shop = use('shop') var shop = use('shop')
var package = use('package')
var ctx = null var ctx = null
var pkg_name = "Local" var pkg = args[0] || package.find_package_dir('.')
var modules = package.list_modules(pkg)
if (args && args.length > 0) { var programs = package.list_programs(pkg)
var alias = args[0]
ctx = shop.get_normalized_module(alias, null)
if (!ctx) {
log.console("Package '" + alias + "' not found in dependencies.")
$_.stop()
return
}
pkg_name = alias + " (" + ctx + ")"
}
var modules = shop.list_modules(ctx)
log.console("Modules in " + pkg_name + ":")
log.console("Modules in " + pkg + ":")
modules.sort() modules.sort()
if (modules.length == 0) { if (modules.length == 0) {
log.console(" (none)") log.console(" (none)")
} else { } else {
@@ -31,4 +20,15 @@ if (modules.length == 0) {
} }
} }
log.console("")
log.console("Programs in " + pkg + ":")
programs.sort()
if (programs.length == 0) {
log.console(" (none)")
} else {
for (var i = 0; i < programs.length; i++) {
log.console(" " + programs[i])
}
}
$_.stop() $_.stop()

123
pack.ce Normal file
View File

@@ -0,0 +1,123 @@
// cell pack <package> [options] - Build static binary for a package and its dependencies
//
// Usage:
// cell pack <package> Build static binary for package (output: app)
// cell pack <package> -o <name> Specify output name
// cell pack <package> -t <target> Cross-compile for target platform
var build = use('build')
var shop = use('shop')
var pkg_tools = use('package')
var target = null
var output_name = 'app'
var target_package = null
var buildtype = 'release'
if (args.length < 1) {
log.error('Usage: cell pack <package> [options]')
log.error('')
log.error('Options:')
log.error(' -o, --output <name> Output name for binary (default: app)')
log.error(' -t, --target <target> Cross-compile for target platform')
log.error(' -b, --buildtype <type> Build type: release, debug, minsize (default: release)')
log.error('')
log.error('Available targets: ' + build.list_targets().join(', '))
$_.stop()
return
}
target_package = args[0]
for (var i = 1; i < args.length; i++) {
if (args[i] == '-t' || args[i] == '--target') {
if (i + 1 < args.length) {
target = args[++i]
} else {
log.error('-t requires a target')
$_.stop()
}
} else if (args[i] == '-o' || args[i] == '--output') {
if (i + 1 < args.length) {
output_name = args[++i]
} else {
log.error('-o requires an output name')
$_.stop()
}
} else if (args[i] == '-b' || args[i] == '--buildtype') {
if (i + 1 < args.length) {
buildtype = args[++i]
if (buildtype != 'release' && buildtype != 'debug' && buildtype != 'minsize') {
log.error('Invalid buildtype: ' + buildtype + '. Must be release, debug, or minsize')
$_.stop()
}
} else {
log.error('-b requires a buildtype (release, debug, minsize)')
$_.stop()
}
} else if (args[i] == '-h' || args[i] == '--help') {
log.console('Usage: cell pack <package> [options]')
log.console('')
log.console('Options:')
log.console(' -o, --output <name> Output name for binary (default: app)')
log.console(' -t, --target <target> Cross-compile for target platform')
log.console(' -b, --buildtype <type> Build type: release, debug, minsize (default: release)')
log.console('')
log.console('Available targets: ' + build.list_targets().join(', '))
$_.stop()
} else {
log.error('Unknown option: ' + args[i])
$_.stop()
}
}
// Detect target if not specified
if (!target) {
target = build.detect_host_target()
if (target) log.console('Target: ' + target)
}
if (target && !build.has_target(target)) {
log.error('Invalid target: ' + target)
log.console('Available targets: ' + build.list_targets().join(', '))
$_.stop()
}
// Prepare packages: core + dependencies + target package
var packages = ['core']
var deps = pkg_tools.gather_dependencies(target_package)
for (var i = 0; i < deps.length; i++) {
packages.push(deps[i])
}
packages.push(target_package)
// Remove duplicates
var unique_packages = []
var seen = {}
for (var i = 0; i < packages.length; i++) {
if (!seen[packages[i]]) {
seen[packages[i]] = true
unique_packages.push(packages[i])
}
}
packages = unique_packages
log.console('Preparing packages...')
for (var package of packages) {
if (package == 'core') continue
shop.extract(package)
}
log.console('Building static binary from ' + text(packages.length) + ' packages: ' + packages.join(', '))
try {
var result = build.build_static(packages, target, output_name, buildtype)
log.console('Build complete: ' + result)
} catch (e) {
log.error('Build failed: ')
log.error(e)
$_.stop()
}
$_.stop()

305
package.cm Normal file
View File

@@ -0,0 +1,305 @@
var package = {}
var fd = use('fd')
var toml = use('toml')
var os = use('os')
function get_path(name)
{
return os.global_shop_path + '/packages/' + name
}
package.load_config = function(name)
{
var config_path = get_path(name) + '/cell.toml'
if (!fd.is_file(config_path))
throw new Error(`${config_path} isn't a path`)
return toml.decode(text(fd.slurp(config_path)))
}
package.save_config = function(name, config)
{
var config_path = get_path(name) + '/cell.toml'
fd.slurpwrite(config_path, utf8.encode(toml.encode(config)))
}
package.dependencies = function(name)
{
return package.load_config(name).dependencies
}
package.find_alias = function(name, locator)
{
var config = package.load_config(name)
if (!config.dependencies) return null
for (var alias in config.dependencies)
if (config.dependencies[alias] == locator)
return alias
return null
}
// alias is optional
package.add_dependency = function(name, locator, alias = locator)
{
var config = package.load_config(name)
if (!config.dependencies) config.dependencies = {}
config.dependencies[alias] = locator
package.save_config(name, config)
}
// locator can be a locator or alias
package.remove_dependency = function(name, locator)
{
var config = package.load_config(name)
if (!config.dependencies) return
if (config.dependencies[locator])
delete config.dependencies[locator]
else {
var alias = package.find_alias(name, locator)
if (alias)
delete config.dependencies[alias]
}
package.save_config(name, config)
}
package.find_package_dir = function(file)
{
var absolute = fd.realpath(file)
var dir = absolute
if (fd.is_file(dir)) {
var last_slash = dir.lastIndexOf('/')
if (last_slash > 0) dir = dir.substring(0, last_slash)
}
while (dir && dir.length > 0) {
var toml_path = dir + '/cell.toml'
if (fd.is_file(toml_path)) {
return dir
}
var last_slash = dir.lastIndexOf('/')
if (last_slash <= 0) break
dir = dir.substring(0, last_slash)
}
return null
}
// For a given package,
// checks for an alias in path, and returns
// { package, path }
// so package + path is the full path
// Returns null if no alias is found for the given path
package.split_alias = function(name, path)
{
if (!path || path.length == 0) {
return null
}
var parts = path.split('/')
var first_part = parts[0]
var config = package.load_config(name)
if (config.dependencies && config.dependencies[first_part]) {
var dep_locator = config.dependencies[first_part]
var remaining_path = parts.slice(1).join('/')
return { package: dep_locator, path: remaining_path }
}
return null
}
package.gather_dependencies = function(name)
{
var all_deps = {}
var visited = {}
function gather_recursive(pkg_name) {
if (visited[pkg_name]) return
visited[pkg_name] = true
var deps = package.dependencies(pkg_name)
if (!deps) return
for (var alias in deps) {
var locator = deps[alias]
if (!all_deps[locator]) {
all_deps[locator] = true
gather_recursive(locator)
}
}
}
gather_recursive(name)
return Object.keys(all_deps)
}
package.list_files = function(pkg) {
var dir = get_path(pkg)
var files = []
var walk = function(current_dir, current_prefix) {
var list = fd.readdir(current_dir)
if (!list) return
for (var i = 0; i < list.length; i++) {
var item = list[i]
if (item == '.' || item == '..') continue
if (item.startsWith('.')) continue
// Skip build directories in root
var full_path = current_dir + "/" + item
var rel_path = current_prefix ? current_prefix + "/" + item : item
var st = fd.stat(full_path)
if (st.isDirectory) {
walk(full_path, rel_path)
} else {
files.push(rel_path)
}
}
}
if (fd.is_dir(dir)) {
walk(dir, "")
}
return files
}
package.list_modules = function(name) {
var files = package.list_files(name)
var modules = []
for (var i = 0; i < files.length; i++) {
if (files[i].endsWith('.cm')) {
modules.push(files[i].substring(0, files[i].length - 3))
}
}
return modules
}
package.list_programs = function(name) {
var files = package.list_files(name)
var programs = []
for (var i = 0; i < files.length; i++) {
if (files[i].endsWith('.ce')) {
programs.push(files[i].substring(0, files[i].length - 3))
}
}
return programs
}
// Get flags from cell.toml for a package
// flag_type is 'CFLAGS' or 'LDFLAGS'
// target is optional (e.g., 'macos_arm64', 'playdate')
// Returns an array of flag strings
package.get_flags = function(name, flag_type, target) {
var config = package.load_config(name)
var flags = []
// Base flags
if (config.compilation && config.compilation[flag_type]) {
var base = config.compilation[flag_type]
flags = flags.concat(base.split(/\s+/).filter(function(f) { return f.length > 0 }))
}
// Target-specific flags
if (target && config.compilation && config.compilation[target] && config.compilation[target][flag_type]) {
var target_flags = config.compilation[target][flag_type]
flags = flags.concat(target_flags.split(/\s+/).filter(function(f) { return f.length > 0 }))
}
return flags
}
// Get all C files for a package, handling target-specific variants
// Excludes main.c for dynamic builds (when exclude_main is true)
// Handles patterns like fd.c vs fd_playdate.c
package.get_c_files = function(name, target, exclude_main) {
var toolchains = use('toolchains')
var known_targets = Object.keys(toolchains)
var files = package.list_files(name)
// Group files by their base name (without target suffix)
var groups = {} // base_key -> { generic: file, variants: { target: file } }
for (var i = 0; i < files.length; i++) {
var file = files[i]
if (!file.endsWith('.c') && !file.endsWith('.cpp')) continue
var ext = file.endsWith('.cpp') ? '.cpp' : '.c'
var base = file.substring(0, file.length - ext.length)
var dir = ''
var name_part = base
var slash = base.lastIndexOf('/')
if (slash >= 0) {
dir = base.substring(0, slash + 1)
name_part = base.substring(slash + 1)
}
// Check for target suffix
var is_variant = false
var variant_target = null
var generic_name = name_part
for (var t = 0; t < known_targets.length; t++) {
var suffix = '_' + known_targets[t]
if (name_part.endsWith(suffix)) {
is_variant = true
variant_target = known_targets[t]
generic_name = name_part.substring(0, name_part.length - suffix.length)
break
}
}
var group_key = dir + generic_name + ext
if (!groups[group_key]) {
groups[group_key] = { generic: null, variants: {} }
}
if (is_variant) {
groups[group_key].variants[variant_target] = file
} else {
groups[group_key].generic = file
}
}
// Select appropriate file from each group
var result = []
for (var key in groups) {
var group = groups[key]
var selected = null
// Prefer target-specific variant if available
if (target && group.variants[target]) {
selected = group.variants[target]
} else if (group.generic) {
selected = group.generic
}
if (selected) {
// Skip main.c if requested
if (exclude_main) {
var basename = selected
var s = selected.lastIndexOf('/')
if (s >= 0) basename = selected.substring(s + 1)
if (basename == 'main.c' || basename.startsWith('main_')) continue
}
result.push(selected)
}
}
return result
}
// Get the absolute path for a package
package.get_dir = function(name) {
return get_path(name)
}
return package

View File

@@ -1,29 +0,0 @@
// cell replace [package] [path]
// replace a package with a local directory
var shop = use('shop')
if (args.length < 2) {
log.console("Usage: cell replace <package> <path>")
log.console(" cell replace <package> --remove")
$_.stop()
}
var pkg = args[0]
var path = args[1]
if (path == '--remove') {
if (shop.remove_replacement(pkg)) {
log.console("Replacement removed.")
} else {
log.console("Failed to remove replacement.")
}
} else {
if (shop.add_replacement(pkg, path)) {
log.console("Replacement added.")
} else {
log.console("Failed to add replacement.")
}
}
$_.stop()

1896
shop.cm

File diff suppressed because it is too large Load Diff

234
toolchains.cm Normal file
View File

@@ -0,0 +1,234 @@
return {
playdate: {
c: 'arm-none-eabi-gcc',
cpp: 'arm-none-eabi-g++',
ar: 'arm-none-eabi-ar',
strip: 'arm-none-eabi-strip',
objcopy: 'arm-none-eabi-objcopy',
ld: 'arm-none-eabi-gcc',
system: 'playdate',
cpu_family: 'arm',
cpu: 'cortex-m7',
endian: 'little',
c_args: ['-mcpu=cortex-m7', '-mthumb', '-mfloat-abi=hard', '-mfpu=fpv5-sp-d16', '-fno-exceptions'],
c_link_args: ['-mcpu=cortex-m7', '-mthumb', '-mfloat-abi=hard', '-mfpu=fpv5-sp-d16', '-nostartfiles', "-T/Users/john/Developer/PlaydateSDK/C_API/buildsupport/link_map.ld"]
},
windows: {
c: 'x86_64-w64-mingw32-gcc',
cpp: 'x86_64-w64-mingw32-g++',
ar: 'x86_64-w64-mingw32-ar',
windres: 'x86_64-w64-mingw32-windres',
strip: 'x86_64-w64-mingw32-strip',
system: 'windows',
cpu_family: 'x86_64',
cpu: 'x86_64',
endian: 'little',
c_args: [],
c_link_args: []
},
windows_i686: {
c: 'i686-w64-mingw32-gcc',
cpp: 'i686-w64-mingw32-g++',
ar: 'i686-w64-mingw32-ar',
windres: 'i686-w64-mingw32-windres',
strip: 'i686-w64-mingw32-strip',
system: 'windows',
cpu_family: 'x86',
cpu: 'i686',
endian: 'little',
c_args: [],
c_link_args: []
},
linux: {
c: 'zig cc -target x86_64-linux-musl',
cpp: 'zig c++ -target x86_64-linux-musl',
ar: 'zig ar',
strip: 'strip',
system: 'linux',
cpu_family: 'x86_64',
cpu: 'x86_64',
endian: 'little',
c_args: [],
c_link_args: []
},
linux_arm64: {
c: 'zig cc -target aarch64-linux-musl',
cpp: 'zig c++ -target aarch64-linux-musl',
ar: 'zig ar',
strip: 'strip',
system: 'linux',
cpu_family: 'aarch64',
cpu: 'aarch64',
endian: 'little',
c_args: [],
c_link_args: []
},
macos_arm64: {
c: 'clang -target arm64-apple-macos11',
cpp: 'clang++ -target arm64-apple-macos11',
ar: 'ar',
strip: 'strip',
system: 'darwin',
cpu_family: 'aarch64',
cpu: 'aarch64',
endian: 'little',
c_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk'],
c_link_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk']
},
macos_x86_64: {
c: 'clang -target x86_64-apple-macos10.12',
cpp: 'clang++ -target x86_64-apple-macos10.12',
ar: 'ar',
strip: 'strip',
system: 'darwin',
cpu_family: 'x86_64',
cpu: 'x86_64',
endian: 'little',
c_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk'],
c_link_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk']
},
ios_arm64: {
c: 'clang -target arm64-apple-ios12.0',
cpp: 'clang++ -target arm64-apple-ios12.0',
ar: 'ar',
strip: 'strip',
system: 'ios',
cpu_family: 'aarch64',
cpu: 'aarch64',
endian: 'little',
c_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk', '-fembed-bitcode'],
c_link_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk', '-fembed-bitcode']
},
ios_simulator_arm64: {
c: 'clang -target arm64-apple-ios12.0-simulator',
cpp: 'clang++ -target arm64-apple-ios12.0-simulator',
ar: 'ar',
strip: 'strip',
system: 'ios',
cpu_family: 'aarch64',
cpu: 'aarch64',
endian: 'little',
c_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'],
c_link_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk']
},
ios_simulator_x86_64: {
c: 'clang -target x86_64-apple-ios12.0-simulator',
cpp: 'clang++ -target x86_64-apple-ios12.0-simulator',
ar: 'ar',
strip: 'strip',
system: 'ios',
cpu_family: 'x86_64',
cpu: 'x86_64',
endian: 'little',
c_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'],
c_link_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk']
},
tvos_arm64: {
c: 'clang -target arm64-apple-tvos12.0',
cpp: 'clang++ -target arm64-apple-tvos12.0',
ar: 'ar',
strip: 'strip',
system: 'tvos',
cpu_family: 'aarch64',
cpu: 'aarch64',
endian: 'little',
c_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk', '-fembed-bitcode'],
c_link_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk', '-fembed-bitcode']
},
tvos_simulator_arm64: {
c: 'clang -target arm64-apple-tvos12.0-simulator',
cpp: 'clang++ -target arm64-apple-tvos12.0-simulator',
ar: 'ar',
strip: 'strip',
system: 'tvos',
cpu_family: 'aarch64',
cpu: 'aarch64',
endian: 'little',
c_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'],
c_link_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk']
},
tvos_simulator_x86_64: {
c: 'clang -target x86_64-apple-tvos12.0-simulator',
cpp: 'clang++ -target x86_64-apple-tvos12.0-simulator',
ar: 'ar',
strip: 'strip',
system: 'tvos',
cpu_family: 'x86_64',
cpu: 'x86_64',
endian: 'little',
c_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'],
c_link_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk']
},
watchos_arm64: {
c: 'clang -target arm64_32-apple-watchos5.0',
cpp: 'clang++ -target arm64_32-apple-watchos5.0',
ar: 'ar',
strip: 'strip',
system: 'watchos',
cpu_family: 'aarch64',
cpu: 'arm64_32',
endian: 'little',
c_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs/WatchOS.sdk', '-fembed-bitcode'],
c_link_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs/WatchOS.sdk', '-fembed-bitcode']
},
watchos_simulator_arm64: {
c: 'clang -target arm64-apple-watchos5.0-simulator',
cpp: 'clang++ -target arm64-apple-watchos5.0-simulator',
ar: 'ar',
strip: 'strip',
system: 'watchos',
cpu_family: 'aarch64',
cpu: 'aarch64',
endian: 'little',
c_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/WatchSimulator.platform/Developer/SDKs/WatchSimulator.sdk'],
c_link_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/WatchSimulator.platform/Developer/SDKs/WatchSimulator.sdk']
},
watchos_simulator_x86_64: {
c: 'clang -target x86_64-apple-watchos5.0-simulator',
cpp: 'clang++ -target x86_64-apple-watchos5.0-simulator',
ar: 'ar',
strip: 'strip',
system: 'watchos',
cpu_family: 'x86_64',
cpu: 'x86_64',
endian: 'little',
c_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/WatchSimulator.platform/Developer/SDKs/WatchSimulator.sdk'],
c_link_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/WatchSimulator.platform/Developer/SDKs/WatchSimulator.sdk']
},
visionos_arm64: {
c: 'clang -target arm64-apple-xros1.0',
cpp: 'clang++ -target arm64-apple-xros1.0',
ar: 'ar',
strip: 'strip',
system: 'visionos',
cpu_family: 'aarch64',
cpu: 'aarch64',
endian: 'little',
c_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/XROS.platform/Developer/SDKs/XROS.sdk'],
c_link_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/XROS.platform/Developer/SDKs/XROS.sdk']
},
visionos_simulator_arm64: {
c: 'clang -target arm64-apple-xros1.0-simulator',
cpp: 'clang++ -target arm64-apple-xros1.0-simulator',
ar: 'ar',
strip: 'strip',
system: 'visionos',
cpu_family: 'aarch64',
cpu: 'aarch64',
endian: 'little',
c_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/XRSimulator.platform/Developer/SDKs/XRSimulator.sdk'],
c_link_args: ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/XRSimulator.platform/Developer/SDKs/XRSimulator.sdk']
},
emscripten: {
c: 'emcc',
cpp: 'em++',
ar: 'emar',
strip: 'emstrip',
system: 'emscripten',
cpu_family: 'wasm32',
cpu: 'wasm32',
endian: 'little',
c_args: [],
c_link_args: []
}
}

View File

@@ -1,45 +1,77 @@
// cell update [alias] - Update packages to latest versions and rebuild dynamic libraries // cell update - Update packages from remote sources
// //
// This command: // This command checks for updates to all packages and downloads new versions.
// 1. Updates all packages from their remote sources // For local packages, ensures the symlink is correct.
// 2. Rebuilds all dynamic libraries via 'cell build -d' // For remote packages, checks the remote for new commits.
//
// Usage:
// cell update - Update all packages
// cell update <package> - Update a specific package
var shop = use('shop') var shop = use('shop')
var os = use('os')
var target = null var target_pkg = null
// Parse arguments // Parse arguments
for (var i = 0; i < args.length; i++) { for (var i = 0; i < args.length; i++) {
if (args[i] == '--target' || args[i] == '-t') { if (args[i] == '--help' || args[i] == '-h') {
if (i + 1 < args.length) { log.console("Usage: cell update [package]")
target = args[i + 1] log.console("Update packages from remote sources.")
i++
}
} else if (args[i] == '--help' || args[i] == '-h') {
log.console("Usage: cell update [options]")
log.console("Update packages and rebuild dynamic libraries.")
log.console("") log.console("")
log.console("Options:") log.console("Arguments:")
log.console(" --target, -t <target> Build for specific target platform") log.console(" package Optional package name to update. If omitted, updates all.")
log.console("") log.console("")
log.console("This command updates all installed packages from their remote") log.console("This command checks for updates to all packages and downloads")
log.console("sources, then rebuilds all dynamic libraries.") log.console("new versions. For local packages, ensures the symlink is correct.")
$_.stop() $_.stop()
} else if (!args[i].startsWith('-')) {
target_pkg = args[i]
} }
} }
var packages = shop.list_shop_packages() function update_and_fetch(pkg)
{
log.console("Checking for updates (" + text(packages.length) + " packages)...") var lock = shop.load_lock()
var old_entry = lock[pkg]
// 1. Update all packages var old_commit = old_entry ? old_entry.commit : null
for (var info of packages) {
var pack = info.package
if (!pack || pack == 'core') continue
log.console("Updating " + pack) var new_entry = shop.update(pkg)
shop.update(pack)
if (new_entry && new_entry.commit) {
log.console(" " + pkg + " " + old_commit.substring(0, 8) + " -> " + new_entry.commit.substring(0, 8))
shop.fetch(pkg)
shop.build_package_scripts(pkg)
return true
}
return false
}
if (target_pkg) {
if (update_and_fetch(target_pkg))
log.console("Updated " + target_pkg + ".")
else
log.console(target_pkg + " is up to date.")
} else {
var packages = shop.list_packages()
var pkg_count = packages.length
log.console("Checking for updates (" + text(pkg_count) + " package" + (pkg_count == 1 ? "" : "s") + ")...")
var updated_count = 0
for (var i = 0; i < packages.length; i++) {
var pkg = packages[i]
if (pkg == 'core') continue
if (update_and_fetch(pkg)) {
updated_count++
}
}
if (updated_count > 0) {
log.console("Updated " + text(updated_count) + " package" + (updated_count == 1 ? "" : "s") + ".")
} else {
log.console("All packages are up to date.")
}
} }
$_.stop() $_.stop()

3
why.ce
View File

@@ -1,4 +1,5 @@
var shop = use('shop') var shop = use('shop')
var pkg = use('package')
if (!args || args.length < 1) { if (!args || args.length < 1) {
log.console("Usage: cell why <package>") log.console("Usage: cell why <package>")
@@ -19,7 +20,7 @@ var found = false
// stack: array of {alias, pkg} leading to current_pkg // stack: array of {alias, pkg} leading to current_pkg
function search(current_pkg, stack) { function search(current_pkg, stack) {
var deps = shop.dependencies(current_pkg) var deps = pkg.dependencies(current_pkg)
// Sort for consistent output // Sort for consistent output
var aliases = [] var aliases = []