fix
This commit is contained in:
21
Makefile
21
Makefile
@@ -7,9 +7,17 @@
|
||||
# The cell shop is at ~/.cell and core scripts are installed to ~/.cell/core
|
||||
|
||||
CELL_SHOP = $(HOME)/.cell
|
||||
CELL_CORE = $(CELL_SHOP)/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
|
||||
cp cell_main cell
|
||||
chmod +x cell
|
||||
@@ -26,17 +34,6 @@ libcell_runtime.dylib: $(CELL_SHOP)/build/dynamic
|
||||
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
|
||||
|
||||
# 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
|
||||
$(CELL_SHOP):
|
||||
mkdir -p $(CELL_SHOP)
|
||||
|
||||
@@ -217,6 +217,7 @@ static const JSCFunctionListEntry js_writer_funcs[] = {
|
||||
|
||||
JSValue js_reader_mod(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
#ifndef MINIZ_NO_TIME
|
||||
const char *file = JS_ToCString(js,argv[0]);
|
||||
if (!file)
|
||||
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);
|
||||
#else
|
||||
return JS_ThrowInternalError(js, "MINIZ_NO_TIME is defined");
|
||||
#endif
|
||||
}
|
||||
|
||||
JSValue js_reader_exists(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
|
||||
@@ -6,7 +6,7 @@ var time = use('time')
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Test configurations
|
||||
const iterations = {
|
||||
def iterations = {
|
||||
simple: 10000000,
|
||||
medium: 1000000,
|
||||
complex: 100000
|
||||
@@ -154,7 +154,7 @@ function benchObjectCreation() {
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
var constructorTime = measureTime(function() {
|
||||
var defructorTime = measureTime(function() {
|
||||
for (var i = 0; i < iterations.medium; i++) {
|
||||
var p = new Point(i, i * 2);
|
||||
}
|
||||
@@ -179,7 +179,7 @@ function benchObjectCreation() {
|
||||
|
||||
return {
|
||||
literalTime: literalTime,
|
||||
constructorTime: constructorTime,
|
||||
defructorTime: defructorTime,
|
||||
prototypeTime: prototypeTime
|
||||
};
|
||||
}
|
||||
@@ -342,9 +342,9 @@ var objResults = benchObjectCreation();
|
||||
log.console(" Literal: " + objResults.literalTime.toFixed(3) + "s => " +
|
||||
(iterations.medium / objResults.literalTime).toFixed(1) + " creates/sec [" +
|
||||
(objResults.literalTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Constructor: " + objResults.constructorTime.toFixed(3) + "s => " +
|
||||
(iterations.medium / objResults.constructorTime).toFixed(1) + " creates/sec [" +
|
||||
(objResults.constructorTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Constructor: " + objResults.defructorTime.toFixed(3) + "s => " +
|
||||
(iterations.medium / objResults.defructorTime).toFixed(1) + " creates/sec [" +
|
||||
(objResults.defructorTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Prototype: " + objResults.prototypeTime.toFixed(3) + "s => " +
|
||||
(iterations.medium / objResults.prototypeTime).toFixed(1) + " creates/sec [" +
|
||||
(objResults.prototypeTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||
|
||||
877
build.ce
877
build.ce
@@ -1,845 +1,112 @@
|
||||
// cell build [options] [actor] - Build cell binary
|
||||
//
|
||||
// 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
|
||||
// cell build [options] - Build dynamic libraries locally for the current machine
|
||||
//
|
||||
// The actor argument specifies the entry point (e.g., "accio" for accio.ce)
|
||||
// If no actor is specified for static builds, builds cell itself.
|
||||
// Usage:
|
||||
// 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 shop = use('shop')
|
||||
var pkg_tools = use('package')
|
||||
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 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 buildtype = 'release'
|
||||
|
||||
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) {
|
||||
target = args[i + 1]
|
||||
i++
|
||||
target = args[++i]
|
||||
} else {
|
||||
log.error("--target requires an argument")
|
||||
log.console("Available targets: " + targets.join(', '))
|
||||
log.error('-t requires a target')
|
||||
$_.stop()
|
||||
}
|
||||
} else if (args[i] == '-p' || args[i] == '--package') {
|
||||
if (i + 1 < args.length) {
|
||||
target_package = args[i + 1]
|
||||
dynamic_mode = true
|
||||
i++
|
||||
target_package = args[++i]
|
||||
} else {
|
||||
log.error("-p requires a package name")
|
||||
log.error('-p requires a package name')
|
||||
$_.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] == '-o' || args[i] == '--output') {
|
||||
} else if (args[i] == '-b' || args[i] == '--buildtype') {
|
||||
if (i + 1 < args.length) {
|
||||
output_name = args[i + 1]
|
||||
i++
|
||||
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("-o requires an output name")
|
||||
log.error('-b requires a buildtype (release, debug, minsize)')
|
||||
$_.stop()
|
||||
}
|
||||
} else if (args[i] == '--help' || args[i] == '-h') {
|
||||
log.console("Usage: cell build [options] [actor]")
|
||||
log.console("Build cell binaries or dynamic libraries.")
|
||||
log.console("")
|
||||
log.console("Options:")
|
||||
log.console(" -d, --dynamic Build dynamic libraries for all packages")
|
||||
log.console(" -p, --package <pkg> Build dynamic library for specific package")
|
||||
log.console(" -o, --output <name> Output name for static binary")
|
||||
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(', '))
|
||||
log.console('Usage: cell build [options]')
|
||||
log.console('')
|
||||
log.console('Options:')
|
||||
log.console(' -p, --package <pkg> Build specific package only')
|
||||
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 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++) {
|
||||
log.console(" " + targets[t])
|
||||
log.console(' ' + targets[t])
|
||||
}
|
||||
$_.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) {
|
||||
var host_platform = os.platform()
|
||||
var host_arch = os.arch ? os.arch() : 'arm64' // Default to arm64 if not available
|
||||
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)
|
||||
target = build.detect_host_target()
|
||||
if (target) log.console('Target: ' + target)
|
||||
}
|
||||
|
||||
if (target && !build.has_target(target)) {
|
||||
log.console("Available targets: " + targets.join(', '))
|
||||
throw new Error("Invalid target: " + target)
|
||||
}
|
||||
|
||||
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.")
|
||||
log.error('Invalid target: ' + target)
|
||||
log.console('Available targets: ' + build.list_targets().join(', '))
|
||||
$_.stop()
|
||||
}
|
||||
log.console("Using cell from: " + cell_dir)
|
||||
|
||||
if (dynamic_mode) {
|
||||
// ========================================================================
|
||||
// DYNAMIC MODE: Build cellmod.dylib for all packages
|
||||
// ========================================================================
|
||||
var build_dir = get_target_build_dir()
|
||||
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")
|
||||
}
|
||||
var packages = shop.list_packages()
|
||||
log.console('Preparing packages...')
|
||||
for (var package of packages) {
|
||||
if (package == 'core') continue
|
||||
shop.extract(package)
|
||||
}
|
||||
|
||||
$_.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()
|
||||
|
||||
@@ -7,7 +7,7 @@ CFLAGS = "-x objective-c"
|
||||
LDFLAGS = "-framework CoreFoundation -framework CFNetwork"
|
||||
|
||||
[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]
|
||||
LDFLAGS = "-lws2_32 -lwinmm -liphlpapi -lbcrypt -lwinhttp -static-libgcc -static-libstdc++"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// cell config - Manage system and actor configurations
|
||||
|
||||
var toml = use('toml')
|
||||
var shop = use('shop')
|
||||
var pkg = use('package')
|
||||
var text = use('text')
|
||||
|
||||
function print_help() {
|
||||
@@ -104,7 +104,7 @@ if (args.length == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
var config = shop.load_config()
|
||||
var config = pkg.load_config()
|
||||
if (!config) {
|
||||
log.error("Failed to load cell.toml")
|
||||
$_.stop()
|
||||
@@ -171,7 +171,7 @@ switch (command) {
|
||||
}
|
||||
|
||||
set_nested(config, path, value)
|
||||
shop.save_config(config)
|
||||
pkg.save_config(config)
|
||||
log.console("Set " + key + " = " + format_value(value))
|
||||
break
|
||||
|
||||
@@ -230,7 +230,7 @@ switch (command) {
|
||||
var value = parse_value(value_str)
|
||||
|
||||
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))
|
||||
break
|
||||
|
||||
|
||||
4
fd.c
4
fd.c
@@ -568,13 +568,13 @@ JSC_CCALL(fd_slurpwrite,
|
||||
int fd = open(str, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
JS_FreeCString(js, str);
|
||||
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);
|
||||
close(fd);
|
||||
|
||||
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;
|
||||
)
|
||||
|
||||
86
fetch.ce
Normal file
86
fetch.ce
Normal 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()
|
||||
@@ -50,6 +50,8 @@ the cell shop looks like this:
|
||||
...
|
||||
link.toml - temporary links for this cell shop
|
||||
lock.toml - manifest of installed packages
|
||||
lib
|
||||
<dynamic libraries>
|
||||
cache
|
||||
<downloaded packages>
|
||||
build - content addressed hash
|
||||
|
||||
@@ -43,36 +43,44 @@ var utf8 = use_embed('utf8')
|
||||
var js = use_embed('js')
|
||||
var fd = use_embed('fd')
|
||||
|
||||
// Get the core path from C runtime
|
||||
var core_path = hidden.core_path
|
||||
if (!core_path) {
|
||||
throw new Error('Core path not set - cell shop not properly initialized')
|
||||
// Get the shop path from HOME environment
|
||||
var home = os.getenv('HOME') || os.getenv('USERPROFILE')
|
||||
if (!home) {
|
||||
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 = {}
|
||||
use_cache['core/os'] = os
|
||||
|
||||
// Load a core module from the file system
|
||||
function use_core(path) {
|
||||
var cache_path = `2::${path}`;
|
||||
if (use_cache[cache_path])
|
||||
return use_cache[cache_path];
|
||||
var cache_key = 'core/' + path
|
||||
if (use_cache[cache_key])
|
||||
return use_cache[cache_key];
|
||||
|
||||
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
|
||||
|
||||
if (fd.is_file(file_path)) {
|
||||
var script_blob = fd.slurp(file_path)
|
||||
var script = utf8.decode(script_blob)
|
||||
var mod = `(function setup_module($_){${script}})`
|
||||
var fn = js.eval(path, mod)
|
||||
var fn = js.eval('core:' + path, mod)
|
||||
var result = fn.call(sym);
|
||||
use_cache[cache_path] = result;
|
||||
use_cache[cache_key] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
use_cache[cache_path] = sym;
|
||||
use_cache[cache_key] = sym;
|
||||
return sym;
|
||||
}
|
||||
|
||||
@@ -205,47 +213,27 @@ function create_actor(desc = {id:guid()}) {
|
||||
|
||||
var $_ = create_actor()
|
||||
|
||||
var shop = use('shop')
|
||||
os.use_cache = use_cache
|
||||
shop.set_os(os, $_)
|
||||
os.global_shop_path = shop_path
|
||||
|
||||
var shop = use('shop')
|
||||
|
||||
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')
|
||||
var time = use('time')
|
||||
|
||||
var default_config = {
|
||||
var config = {
|
||||
ar_timer: 60,
|
||||
actor_memory:0,
|
||||
net_service:0.1,
|
||||
reply_timeout:60,
|
||||
main: false,
|
||||
main: true
|
||||
}
|
||||
|
||||
config ??= {}
|
||||
config.system ??= {}
|
||||
config.system.__proto__ = default_config
|
||||
|
||||
cell.config = config
|
||||
|
||||
ENETSERVICE = config.system.net_service
|
||||
REPLYTIMEOUT = config.system.reply_timeout
|
||||
ENETSERVICE = config.net_service
|
||||
REPLYTIMEOUT = config.reply_timeout
|
||||
|
||||
/*
|
||||
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: remove global access (ie globalThis.use)`)
|
||||
//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)
|
||||
js.mem_limit(config.system.actor_memory)
|
||||
if (config.actor_memory)
|
||||
js.mem_limit(config.actor_memory)
|
||||
|
||||
if (config.system.stack_max)
|
||||
if (config.stack_max)
|
||||
js.max_stacksize(config.system.stack_max);
|
||||
|
||||
overling = cell.args.overling
|
||||
@@ -648,21 +636,6 @@ if (!program) {
|
||||
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) {
|
||||
var greeter = greeters[id]
|
||||
if (greeter) {
|
||||
@@ -751,9 +724,15 @@ actor_mod.setname(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)
|
||||
|
||||
if (!locator) {
|
||||
var pkg = package.find_package_dir(cell.args.program + ".ce")
|
||||
locator = shop.resolve_locator(cell.args.program + ".ce", pkg)
|
||||
}
|
||||
|
||||
if (!locator)
|
||||
throw new Error(`Main program ${cell.args.program} could not be found`)
|
||||
|
||||
|
||||
18
list.ce
18
list.ce
@@ -4,6 +4,7 @@
|
||||
// cell list package <name> -> list the packages for the package <name>
|
||||
|
||||
var shop = use('shop')
|
||||
var pkg = use('package')
|
||||
|
||||
var mode = 'local'
|
||||
var target_pkg = null
|
||||
@@ -56,23 +57,16 @@ if (mode == 'local') {
|
||||
if (all.length == 0) log.console(" (none)")
|
||||
} else if (mode == 'shop') {
|
||||
log.console("Shop Packages:")
|
||||
var all = shop.list_shop_packages()
|
||||
// Sort by package name or something
|
||||
var all = shop.list_packages()
|
||||
|
||||
if (all.length == 0) {
|
||||
if (all.length == 0)
|
||||
log.console(" (none)")
|
||||
} else {
|
||||
for (var i = 0; i < all.length; i++) {
|
||||
var item = all[i]
|
||||
var name = item.package || "unknown"
|
||||
var ver = item.commit || item.type || "unknown"
|
||||
log.console(" " + name + " [" + ver + "]")
|
||||
}
|
||||
}
|
||||
else
|
||||
all.forEach(package => log.console(" " + package))
|
||||
}
|
||||
|
||||
function print_deps(ctx) {
|
||||
var deps = shop.dependencies(ctx)
|
||||
var deps = pkg.dependencies(ctx)
|
||||
var aliases = []
|
||||
for (var k in deps) aliases.push(k)
|
||||
aliases.sort()
|
||||
|
||||
32
ls.ce
32
ls.ce
@@ -3,26 +3,15 @@
|
||||
// otherwise, list the local one
|
||||
|
||||
var shop = use('shop')
|
||||
var package = use('package')
|
||||
|
||||
var ctx = null
|
||||
var pkg_name = "Local"
|
||||
|
||||
if (args && args.length > 0) {
|
||||
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 + ":")
|
||||
var pkg = args[0] || package.find_package_dir('.')
|
||||
var modules = package.list_modules(pkg)
|
||||
var programs = package.list_programs(pkg)
|
||||
|
||||
log.console("Modules in " + pkg + ":")
|
||||
modules.sort()
|
||||
|
||||
if (modules.length == 0) {
|
||||
log.console(" (none)")
|
||||
} 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()
|
||||
|
||||
123
pack.ce
Normal file
123
pack.ce
Normal 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
305
package.cm
Normal 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
|
||||
29
replace.ce
29
replace.ce
@@ -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()
|
||||
234
toolchains.cm
Normal file
234
toolchains.cm
Normal 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: []
|
||||
}
|
||||
}
|
||||
88
update.ce
88
update.ce
@@ -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:
|
||||
// 1. Updates all packages from their remote sources
|
||||
// 2. Rebuilds all dynamic libraries via 'cell build -d'
|
||||
// This command checks for updates to all packages and downloads new versions.
|
||||
// For local packages, ensures the symlink is correct.
|
||||
// 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 os = use('os')
|
||||
|
||||
var target = null
|
||||
var target_pkg = null
|
||||
|
||||
// Parse arguments
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
if (args[i] == '--target' || args[i] == '-t') {
|
||||
if (i + 1 < args.length) {
|
||||
target = args[i + 1]
|
||||
i++
|
||||
}
|
||||
} else if (args[i] == '--help' || args[i] == '-h') {
|
||||
log.console("Usage: cell update [options]")
|
||||
log.console("Update packages and rebuild dynamic libraries.")
|
||||
if (args[i] == '--help' || args[i] == '-h') {
|
||||
log.console("Usage: cell update [package]")
|
||||
log.console("Update packages from remote sources.")
|
||||
log.console("")
|
||||
log.console("Options:")
|
||||
log.console(" --target, -t <target> Build for specific target platform")
|
||||
log.console("Arguments:")
|
||||
log.console(" package Optional package name to update. If omitted, updates all.")
|
||||
log.console("")
|
||||
log.console("This command updates all installed packages from their remote")
|
||||
log.console("sources, then rebuilds all dynamic libraries.")
|
||||
log.console("This command checks for updates to all packages and downloads")
|
||||
log.console("new versions. For local packages, ensures the symlink is correct.")
|
||||
$_.stop()
|
||||
} else if (!args[i].startsWith('-')) {
|
||||
target_pkg = args[i]
|
||||
}
|
||||
}
|
||||
|
||||
var packages = shop.list_shop_packages()
|
||||
|
||||
log.console("Checking for updates (" + text(packages.length) + " packages)...")
|
||||
|
||||
// 1. Update all packages
|
||||
for (var info of packages) {
|
||||
var pack = info.package
|
||||
if (!pack || pack == 'core') continue
|
||||
function update_and_fetch(pkg)
|
||||
{
|
||||
var lock = shop.load_lock()
|
||||
var old_entry = lock[pkg]
|
||||
var old_commit = old_entry ? old_entry.commit : null
|
||||
|
||||
log.console("Updating " + pack)
|
||||
shop.update(pack)
|
||||
var new_entry = shop.update(pkg)
|
||||
|
||||
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()
|
||||
|
||||
3
why.ce
3
why.ce
@@ -1,4 +1,5 @@
|
||||
var shop = use('shop')
|
||||
var pkg = use('package')
|
||||
|
||||
if (!args || args.length < 1) {
|
||||
log.console("Usage: cell why <package>")
|
||||
@@ -19,7 +20,7 @@ var found = false
|
||||
// stack: array of {alias, pkg} leading to current_pkg
|
||||
|
||||
function search(current_pkg, stack) {
|
||||
var deps = shop.dependencies(current_pkg)
|
||||
var deps = pkg.dependencies(current_pkg)
|
||||
|
||||
// Sort for consistent output
|
||||
var aliases = []
|
||||
|
||||
Reference in New Issue
Block a user