better path resolution
This commit is contained in:
27
add.ce
27
add.ce
@@ -78,29 +78,6 @@ if (!fd.is_file(cwd + '/cell.toml')) {
|
|||||||
$stop()
|
$stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursively find all cell packages in a directory
|
|
||||||
function find_packages(dir) {
|
|
||||||
var found = []
|
|
||||||
var list = fd.readdir(dir)
|
|
||||||
if (!list) return found
|
|
||||||
if (fd.is_file(dir + '/cell.toml')) {
|
|
||||||
push(found, dir)
|
|
||||||
}
|
|
||||||
arrfor(list, function(item) {
|
|
||||||
if (item == '.' || item == '..' || item == '.cell' || item == '.git') return
|
|
||||||
var full = dir + '/' + item
|
|
||||||
var st = fd.stat(full)
|
|
||||||
var sub = null
|
|
||||||
if (st && st.isDirectory) {
|
|
||||||
sub = find_packages(full)
|
|
||||||
arrfor(sub, function(p) {
|
|
||||||
push(found, p)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
// If -r flag, find all packages recursively and add each
|
// If -r flag, find all packages recursively and add each
|
||||||
if (recursive) {
|
if (recursive) {
|
||||||
if (!locator) {
|
if (!locator) {
|
||||||
@@ -111,7 +88,9 @@ if (recursive) {
|
|||||||
log.error(`${locator} is not a directory`)
|
log.error(`${locator} is not a directory`)
|
||||||
$stop()
|
$stop()
|
||||||
}
|
}
|
||||||
locators = find_packages(resolved)
|
locators = filter(pkg.find_packages(resolved), function(p) {
|
||||||
|
return p != cwd
|
||||||
|
})
|
||||||
if (length(locators) == 0) {
|
if (length(locators) == 0) {
|
||||||
log.console("No packages found in " + resolved)
|
log.console("No packages found in " + resolved)
|
||||||
$stop()
|
$stop()
|
||||||
|
|||||||
20
fetch.ce
20
fetch.ce
@@ -33,12 +33,26 @@ for (i = 0; i < length(args); i++) {
|
|||||||
var all_packages = shop.list_packages()
|
var all_packages = shop.list_packages()
|
||||||
var lock = shop.load_lock()
|
var lock = shop.load_lock()
|
||||||
var packages_to_fetch = []
|
var packages_to_fetch = []
|
||||||
|
var _update = null
|
||||||
|
|
||||||
if (target_pkg) {
|
if (target_pkg) {
|
||||||
// Fetch specific package
|
// Fetch specific package - auto-update if not in lock
|
||||||
if (find(all_packages, target_pkg) == null) {
|
if (find(all_packages, target_pkg) == null) {
|
||||||
log.error("Package not found: " + target_pkg)
|
log.console("Package not in lock, updating: " + target_pkg)
|
||||||
$stop()
|
_update = function() {
|
||||||
|
shop.update(target_pkg)
|
||||||
|
} disruption {
|
||||||
|
log.error("Could not update package: " + target_pkg)
|
||||||
|
$stop()
|
||||||
|
}
|
||||||
|
_update()
|
||||||
|
// Reload after update
|
||||||
|
all_packages = shop.list_packages()
|
||||||
|
lock = shop.load_lock()
|
||||||
|
if (find(all_packages, target_pkg) == null) {
|
||||||
|
log.error("Package not found: " + target_pkg)
|
||||||
|
$stop()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
push(packages_to_fetch, target_pkg)
|
push(packages_to_fetch, target_pkg)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
37
install.ce
37
install.ce
@@ -79,28 +79,7 @@ if (locator && (locator == '.' || starts_with(locator, './') || starts_with(loca
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursively find all cell packages in a directory
|
var cwd = fd.realpath('.')
|
||||||
function find_packages(dir) {
|
|
||||||
var found = []
|
|
||||||
var list = fd.readdir(dir)
|
|
||||||
if (!list) return found
|
|
||||||
if (fd.is_file(dir + '/cell.toml')) {
|
|
||||||
push(found, dir)
|
|
||||||
}
|
|
||||||
arrfor(list, function(item) {
|
|
||||||
if (item == '.' || item == '..' || item == '.cell' || item == '.git') return
|
|
||||||
var full = dir + '/' + item
|
|
||||||
var st = fd.stat(full)
|
|
||||||
var sub = null
|
|
||||||
if (st && st.isDirectory) {
|
|
||||||
sub = find_packages(full)
|
|
||||||
arrfor(sub, function(p) {
|
|
||||||
push(found, p)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
// If -r flag, find all packages recursively and install each
|
// If -r flag, find all packages recursively and install each
|
||||||
if (recursive) {
|
if (recursive) {
|
||||||
@@ -112,7 +91,9 @@ if (recursive) {
|
|||||||
log.error(`${locator} is not a directory`)
|
log.error(`${locator} is not a directory`)
|
||||||
$stop()
|
$stop()
|
||||||
}
|
}
|
||||||
locators = find_packages(resolved)
|
locators = filter(pkg.find_packages(resolved), function(p) {
|
||||||
|
return p != cwd
|
||||||
|
})
|
||||||
if (length(locators) == 0) {
|
if (length(locators) == 0) {
|
||||||
log.console("No packages found in " + resolved)
|
log.console("No packages found in " + resolved)
|
||||||
$stop()
|
$stop()
|
||||||
@@ -133,6 +114,16 @@ var visited = {}
|
|||||||
|
|
||||||
// Recursive mode: install all found packages and exit
|
// Recursive mode: install all found packages and exit
|
||||||
if (recursive) {
|
if (recursive) {
|
||||||
|
if (dry_run) {
|
||||||
|
log.console("Would install:")
|
||||||
|
arrfor(locators, function(loc) {
|
||||||
|
var lock = shop.load_lock()
|
||||||
|
var exists = lock[loc] != null
|
||||||
|
log.console(" " + loc + (exists ? " (already installed)" : ""))
|
||||||
|
})
|
||||||
|
$stop()
|
||||||
|
}
|
||||||
|
|
||||||
arrfor(locators, function(loc) {
|
arrfor(locators, function(loc) {
|
||||||
log.console(" Installing " + loc + "...")
|
log.console(" Installing " + loc + "...")
|
||||||
var _inst = function() {
|
var _inst = function() {
|
||||||
|
|||||||
@@ -1204,23 +1204,23 @@ if (ends_with(prog, '.ce')) prog = text(prog, 0, -3)
|
|||||||
|
|
||||||
var package = use_core('package')
|
var package = use_core('package')
|
||||||
|
|
||||||
// Find the .ce file
|
// Find the .ce file using unified resolver
|
||||||
var prog_path = prog + ".ce"
|
var cwd_package = package.find_package_dir(".")
|
||||||
var pkg_dir = null
|
var prog_info = shop.resolve_program ? shop.resolve_program(prog, cwd_package) : null
|
||||||
var core_dir = null
|
var prog_path = null
|
||||||
if (!fd.is_file(prog_path)) {
|
if (prog_info) {
|
||||||
pkg_dir = package.find_package_dir(".")
|
prog_path = prog_info.path
|
||||||
if (pkg_dir)
|
} else {
|
||||||
prog_path = pkg_dir + '/' + prog + '.ce'
|
// Fallback: check CWD, package dir, and core
|
||||||
}
|
prog_path = prog + ".ce"
|
||||||
if (!fd.is_file(prog_path)) {
|
if (!fd.is_file(prog_path) && cwd_package)
|
||||||
// Check core packages
|
prog_path = cwd_package + '/' + prog + '.ce'
|
||||||
core_dir = core_path
|
if (!fd.is_file(prog_path))
|
||||||
prog_path = core_dir + '/' + prog + '.ce'
|
prog_path = core_path + '/' + prog + '.ce'
|
||||||
}
|
if (!fd.is_file(prog_path)) {
|
||||||
if (!fd.is_file(prog_path)) {
|
os.print(`Main program ${prog} could not be found\n`)
|
||||||
os.print(`Main program ${prog} could not be found\n`)
|
os.exit(1)
|
||||||
os.exit(1)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$_.clock(_ => {
|
$_.clock(_ => {
|
||||||
@@ -1244,18 +1244,33 @@ $_.clock(_ => {
|
|||||||
|
|
||||||
var pkg = file_info ? file_info.package : null
|
var pkg = file_info ? file_info.package : null
|
||||||
|
|
||||||
// Verify all transitive dependency packages are present
|
// Verify all transitive dependency packages are present, auto-install if missing
|
||||||
var _deps = null
|
var _deps = null
|
||||||
var _di = 0
|
var _di = 0
|
||||||
var _dep_dir = null
|
var _dep_dir = null
|
||||||
|
var _auto_install = null
|
||||||
if (pkg) {
|
if (pkg) {
|
||||||
_deps = package.gather_dependencies(pkg)
|
_deps = package.gather_dependencies(pkg)
|
||||||
_di = 0
|
_di = 0
|
||||||
while (_di < length(_deps)) {
|
while (_di < length(_deps)) {
|
||||||
_dep_dir = package.get_dir(_deps[_di])
|
_dep_dir = package.get_dir(_deps[_di])
|
||||||
if (!fd.is_dir(_dep_dir)) {
|
if (!fd.is_dir(_dep_dir)) {
|
||||||
log.error('missing dependency package: ' + _deps[_di])
|
log.console('installing missing dependency: ' + _deps[_di])
|
||||||
disrupt
|
_auto_install = function() {
|
||||||
|
shop.update(_deps[_di])
|
||||||
|
shop.fetch(_deps[_di])
|
||||||
|
shop.extract(_deps[_di])
|
||||||
|
shop.build_package_scripts(_deps[_di])
|
||||||
|
} disruption {
|
||||||
|
log.error('failed to install dependency: ' + _deps[_di])
|
||||||
|
disrupt
|
||||||
|
}
|
||||||
|
_auto_install()
|
||||||
|
_dep_dir = package.get_dir(_deps[_di])
|
||||||
|
if (!fd.is_dir(_dep_dir)) {
|
||||||
|
log.error('missing dependency package: ' + _deps[_di])
|
||||||
|
disrupt
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_di = _di + 1
|
_di = _di + 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -318,7 +318,9 @@ JSC_SCALL(os_system,
|
|||||||
)
|
)
|
||||||
|
|
||||||
JSC_CCALL(os_exit,
|
JSC_CCALL(os_exit,
|
||||||
exit(0);
|
int code = 0;
|
||||||
|
if (argc > 0) JS_ToInt32(js, &code, argv[0]);
|
||||||
|
exit(code);
|
||||||
)
|
)
|
||||||
|
|
||||||
static JSValue js_os_dylib_open(JSContext *js, JSValue self, int argc, JSValue *argv)
|
static JSValue js_os_dylib_open(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||||
@@ -714,7 +716,7 @@ static const JSCFunctionListEntry js_os_funcs[] = {
|
|||||||
MIST_FUNC_DEF(os, rusage, 0),
|
MIST_FUNC_DEF(os, rusage, 0),
|
||||||
MIST_FUNC_DEF(os, mallinfo, 0),
|
MIST_FUNC_DEF(os, mallinfo, 0),
|
||||||
MIST_FUNC_DEF(os, system, 1),
|
MIST_FUNC_DEF(os, system, 1),
|
||||||
MIST_FUNC_DEF(os, exit, 0),
|
MIST_FUNC_DEF(os, exit, 1),
|
||||||
MIST_FUNC_DEF(os, sleep, 1),
|
MIST_FUNC_DEF(os, sleep, 1),
|
||||||
MIST_FUNC_DEF(os, dylib_open, 1),
|
MIST_FUNC_DEF(os, dylib_open, 1),
|
||||||
MIST_FUNC_DEF(os, dylib_preload, 1),
|
MIST_FUNC_DEF(os, dylib_preload, 1),
|
||||||
|
|||||||
@@ -778,6 +778,11 @@ function resolve_path(path, ctx)
|
|||||||
var ctx_path = null
|
var ctx_path = null
|
||||||
var alias = null
|
var alias = null
|
||||||
var package_path = null
|
var package_path = null
|
||||||
|
var lock = null
|
||||||
|
var best_pkg = null
|
||||||
|
var best_remainder = null
|
||||||
|
var shop_dir = null
|
||||||
|
var shop_file = null
|
||||||
|
|
||||||
if (explicit) {
|
if (explicit) {
|
||||||
if (is_internal_path(explicit.path) && ctx && explicit.package != ctx)
|
if (is_internal_path(explicit.path) && ctx && explicit.package != ctx)
|
||||||
@@ -823,6 +828,25 @@ function resolve_path(path, ctx)
|
|||||||
if (fd.is_file(package_path))
|
if (fd.is_file(package_path))
|
||||||
return {path: package_path, scope: SCOPE_PACKAGE, pkg: ctx}
|
return {path: package_path, scope: SCOPE_PACKAGE, pkg: ctx}
|
||||||
|
|
||||||
|
// Shop package scanning: longest prefix match against lock.toml entries
|
||||||
|
lock = Shop.load_lock()
|
||||||
|
best_pkg = null
|
||||||
|
best_remainder = null
|
||||||
|
arrfor(array(lock), function(pkg_name) {
|
||||||
|
if (starts_with(path, pkg_name + '/')) {
|
||||||
|
if (!best_pkg || length(pkg_name) > length(best_pkg)) {
|
||||||
|
best_pkg = pkg_name
|
||||||
|
best_remainder = text(path, length(pkg_name) + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (best_pkg && best_remainder) {
|
||||||
|
shop_dir = get_packages_dir() + '/' + safe_package_path(best_pkg)
|
||||||
|
shop_file = shop_dir + '/' + best_remainder
|
||||||
|
if (fd.is_file(shop_file))
|
||||||
|
return {path: shop_file, scope: SCOPE_PACKAGE, pkg: best_pkg}
|
||||||
|
}
|
||||||
|
|
||||||
core_dir = Shop.get_core_dir()
|
core_dir = Shop.get_core_dir()
|
||||||
core_file_path = core_dir + '/' + path
|
core_file_path = core_dir + '/' + path
|
||||||
if (fd.is_file(core_file_path))
|
if (fd.is_file(core_file_path))
|
||||||
@@ -1298,6 +1322,64 @@ Shop.resolve_use_path = function(path, ctx) {
|
|||||||
return info.path
|
return info.path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve a program (.ce) path using the unified resolver.
|
||||||
|
// Returns {path, scope, pkg} or null.
|
||||||
|
// If the path looks like a remote package locator and is not found locally,
|
||||||
|
// attempts to auto-fetch and install it.
|
||||||
|
Shop.resolve_program = function(prog, package_context) {
|
||||||
|
var info = resolve_path(prog + '.ce', package_context)
|
||||||
|
if (info) return info
|
||||||
|
|
||||||
|
// Auto-install: if the path matches a recognized remote locator, try fetching
|
||||||
|
var lock = Shop.load_lock()
|
||||||
|
var best_pkg = null
|
||||||
|
var best_remainder = null
|
||||||
|
var pkg_info = null
|
||||||
|
var parts = array(prog, '/')
|
||||||
|
var i = 0
|
||||||
|
var candidate = null
|
||||||
|
var _auto = null
|
||||||
|
arrfor(array(lock), function(pkg_name) {
|
||||||
|
if (starts_with(prog, pkg_name + '/')) {
|
||||||
|
if (!best_pkg || length(pkg_name) > length(best_pkg)) {
|
||||||
|
best_pkg = pkg_name
|
||||||
|
best_remainder = text(prog, length(pkg_name) + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// If not in lock, check if this looks like a fetchable package
|
||||||
|
if (!best_pkg) {
|
||||||
|
for (i = length(parts) - 1; i >= 1; i--) {
|
||||||
|
candidate = text(array(parts, 0, i), '/')
|
||||||
|
pkg_info = Shop.resolve_package_info(candidate)
|
||||||
|
if (pkg_info && pkg_info != 'local') {
|
||||||
|
best_pkg = candidate
|
||||||
|
best_remainder = text(array(parts, i), '/')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (best_pkg && best_remainder) {
|
||||||
|
log.console('fetching ' + best_pkg + '...')
|
||||||
|
_auto = function() {
|
||||||
|
Shop.update(best_pkg)
|
||||||
|
Shop.fetch(best_pkg)
|
||||||
|
Shop.extract(best_pkg)
|
||||||
|
Shop.build_package_scripts(best_pkg)
|
||||||
|
} disruption {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
_auto()
|
||||||
|
// Retry resolution
|
||||||
|
info = resolve_path(prog + '.ce', package_context)
|
||||||
|
if (info) return info
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
// Resolve a use() module path to {resolved_path, package, type} without compiling.
|
// Resolve a use() module path to {resolved_path, package, type} without compiling.
|
||||||
// type is 'script', 'native', or null. Checks .cm files, C symbols, and aliases.
|
// type is 'script', 'native', or null. Checks .cm files, C symbols, and aliases.
|
||||||
Shop.resolve_import_info = function(path, ctx) {
|
Shop.resolve_import_info = function(path, ctx) {
|
||||||
@@ -1410,7 +1492,7 @@ Shop.fetch = function(pkg) {
|
|||||||
if (actual_hash == expected_hash) {
|
if (actual_hash == expected_hash) {
|
||||||
return { status: 'cached' }
|
return { status: 'cached' }
|
||||||
}
|
}
|
||||||
log.console("Zip hash mismatch for " + pkg + ", re-fetching...")
|
log.shop("Zip hash mismatch for " + pkg + ", re-fetching...")
|
||||||
} else {
|
} else {
|
||||||
// No hash stored yet - compute and store it
|
// No hash stored yet - compute and store it
|
||||||
actual_hash = text(crypto.blake2(zip_blob), 'h')
|
actual_hash = text(crypto.blake2(zip_blob), 'h')
|
||||||
@@ -1522,13 +1604,13 @@ Shop.update = function(pkg) {
|
|||||||
var lock_entry = lock[pkg]
|
var lock_entry = lock[pkg]
|
||||||
var info = Shop.resolve_package_info(pkg)
|
var info = Shop.resolve_package_info(pkg)
|
||||||
|
|
||||||
log.console(`checking ${pkg}`)
|
log.shop(`checking ${pkg}`)
|
||||||
|
|
||||||
var new_entry = null
|
var new_entry = null
|
||||||
if (info == 'local') {
|
if (info == 'local') {
|
||||||
// Check if local path exists
|
// Check if local path exists
|
||||||
if (!fd.is_dir(pkg)) {
|
if (!fd.is_dir(pkg)) {
|
||||||
log.console(` Local path does not exist: ${pkg}`)
|
log.shop(` Local path does not exist: ${pkg}`)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
// Local packages always get a lock entry
|
// Local packages always get a lock entry
|
||||||
@@ -1544,8 +1626,8 @@ Shop.update = function(pkg) {
|
|||||||
var local_commit = lock_entry ? lock_entry.commit : null
|
var local_commit = lock_entry ? lock_entry.commit : null
|
||||||
var remote_commit = fetch_remote_hash(pkg)
|
var remote_commit = fetch_remote_hash(pkg)
|
||||||
|
|
||||||
log.console(`local commit: ${local_commit}`)
|
log.shop(`local commit: ${local_commit}`)
|
||||||
log.console(`remote commit: ${remote_commit}`)
|
log.shop(`remote commit: ${remote_commit}`)
|
||||||
|
|
||||||
if (!remote_commit) {
|
if (!remote_commit) {
|
||||||
log.error("Could not resolve commit for " + pkg)
|
log.error("Could not resolve commit for " + pkg)
|
||||||
@@ -1574,7 +1656,7 @@ function install_zip(zip_blob, target_dir) {
|
|||||||
if (fd.is_link(target_dir)) fd.unlink(target_dir)
|
if (fd.is_link(target_dir)) fd.unlink(target_dir)
|
||||||
if (fd.is_dir(target_dir)) fd.rmdir(target_dir, 1)
|
if (fd.is_dir(target_dir)) fd.rmdir(target_dir, 1)
|
||||||
|
|
||||||
log.console("Extracting to " + target_dir)
|
log.shop("Extracting to " + target_dir)
|
||||||
ensure_dir(target_dir)
|
ensure_dir(target_dir)
|
||||||
|
|
||||||
var count = zip.count()
|
var count = zip.count()
|
||||||
|
|||||||
22
package.cm
22
package.cm
@@ -202,6 +202,28 @@ package.gather_dependencies = function(name)
|
|||||||
return array(all_deps)
|
return array(all_deps)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Recursively find all cell packages (dirs with cell.toml) under a directory
|
||||||
|
package.find_packages = function(dir) {
|
||||||
|
var found = []
|
||||||
|
var list = fd.readdir(dir)
|
||||||
|
if (!list) return found
|
||||||
|
if (fd.is_file(dir + '/cell.toml'))
|
||||||
|
push(found, dir)
|
||||||
|
arrfor(list, function(item) {
|
||||||
|
if (item == '.' || item == '..' || item == '.cell' || item == '.git') return
|
||||||
|
var full = dir + '/' + item
|
||||||
|
var st = fd.stat(full)
|
||||||
|
var sub = null
|
||||||
|
if (st && st.isDirectory) {
|
||||||
|
sub = package.find_packages(full)
|
||||||
|
arrfor(sub, function(p) {
|
||||||
|
push(found, p)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
package.list_files = function(pkg) {
|
package.list_files = function(pkg) {
|
||||||
var dir = get_path(pkg)
|
var dir = get_path(pkg)
|
||||||
if (!fd.is_dir(dir)) return []
|
if (!fd.is_dir(dir)) return []
|
||||||
|
|||||||
@@ -8057,6 +8057,11 @@ static JSValue js_cell_text_format (JSContext *ctx, JSValue this_val, int argc,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!made_substitution && JS_IsNull (cv_ref.val)) {
|
||||||
|
substitution = JS_NewString (ctx, "null");
|
||||||
|
made_substitution = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!made_substitution && !JS_IsNull (cv_ref.val)) {
|
if (!made_substitution && !JS_IsNull (cv_ref.val)) {
|
||||||
JSValue conv_text_val = JS_ToString (ctx, cv_ref.val);
|
JSValue conv_text_val = JS_ToString (ctx, cv_ref.val);
|
||||||
if (JS_IsText (conv_text_val)) {
|
if (JS_IsText (conv_text_val)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user