193 lines
4.9 KiB
Plaintext
193 lines
4.9 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 pkg_tools = use('package')
|
|
var build = use('build')
|
|
|
|
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) {
|
|
return shop.trace_deps(file_path)
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// Expand C packages into individual files for parallel compilation
|
|
var target = build.detect_host_target()
|
|
var pkg = null
|
|
var c_files = null
|
|
var k = 0
|
|
j = 0
|
|
while (j < length(deps.c_packages)) {
|
|
pkg = deps.c_packages[j]
|
|
if (pkg != 'core') {
|
|
c_files = pkg_tools.get_c_files(pkg, target, true)
|
|
k = 0
|
|
while (k < length(c_files)) {
|
|
uncached[] = {type: 'c_file', package: pkg, file: c_files[k]}
|
|
k = k + 1
|
|
}
|
|
}
|
|
j = j + 1
|
|
}
|
|
|
|
return uncached
|
|
}
|
|
|
|
function item_name(item) {
|
|
if (item.path) return item.path
|
|
if (item.file) return item.package + '/' + item.file
|
|
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,
|
|
file: item.file
|
|
})
|
|
}
|
|
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: ' + text(length(uncached)) + ' modules to compile')
|
|
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()
|
|
}
|