// cell build [--target ] - Build a static cell binary for a project // Collects all C files from cell source, scripts, and project packages, // compiles them, and links into a single static executable. var build = use('build') var shop = use('shop') var fd = use('fd') var os = use('os') var targets = [ "arm64-macos", "x86_64-macos", "x86_64-linux", "arm64-linux", "windows", "playdate" ] // Parse arguments var target = null 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 { log.error("--target requires an argument") log.console("Available targets: " + targets.join(', ')) $_.stop() } } else if (args[i] == '--help' || args[i] == '-h') { log.console("Usage: cell build [--target ]") log.console("Build a static cell binary for the current project.") log.console("") log.console("Options:") log.console(" --target, -t Cross-compile for target platform") log.console("") log.console("Available targets: " + targets.join(', ')) $_.stop() } else if (args[i] == '--list-targets') { log.console("Available targets:") for (var t = 0; t < targets.length; t++) { log.console(" " + targets[t]) } $_.stop() } } // Resolve target target = build.resolve_target(target) if (target) { log.console("Building for target: " + target) } else { log.console("Building for host platform") } // Find cell package - it should be at ~/work/cell or a dependency var cell_dir = null // First check if we're in the cell directory itself if (fd.is_file('source/cell.c') && fd.is_file('source/quickjs.c')) { cell_dir = '.' log.console("Building from cell source directory") } else { // Check for cell as a local path dependency or linked package 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 = '.cell/modules/' + parsed.path if (fd.is_file(pkg_dir + '/source/cell.c')) { cell_dir = pkg_dir log.console("Using cell from dependency: " + pkg_dir) } } // Fallback: try ~/work/cell if (!cell_dir) { var home_cell = os.getenv('HOME') + '/work/cell' if (fd.is_file(home_cell + '/source/cell.c')) { cell_dir = home_cell log.console("Using cell from: " + cell_dir) } } } if (!cell_dir) { log.error("Could not find cell source. Add cell as a dependency or run from cell directory.") $_.stop() } // Collect all C files from cell var source_files = build.list_files(cell_dir + '/source') var script_files = build.list_files(cell_dir + '/scripts') // Prefix with directory 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 var c_files = build.select_c_files(all_files, target) // Debug: show which files were selected if (target == 'playdate') { for (var i = 0; i < c_files.length; i++) { if (c_files[i].indexOf('fd') >= 0 || c_files[i].indexOf('main') >= 0) { log.console(" Selected: " + c_files[i]) } } } log.console("Found " + text(c_files.length) + " C files to compile") // Get build directory var build_dir = build.get_build_dir(target) build.ensure_dir(build_dir) // Load cell config for platform-specific flags var cell_config = build.load_config(cell_dir) var target_system = build.get_target_system(target) var platform = target_system || os.platform() var cflags = build.get_flags(cell_config, platform, 'CFLAGS') var ldflags = build.get_flags(cell_config, platform, 'LDFLAGS') // Compile options var compile_options = { target: target, cflags: cflags, includes: [cell_dir + '/source'], defines: {} } // Add target-specific defines if (target == 'playdate') { compile_options.defines.TARGET_PLAYDATE = true } // Compile all C files var objects = [] for (var i = 0; i < c_files.length; i++) { var src = cell_dir + '/' + c_files[i] var obj = build_dir + '/' + c_files[i] + '.o' // Check if recompilation needed 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("Build failed") $_.stop() } } objects.push(obj) } // Collect C files from project packages var packages = shop.list_packages() for (var p = 0; p < packages.length; p++) { var pkg = packages[p] var parsed = shop.parse_package(pkg) var pkg_dir = '.cell/modules/' + parsed.path if (!fd.is_dir(pkg_dir)) continue var pkg_files = build.list_files(pkg_dir) var pkg_c_files = build.select_c_files(pkg_files, target) var pkg_config = build.load_config(pkg_dir) var pkg_ldflags = build.get_flags(pkg_config, platform, 'LDFLAGS') if (pkg_ldflags) { if (ldflags != '') ldflags += ' ' ldflags += pkg_ldflags } if (pkg_c_files.length > 0) { log.console("Compiling " + text(pkg_c_files.length) + " C files from " + parsed.path) var pkg_cflags = build.get_flags(pkg_config, platform, 'CFLAGS') // Create symbol prefix for package 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: [cell_dir + '/source', 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) } } } // Collect C files from local project (only if not building from cell source directory) if (cell_dir != '.') { var local_config = shop.load_config() || {} var local_ldflags = build.get_flags(local_config, platform, 'LDFLAGS') if (local_ldflags) { if (ldflags != '') ldflags += ' ' ldflags += local_ldflags } var local_files = build.list_files('.') var local_c_files = build.select_c_files(local_files, target) if (local_c_files.length > 0) { log.console("Compiling " + text(local_c_files.length) + " local C files") var local_cflags = build.get_flags(local_config, platform, 'CFLAGS') for (var f = 0; f < local_c_files.length; f++) { var src = local_c_files[f] var obj = build_dir + '/local/' + local_c_files[f] + '.o' var safe_name = local_c_files[f].substring(0, local_c_files[f].lastIndexOf('.')).replace(/\//g, '_').replace(/-/g, '_') var use_name = 'js_local_' + safe_name + '_use' var local_options = { target: target, cflags: local_cflags, includes: [cell_dir + '/source', '.'], defines: { CELL_USE_NAME: use_name } } var needs_compile = true if (fd.is_file(obj)) { var src_stat = fd.stat(src) var obj_stat = fd.stat(obj) if (src_stat && obj_stat && src_stat.mtime <= obj_stat.mtime) { needs_compile = false } } 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) var exe_name = build_dir + '/cell' + exe_ext var link_options = { target: target, ldflags: ldflags, libs: [] } // Add platform-specific libraries if (!target || platform == 'macOS' || platform == 'darwin') { // macOS needs no extra libs for static build } else if (target == 'windows') { link_options.libs.push('ws2_32') link_options.libs.push('winmm') } var result = build.link_executable(objects, exe_name, link_options) if (!result) { log.error("Linking failed") $_.stop() } // TODO: Append core.qop to executable // For now, just report success log.console("") log.console("Build complete: " + exe_name) log.console("") log.console("Note: To create a fully functional cell binary, you need to append core.qop:") log.console(" cat core.qop >> " + exe_name) $_.stop()