// cell boot [--native] - Pre-compile all module dependencies in parallel // // Discovers all transitive module dependencies for a program, // checks which are not yet cached, and compiles uncached ones // in parallel using worker actors composed via parallel() requestors. // // Also used as a child actor by engine.cm for auto-boot. var shop = use('internal/shop') var fd = use('fd') var is_native = false var target_prog = null var target_pkg = null var i = 0 // Child actor mode: receive message from engine.cm var _child_mode = false var run_boot = null $receiver(function(msg) { _child_mode = true is_native = msg.native || false target_prog = msg.program target_pkg = msg.package run_boot() }) // CLI mode: parse arguments if (args && length(args) > 0) { for (i = 0; i < length(args); i = i + 1) { if (args[i] == '--native') { is_native = true } else if (args[i] == '--help' || args[i] == '-h') { log.console("Usage: cell boot [--native] ") log.console("") log.console("Pre-compile all module dependencies for a program.") log.console("Uncached modules are compiled in parallel.") $stop() } else if (!starts_with(args[i], '-')) { target_prog = args[i] } } if (!target_prog) { log.error("boot: no program specified") $stop() } } // Discover all transitive module dependencies for a file function discover_deps(file_path) { var visited = {} var scripts = [] var c_packages = {} function trace(fp) { if (visited[fp]) return visited[fp] = true var fi = shop.file_info(fp) var file_pkg = fi.package var idx = null var j = 0 var imp = null var mod_path = null var rinfo = null // record this script (skip the root program itself) if (ends_with(fp, '.cm')) { scripts[] = {path: fp, package: file_pkg} } var _trace = function() { idx = shop.index_file(fp) if (!idx || !idx.imports) return j = 0 while (j < length(idx.imports)) { imp = idx.imports[j] mod_path = imp.module_path rinfo = shop.resolve_import_info(mod_path, file_pkg) if (rinfo) { if (rinfo.type == 'script' && rinfo.resolved_path) { trace(rinfo.resolved_path) } else if (rinfo.type == 'native' && rinfo.package) { c_packages[rinfo.package] = true } } j = j + 1 } } disruption {} _trace() } trace(file_path) return {scripts: scripts, c_packages: array(c_packages)} } // Filter out already-cached modules function filter_uncached(deps) { var uncached = [] var j = 0 var s = null j = 0 while (j < length(deps.scripts)) { s = deps.scripts[j] if (is_native) { if (!shop.is_native_cached(s.path, s.package)) { uncached[] = {type: 'native_script', path: s.path, package: s.package} } } else { if (!shop.is_cached(s.path)) { uncached[] = {type: 'script', path: s.path, package: s.package} } } j = j + 1 } // C packages always included — build_dynamic handles its own caching j = 0 while (j < length(deps.c_packages)) { if (deps.c_packages[j] != 'core') { uncached[] = {type: 'c_package', package: deps.c_packages[j]} } j = j + 1 } return uncached } function item_name(item) { if (item.path) return item.path return item.package } // Create a requestor that spawns a compile_worker actor for one item function make_compile_requestor(item) { var worker = null var name = item_name(item) return function(callback, value) { log.console('boot: spawning worker for ' + name) $start(function(event) { if (event.type == 'greet') { worker = event.actor send(event.actor, { type: item.type, path: item.path, package: item.package }) } if (event.type == 'stop') { callback(name) } if (event.type == 'disrupt') { log.error('boot: worker failed for ' + name) callback(null, {message: 'compile failed: ' + name}) } }, 'compile_worker') return function cancel(reason) { if (worker) $stop(worker) } } } run_boot = function() { var prog_path = null var prog_info = null var deps = null var uncached = null var requestors = null var p = null // Resolve the program path if (target_prog) { p = target_prog if (ends_with(p, '.ce')) p = text(p, 0, -3) prog_info = shop.resolve_program ? shop.resolve_program(p, target_pkg) : null if (prog_info) { prog_path = prog_info.path if (!target_pkg && prog_info.pkg) target_pkg = prog_info.pkg } else { prog_path = p + '.ce' if (!fd.is_file(prog_path)) { prog_path = null } } } if (!prog_path || !fd.is_file(prog_path)) { log.error('boot: could not find program: ' + text(target_prog || '')) $stop() return } // Discover all transitive deps deps = discover_deps(prog_path) uncached = filter_uncached(deps) if (length(uncached) == 0) { log.console('boot: all modules cached') $stop() return } // Compile uncached modules in parallel using worker actors log.console('boot: compiling ' + text(length(uncached)) + ' modules...') requestors = array(uncached, make_compile_requestor) parallel(requestors)(function(results, reason) { if (reason) { log.error('boot: ' + (reason.message || text(reason))) } else { log.console('boot: compiled ' + text(length(results)) + ' modules') } $stop() }, null) } // CLI mode: start immediately if (!_child_mode && target_prog) { run_boot() }