224 lines
5.6 KiB
Plaintext
224 lines
5.6 KiB
Plaintext
// cell boot [--native] <program> - 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] <program>")
|
|
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()
|
|
}
|