diff --git a/imports.ce b/imports.ce new file mode 100644 index 00000000..e76b75a4 --- /dev/null +++ b/imports.ce @@ -0,0 +1,189 @@ +// cell imports [] - Trace module-level imports +// +// Usage: +// cell imports Trace imports for current package entry +// cell imports Trace imports starting from a .ce or .cm file +// cell imports Trace imports starting from a .cm file +// +// Options: +// --flat Flat list instead of tree +// --packages Only show unique packages, not individual modules + +var shop = use('internal/shop') +var pkg = use('package') +var fd = use('fd') + +var target = null +var flat_mode = false +var packages_only = false +var i = 0 +var pkg_dir = null +var config = null +var target_path = null + +for (i = 0; i < length(args); i++) { + if (args[i] == '--flat') { + flat_mode = true + } else if (args[i] == '--packages') { + packages_only = true + } else if (args[i] == '--help' || args[i] == '-h') { + log.console("Usage: cell imports [] [options]") + log.console("") + log.console("Trace module-level imports across packages.") + log.console("") + log.console("Options:") + log.console(" --flat Flat list instead of tree") + log.console(" --packages Only show unique packages, not individual modules") + $stop() + } else if (!starts_with(args[i], '-')) { + target = args[i] + } +} + +// Resolve target file +if (target) { + if (!ends_with(target, '.ce') && !ends_with(target, '.cm')) + target = target + '.ce' + if (fd.is_file(target)) + target_path = fd.realpath(target) + else { + pkg_dir = pkg.find_package_dir('.') + if (pkg_dir) + target_path = pkg_dir + '/' + target + } +} else { + pkg_dir = pkg.find_package_dir('.') + if (pkg_dir) { + config = pkg.load_config(null) + if (config.entry) + target_path = pkg_dir + '/' + config.entry + } +} + +if (!target_path || !fd.is_file(target_path)) { + log.error('Could not find file: ' + (target || '(no target specified)')) + $stop() +} + +// Collect all imports recursively +var visited = {} +var all_imports = [] +var all_packages = {} + +function trace_imports(file_path, depth) { + if (visited[file_path]) return + visited[file_path] = true + + var fi = shop.file_info(file_path) + var file_pkg = fi.package || '(local)' + var idx = null + var j = 0 + var imp = null + var mod_path = null + var resolved = null + var imp_pkg = null + var imp_type = null + var rinfo = null + + all_packages[file_pkg] = true + + var _trace = function() { + idx = shop.index_file(file_path) + if (!idx || !idx.imports) return + + j = 0 + while (j < length(idx.imports)) { + imp = idx.imports[j] + mod_path = imp.module_path + resolved = null + imp_pkg = '?' + imp_type = 'unresolved' + + // Use the full resolver that checks .cm files, C symbols, and aliases + rinfo = shop.resolve_import_info(mod_path, file_pkg) + if (rinfo) { + resolved = rinfo.resolved_path + imp_pkg = rinfo.package || '?' + imp_type = rinfo.type + } + + all_packages[imp_pkg] = true + + push(all_imports, { + from: file_path, + from_pkg: file_pkg, + module_path: mod_path, + resolved_path: resolved, + package: imp_pkg, + type: imp_type, + depth: depth + }) + + // Recurse into resolved scripts + if (resolved && (ends_with(resolved, '.cm') || ends_with(resolved, '.ce'))) { + trace_imports(resolved, depth + 1) + } + j = j + 1 + } + } disruption { + // File might fail to parse/index + } + _trace() +} + +trace_imports(target_path, 0) + +// Output results +var fi2 = null +if (packages_only) { + log.console("Packages used by " + target_path + ":") + log.console("") + arrfor(array(all_packages), function(p) { + log.console(" " + p) + }) +} else if (flat_mode) { + log.console("Imports from " + target_path + ":") + log.console("") + arrfor(all_imports, function(imp) { + var suffix = '' + if (imp.type == 'native') suffix = ' [native]' + if (imp.type == 'unresolved') suffix = ' [unresolved]' + log.console(" " + imp.module_path + " -> " + imp.package + suffix) + }) +} else { + // Tree output + function print_tree(file_path, prefix, is_last) { + var children = filter(all_imports, function(imp) { + return imp.from == file_path + }) + + var j = 0 + var imp = null + var last = false + var connector = null + var suffix = null + var child_prefix = null + while (j < length(children)) { + imp = children[j] + last = (j == length(children) - 1) + connector = last ? "\\-- " : "|-- " + suffix = " (" + imp.package + ")" + if (imp.type == 'native') suffix = suffix + " [native]" + if (imp.type == 'unresolved') suffix = suffix + " [unresolved]" + + log.console(prefix + connector + imp.module_path + suffix) + + if (imp.resolved_path) { + child_prefix = prefix + (last ? " " : "| ") + print_tree(imp.resolved_path, child_prefix, last) + } + j = j + 1 + } + } + + fi2 = shop.file_info(target_path) + log.console(target_path + " (" + (fi2.package || 'local') + ")") + print_tree(target_path, "", true) +} + +$stop() diff --git a/internal/engine.cm b/internal/engine.cm index 3f82a84e..8f6c3217 100644 --- a/internal/engine.cm +++ b/internal/engine.cm @@ -420,7 +420,7 @@ core_extras.native_mode = native_mode // NOW load shop -- it receives all of the above via env var shop = use_core('internal/shop') -if (native_mode) use_core('build') +use_core('build') var time = use_core('time') var toml = use_core('toml') @@ -498,10 +498,8 @@ function pretty_format(rec) { var out = null var i = 0 var fr = null - if (rec.source && rec.source.file) - src = rec.source.file + ":" + text(rec.source.line) ev = is_text(rec.event) ? rec.event : json.encode(rec.event, false) - out = `[${aid}] [${rec.channel}] ${src} ${ev}\n` + out = `[${aid}] [${rec.channel}] ${ev}\n` if (rec.stack && length(rec.stack) > 0) { for (i = 0; i < length(rec.stack); i = i + 1) { fr = rec.stack[i] @@ -1248,6 +1246,20 @@ $_.clock(_ => { } var pkg = file_info ? file_info.package : null + + // Pre-build C modules for all transitive dependencies + var _deps = null + var _di = 0 + if (pkg && shop.ensure_package_dylibs) { + _deps = package.gather_dependencies(pkg) + _di = 0 + while (_di < length(_deps)) { + shop.ensure_package_dylibs(_deps[_di]) + _di = _di + 1 + } + shop.ensure_package_dylibs(pkg) + } + env.use = function(path) { var ck = 'core/' + path if (use_cache[ck]) return use_cache[ck] diff --git a/internal/shop.cm b/internal/shop.cm index a47c30d4..2d549f85 100644 --- a/internal/shop.cm +++ b/internal/shop.cm @@ -1220,7 +1220,7 @@ function execute_module(info) // C only used = call_c_module(c_resolve) } else { - print(`Module ${info.path} could not be found`); disrupt + log.shop(`Module could not be found (c_resolve scope=${info.c_resolve.scope}, mod_resolve scope=${info.mod_resolve.scope}, cache_key=${info.cache_key})`); disrupt } if (!used) { print(`Module ${info} returned null`); disrupt } @@ -1230,8 +1230,21 @@ function execute_module(info) function get_module(path, package_context) { var info = resolve_module_info(path, package_context) + var _ctx_dir = null + var _alias = null - if (!info) { print(`Module ${path} could not be found in ${package_context}`); disrupt } + if (!info) { + log.shop(`Module '${path}' could not be found in package '${package_context}'`) + _ctx_dir = package_context ? (starts_with(package_context, '/') ? package_context : get_packages_dir() + '/' + safe_package_path(package_context)) : null + if (_ctx_dir) { + if (fd.is_file(_ctx_dir + '/' + path + '.c') || fd.is_file(_ctx_dir + '/' + path + '.cpp')) + log.shop(`C source exists at ${_ctx_dir}/${path}.c but was not compiled - run 'cell build'`) + } + _alias = pkg_tools.split_alias(package_context, path) + if (_alias == null && search(path, '/') != null) + log.shop(`Alias '${array(path, '/')[0]}' could not be resolved in package '${package_context}'`) + disrupt + } return execute_module(info) } @@ -1254,7 +1267,20 @@ Shop.use = function use(path, package_context) { } var info = resolve_module_info(path, package_context) - if (!info) { print(`Module ${path} could not be found in ${package_context}`); disrupt } + var _ctx_dir2 = null + var _alias2 = null + if (!info) { + log.shop(`Module '${path}' could not be found in package '${package_context}'`) + _ctx_dir2 = package_context ? (starts_with(package_context, '/') ? package_context : get_packages_dir() + '/' + safe_package_path(package_context)) : null + if (_ctx_dir2) { + if (fd.is_file(_ctx_dir2 + '/' + path + '.c') || fd.is_file(_ctx_dir2 + '/' + path + '.cpp')) + log.shop(`C source exists at ${_ctx_dir2}/${path}.c but was not compiled - run 'cell build'`) + } + _alias2 = pkg_tools.split_alias(package_context, path) + if (_alias2 == null && search(path, '/') != null) + log.shop(`Alias '${array(path, '/')[0]}' could not be resolved in package '${package_context}'`) + disrupt + } if (use_cache[info.cache_key]) return use_cache[info.cache_key] @@ -1272,6 +1298,28 @@ Shop.resolve_use_path = function(path, ctx) { return info.path } +// 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. +Shop.resolve_import_info = function(path, ctx) { + var mod_info = resolve_path(path + '.cm', ctx) + var c_info = null + var c_pkg = null + if (mod_info) + return {resolved_path: mod_info.path, package: mod_info.pkg, type: 'script'} + + c_info = resolve_c_symbol(path, ctx) + if (c_info && c_info.scope < 900) { + c_pkg = c_info.package + if (!c_pkg) { + if (c_info.scope == SCOPE_CORE) c_pkg = 'core' + else c_pkg = ctx + } + return {resolved_path: null, package: c_pkg, type: 'native'} + } + + return null +} + // Get cache path for a package and commit function get_cache_path(pkg, commit) { return global_shop_path + '/cache/' + replace(replace(pkg, '@','_'), '/','_') + '_' + commit + '.zip' @@ -1710,6 +1758,7 @@ Shop.get_lib_dir = function() { } Shop.ensure_dir = ensure_dir +Shop.ensure_package_dylibs = ensure_package_dylibs Shop.get_local_dir = function() { return global_shop_path + "/local" diff --git a/package.cm b/package.cm index 61beb139..506006c4 100644 --- a/package.cm +++ b/package.cm @@ -205,7 +205,7 @@ package.gather_dependencies = function(name) package.list_files = function(pkg) { var dir = get_path(pkg) if (!fd.is_dir(dir)) return [] - return fd.globfs(["*", "!.*"], dir) + return fd.globfs(["**/*", "!.*"], dir) } package.list_modules = function(name) {