From 4ac92c8a87ab12aa9f12c0e9da8e90550c171c4a Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Fri, 20 Feb 2026 14:44:48 -0600 Subject: [PATCH] reduce dups --- add.ce | 92 +++++--------------- audit.ce | 9 +- build.ce | 15 +--- clean.ce | 8 +- clone.ce | 23 +---- fetch.ce | 100 +++++---------------- graph.ce | 10 +-- install.ce | 210 +++++++------------------------------------- internal/engine.cm | 10 +-- internal/shop.cm | 212 ++++++++++++++++++++++----------------------- link.ce | 21 +---- list.ce | 10 +-- remove.ce | 9 +- resolve.ce | 8 +- update.ce | 121 ++++++++------------------ verify.ce | 9 +- 16 files changed, 232 insertions(+), 635 deletions(-) diff --git a/add.ce b/add.ce index 3749242c..ba2ce584 100644 --- a/add.ce +++ b/add.ce @@ -9,20 +9,18 @@ var shop = use('internal/shop') var pkg = use('package') -var build = use('build') var fd = use('fd') var locator = null var alias = null -var resolved = null -var parts = null -var cwd = null -var build_target = null var recursive = false +var cwd = fd.realpath('.') +var parts = null var locators = null var added = 0 var failed = 0 -var summary = null +var _add_dep = null +var _install = null array(args, function(arg) { if (arg == '--help' || arg == '-h') { @@ -52,89 +50,65 @@ if (!locator && !recursive) { $stop() } -// Resolve relative paths to absolute paths -if (locator && (locator == '.' || starts_with(locator, './') || starts_with(locator, '../') || fd.is_dir(locator))) { - resolved = fd.realpath(locator) - if (resolved) { - locator = resolved - } -} +if (locator) + locator = shop.resolve_locator(locator) // Generate default alias from locator if (!alias && locator) { - // Use the last component of the locator as alias parts = array(locator, '/') alias = parts[length(parts) - 1] - // Remove any version suffix - if (search(alias, '@') != null) { + if (search(alias, '@') != null) alias = array(alias, '@')[0] - } } // Check we're in a package directory -cwd = fd.realpath('.') if (!fd.is_file(cwd + '/cell.toml')) { log.error("Not in a package directory (no cell.toml found)") $stop() } -// If -r flag, find all packages recursively and add each +// Recursive mode if (recursive) { - if (!locator) { - locator = '.' - } - resolved = fd.realpath(locator) - if (!resolved || !fd.is_dir(resolved)) { + if (!locator) locator = '.' + locator = shop.resolve_locator(locator) + if (!fd.is_dir(locator)) { log.error(`${locator} is not a directory`) $stop() } - locators = filter(pkg.find_packages(resolved), function(p) { + locators = filter(pkg.find_packages(locator), function(p) { return p != cwd }) if (length(locators) == 0) { - log.console("No packages found in " + resolved) + log.console("No packages found in " + locator) $stop() } - log.console(`Found ${text(length(locators))} package(s) in ${resolved}`) + log.console(`Found ${text(length(locators))} package(s) in ${locator}`) + added = 0 + failed = 0 arrfor(locators, function(loc) { - // Generate alias from directory name var loc_parts = array(loc, '/') var loc_alias = loc_parts[length(loc_parts) - 1] - log.console(" Adding " + loc + " as '" + loc_alias + "'...") var _add = function() { pkg.add_dependency(null, loc, loc_alias) - shop.get(loc) - shop.extract(loc) - shop.build_package_scripts(loc) - var _build_c = function() { - build_target = build.detect_host_target() - build.build_dynamic(loc, build_target, 'release') - } disruption { - // Not all packages have C code - } - _build_c() - added++ + shop.sync(loc) + added = added + 1 } disruption { log.console(` Warning: Failed to add ${loc}`) - failed++ + failed = failed + 1 } _add() }) - summary = "Added " + text(added) + " package(s)." - if (failed > 0) { - summary += " Failed: " + text(failed) + "." - } - log.console(summary) + log.console("Added " + text(added) + " package(s)." + (failed > 0 ? " Failed: " + text(failed) + "." : "")) $stop() } +// Single package add log.console("Adding " + locator + " as '" + alias + "'...") -// Add to local project's cell.toml -var _add_dep = function() { +_add_dep = function() { pkg.add_dependency(null, locator, alias) log.console(" Added to cell.toml") } disruption { @@ -143,26 +117,8 @@ var _add_dep = function() { } _add_dep() -// Install to shop -var _install = function() { - shop.get(locator) - shop.extract(locator) - - // Build scripts - var script_result = shop.build_package_scripts(locator) - if (length(script_result.errors) > 0) { - log.console(" Warning: " + text(length(script_result.errors)) + " script(s) failed to compile") - } - - // Build C code if any - var _build_c = function() { - build_target = build.detect_host_target() - build.build_dynamic(locator, build_target, 'release') - } disruption { - // Not all packages have C code - } - _build_c() - +_install = function() { + shop.sync_with_deps(locator) log.console(" Installed to shop") } disruption { log.error("Failed to install") diff --git a/audit.ce b/audit.ce index 8b228098..948f76db 100644 --- a/audit.ce +++ b/audit.ce @@ -10,11 +10,9 @@ var shop = use('internal/shop') var pkg = use('package') -var fd = use('fd') var target_package = null var i = 0 -var resolved = null for (i = 0; i < length(args); i++) { if (args[i] == '--help' || args[i] == '-h') { @@ -30,12 +28,7 @@ for (i = 0; i < length(args); i++) { // Resolve local paths if (target_package) { - if (target_package == '.' || starts_with(target_package, './') || starts_with(target_package, '../') || fd.is_dir(target_package)) { - resolved = fd.realpath(target_package) - if (resolved) { - target_package = resolved - } - } + target_package = shop.resolve_locator(target_package) } var packages = null diff --git a/build.ce b/build.ce index 7d3b445a..fd025aa3 100644 --- a/build.ce +++ b/build.ce @@ -22,7 +22,6 @@ var dry_run = false var i = 0 var targets = null var t = 0 -var resolved = null var lib = null var results = null var success = 0 @@ -74,15 +73,8 @@ for (i = 0; i < length(args); i++) { } } -// Resolve local paths to absolute paths -if (target_package) { - if (target_package == '.' || starts_with(target_package, './') || starts_with(target_package, '../') || fd.is_dir(target_package)) { - resolved = fd.realpath(target_package) - if (resolved) { - target_package = resolved - } - } -} +if (target_package) + target_package = shop.resolve_locator(target_package) // Detect target if not specified if (!target) { @@ -97,10 +89,9 @@ if (target && !build.has_target(target)) { } var packages = shop.list_packages() -log.console('Preparing packages...') arrfor(packages, function(package) { if (package == 'core') return - shop.extract(package) + shop.sync(package, {no_build: true}) }) var _build = null diff --git a/clean.ce b/clean.ce index 89c80bc7..9b5cceed 100644 --- a/clean.ce +++ b/clean.ce @@ -24,7 +24,6 @@ var clean_fetch = false var deep = false var dry_run = false var i = 0 -var resolved = null var deps = null for (i = 0; i < length(args); i++) { @@ -76,12 +75,7 @@ var is_shop_scope = (scope == 'shop') var is_world_scope = (scope == 'world') if (!is_shop_scope && !is_world_scope) { - if (scope == '.' || starts_with(scope, './') || starts_with(scope, '../') || fd.is_dir(scope)) { - resolved = fd.realpath(scope) - if (resolved) { - scope = resolved - } - } + scope = shop.resolve_locator(scope) } var files_to_delete = [] diff --git a/clone.ce b/clone.ce index 9f9b1afe..366e5407 100644 --- a/clone.ce +++ b/clone.ce @@ -7,10 +7,6 @@ var fd = use('fd') var http = use('http') var miniz = use('miniz') -var resolved = null -var cwd = null -var parent = null - if (length(args) < 2) { log.console("Usage: cell clone ") log.console("Clones a cell package to a local path and links it.") @@ -21,24 +17,7 @@ var origin = args[0] var target_path = args[1] // Resolve target path to absolute -if (target_path == '.' || starts_with(target_path, './') || starts_with(target_path, '../')) { - resolved = fd.realpath(target_path) - if (resolved) { - target_path = resolved - } else { - // Path doesn't exist yet, resolve relative to cwd - cwd = fd.realpath('.') - if (target_path == '.') { - target_path = cwd - } else if (starts_with(target_path, './')) { - target_path = cwd + text(target_path, 1) - } else if (starts_with(target_path, '../')) { - // Go up one directory from cwd - parent = fd.dirname(cwd) - target_path = parent + text(target_path, 2) - } - } -} +target_path = shop.resolve_locator(target_path) // Check if target already exists if (fd.is_dir(target_path)) { diff --git a/fetch.ce b/fetch.ce index cc526740..f2b5c45a 100644 --- a/fetch.ce +++ b/fetch.ce @@ -1,104 +1,46 @@ -// cell fetch - Fetch package zips from remote sources +// cell fetch - Sync packages from remote sources // -// This command ensures that the zip files on disk match what's in the lock file. -// For local packages, this is a no-op. -// For remote packages, downloads the zip if not present or hash mismatch. +// Ensures all packages are fetched, extracted, compiled, and ready to use. +// For local packages, this is a no-op (symlinks only). // // Usage: -// cell fetch - Fetch all packages -// cell fetch - Fetch a specific package +// cell fetch - Sync all packages +// cell fetch - Sync a specific package var shop = use('internal/shop') -// Parse arguments var target_pkg = null var i = 0 +var packages = null +var count = 0 for (i = 0; i < length(args); i++) { if (args[i] == '--help' || args[i] == '-h') { log.console("Usage: cell fetch [package]") - log.console("Fetch package zips from remote sources.") + log.console("Sync packages from remote sources.") log.console("") log.console("Arguments:") - log.console(" package Optional package name to fetch. If omitted, fetches all.") - log.console("") - log.console("This command ensures that the zip files on disk match what's in") - log.console("the lock file. For local packages, this is a no-op.") + log.console(" package Optional package to sync. If omitted, syncs all.") $stop() } else if (!starts_with(args[i], '-')) { target_pkg = args[i] } } -var all_packages = shop.list_packages() -var lock = shop.load_lock() -var packages_to_fetch = [] -var _update = null - if (target_pkg) { - // Fetch specific package - auto-update if not in lock - if (find(all_packages, target_pkg) == null) { - log.console("Package not in lock, updating: " + target_pkg) - _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) + target_pkg = shop.resolve_locator(target_pkg) + log.console("Syncing " + target_pkg + "...") + shop.sync(target_pkg) + log.console("Done.") } else { - // Fetch all packages - packages_to_fetch = all_packages + packages = shop.list_packages() + count = 0 + arrfor(packages, function(pkg) { + if (pkg == 'core') return + shop.sync(pkg) + count = count + 1 + }) + log.console("Synced " + text(count) + " package(s).") } -var remote_count = 0 -arrfor(packages_to_fetch, function(pkg) { - var entry = lock[pkg] - if (pkg != 'core' && (!entry || entry.type != 'local')) - remote_count++ -}, null, null) - -if (remote_count > 0) - log.console(`Fetching ${text(remote_count)} remote package(s)...`) - -var downloaded_count = 0 -var cached_count = 0 -var fail_count = 0 - -arrfor(packages_to_fetch, function(pkg) { - // Skip core (handled separately) - if (pkg == 'core') return - - var result = shop.fetch(pkg) - if (result.status == 'local') { - // Local packages are just symlinks, nothing to fetch - return - } else if (result.status == 'cached') { - cached_count++ - } else if (result.status == 'downloaded') { - log.console(" Downloaded: " + pkg) - downloaded_count++ - } else if (result.status == 'error') { - log.error(" Failed: " + pkg + (result.message ? " - " + result.message : "")) - fail_count++ - } -}, null, null) - -log.console("") -var parts = [] -if (downloaded_count > 0) push(parts, `${text(downloaded_count)} downloaded`) -if (cached_count > 0) push(parts, `${text(cached_count)} cached`) -if (fail_count > 0) push(parts, `${text(fail_count)} failed`) -if (length(parts) == 0) push(parts, "nothing to fetch") -log.console("Fetch complete: " + text(parts, ", ")) - $stop() diff --git a/graph.ce b/graph.ce index 07d24566..e8eb2cea 100644 --- a/graph.ce +++ b/graph.ce @@ -15,7 +15,6 @@ var shop = use('internal/shop') var pkg = use('package') var link = use('link') -var fd = use('fd') var json = use('json') var target_locator = null @@ -23,7 +22,6 @@ var format = 'tree' var show_locked = false var show_world = false var i = 0 -var resolved = null for (i = 0; i < length(args); i++) { if (args[i] == '--format' || args[i] == '-f') { @@ -127,13 +125,7 @@ if (show_world) { target_locator = '.' } - // Resolve local paths - if (target_locator == '.' || starts_with(target_locator, './') || starts_with(target_locator, '../') || fd.is_dir(target_locator)) { - resolved = fd.realpath(target_locator) - if (resolved) { - target_locator = resolved - } - } + target_locator = shop.resolve_locator(target_locator) push(roots, target_locator) } diff --git a/install.ce b/install.ce index c5f2c818..41deb8df 100644 --- a/install.ce +++ b/install.ce @@ -1,4 +1,4 @@ -// cell install - Install a package to the shop +// cell install - Install a package and its dependencies // // Usage: // cell install Install a package and its dependencies @@ -6,34 +6,23 @@ // // Options: // --target Build for target platform -// --refresh Refresh floating refs before locking // --dry-run Show what would be installed // -r Recursively find and install all packages in directory var shop = use('internal/shop') -var build = use('build') var pkg = use('package') var fd = use('fd') -if (length(args) < 1) { - log.console("Usage: cell install [options]") - log.console("") - log.console("Options:") - log.console(" --target Build for target platform") - log.console(" --refresh Refresh floating refs before locking") - log.console(" --dry-run Show what would be installed") - log.console(" -r Recursively find and install all packages in directory") - $stop() -} - var locator = null var target_triple = null -var refresh = false var dry_run = false var recursive = false var i = 0 -var resolved = null var locators = null +var cwd = fd.realpath('.') +var lock = null +var installed = 0 +var failed = 0 for (i = 0; i < length(args); i++) { if (args[i] == '--target' || args[i] == '-t') { @@ -43,8 +32,6 @@ for (i = 0; i < length(args); i++) { log.error('--target requires a triple') $stop() } - } else if (args[i] == '--refresh') { - refresh = true } else if (args[i] == '--dry-run') { dry_run = true } else if (args[i] == '-r') { @@ -52,11 +39,10 @@ for (i = 0; i < length(args); i++) { } else if (args[i] == '--help' || args[i] == '-h') { log.console("Usage: cell install [options]") log.console("") - log.console("Install a package and its dependencies to the shop.") + log.console("Install a package and its dependencies.") log.console("") log.console("Options:") log.console(" --target Build for target platform") - log.console(" --refresh Refresh floating refs before locking") log.console(" --dry-run Show what would be installed") log.console(" -r Recursively find and install all packages in directory") $stop() @@ -66,197 +52,65 @@ for (i = 0; i < length(args); i++) { } if (!locator && !recursive) { - log.console("Usage: cell install ") + log.console("Usage: cell install [options]") $stop() } -// Resolve relative paths to absolute paths -// Local paths like '.' or '../foo' need to be converted to absolute paths -if (locator && (locator == '.' || starts_with(locator, './') || starts_with(locator, '../') || fd.is_dir(locator))) { - resolved = fd.realpath(locator) - if (resolved) { - locator = resolved - } -} +if (locator) + locator = shop.resolve_locator(locator) -var cwd = fd.realpath('.') - -// If -r flag, find all packages recursively and install each +// Recursive mode: find all packages in directory and install each if (recursive) { - if (!locator) { - locator = '.' - } - resolved = fd.realpath(locator) - if (!resolved || !fd.is_dir(resolved)) { + if (!locator) locator = '.' + locator = shop.resolve_locator(locator) + if (!fd.is_dir(locator)) { log.error(`${locator} is not a directory`) $stop() } - locators = filter(pkg.find_packages(resolved), function(p) { + locators = filter(pkg.find_packages(locator), function(p) { return p != cwd }) if (length(locators) == 0) { - log.console("No packages found in " + resolved) + log.console("No packages found in " + locator) $stop() } - log.console(`Found ${text(length(locators))} package(s) in ${resolved}`) -} + log.console(`Found ${text(length(locators))} package(s) in ${locator}`) -// Default target -if (!target_triple) { - target_triple = build.detect_host_target() -} - -// Gather all packages that will be installed -var packages_to_install = [] -var skipped_packages = [] -var summary = null -var visited = {} - -// Recursive mode: install all found packages and exit -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)" : "")) + lock = shop.load_lock() + log.console(" " + loc + (lock[loc] ? " (already installed)" : "")) }) $stop() } + installed = 0 + failed = 0 arrfor(locators, function(loc) { log.console(" Installing " + loc + "...") var _inst = function() { - shop.update(loc) - shop.extract(loc) - shop.build_package_scripts(loc) - var _build_c = function() { - build.build_dynamic(loc, target_triple, 'release') - } disruption { - // Not all packages have C code - } - _build_c() - push(packages_to_install, loc) + shop.sync(loc, {target: target_triple}) + installed = installed + 1 } disruption { - push(skipped_packages, loc) + failed = failed + 1 log.console(` Warning: Failed to install ${loc}`) } _inst() }) - summary = "Installed " + text(length(packages_to_install)) + " package(s)." - if (length(skipped_packages) > 0) { - summary += " Failed: " + text(length(skipped_packages)) + "." - } - log.console(summary) + log.console("Installed " + text(installed) + " package(s)." + (failed > 0 ? " Failed: " + text(failed) + "." : "")) + $stop() +} + +// Single package install with dependencies +if (dry_run) { + log.console("Would install: " + locator + " (and dependencies)") $stop() } log.console("Installing " + locator + "...") - -function gather_packages(pkg_locator) { - var lock = null - var update_result = null - var deps = null - if (visited[pkg_locator]) return - visited[pkg_locator] = true - - // Check if this is a local path that doesn't exist - if (starts_with(pkg_locator, '/') && !fd.is_dir(pkg_locator)) { - push(skipped_packages, pkg_locator) - log.console(" Skipping missing local package: " + pkg_locator) - return - } - - push(packages_to_install, pkg_locator) - - // Try to read dependencies - var _gather = function() { - // For packages not yet extracted, we need to update and extract first to read deps - lock = shop.load_lock() - if (!lock[pkg_locator]) { - if (!dry_run) { - update_result = shop.update(pkg_locator) - if (update_result) { - shop.extract(pkg_locator) - } else { - // Update failed - package might not be fetchable - log.console("Warning: Could not fetch " + pkg_locator) - return - } - } - } else { - // Package is in lock, ensure it's extracted - if (!dry_run) { - shop.extract(pkg_locator) - } - } - - deps = pkg.dependencies(pkg_locator) - if (deps) { - arrfor(array(deps), function(alias) { - var dep_locator = deps[alias] - gather_packages(dep_locator) - }) - } - } disruption { - // Package might not have dependencies or cell.toml issue - if (!dry_run) { - log.console(`Warning: Could not read dependencies for ${pkg_locator}`) - } - } - _gather() -} - -// Gather all packages -gather_packages(locator) - -if (dry_run) { - log.console("Would install:") - arrfor(packages_to_install, function(p) { - var lock = shop.load_lock() - var exists = lock[p] != null - log.console(" " + p + (exists ? " (already installed)" : "")) - }) - if (length(skipped_packages) > 0) { - log.console("") - log.console("Would skip (missing local paths):") - arrfor(skipped_packages, function(p) { - log.console(" " + p) - }) - } - $stop() -} - -// Install each package -function install_package(pkg_locator) { - // Update lock entry - shop.update(pkg_locator) - - // Extract/symlink the package - shop.extract(pkg_locator) - - // Build scripts - shop.build_package_scripts(pkg_locator) - - // Build C code - var _build_c = function() { - build.build_dynamic(pkg_locator, target_triple, 'release') - } disruption { - // Not all packages have C code - } - _build_c() -} - -arrfor(packages_to_install, function(p) { - log.console(" Installing " + p + "...") - install_package(p) -}) - -summary = "Installed " + text(length(packages_to_install)) + " package(s)." -if (length(skipped_packages) > 0) { - summary += " Skipped " + text(length(skipped_packages)) + " missing local path(s)." -} -log.console(summary) +shop.sync_with_deps(locator, {refresh: true, target: target_triple}) +log.console("Done.") $stop() diff --git a/internal/engine.cm b/internal/engine.cm index 853ea881..6d839d0c 100644 --- a/internal/engine.cm +++ b/internal/engine.cm @@ -1268,20 +1268,12 @@ $_.clock(_ => { if (!fd.is_dir(_dep_dir)) { log.console('installing missing dependency: ' + _deps[_di]) _auto_install = function() { - shop.update(_deps[_di]) - shop.fetch(_deps[_di]) - shop.extract(_deps[_di]) - shop.build_package_scripts(_deps[_di]) + shop.sync(_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 } diff --git a/internal/shop.cm b/internal/shop.cm index ceea81ea..e6e3949c 100644 --- a/internal/shop.cm +++ b/internal/shop.cm @@ -270,6 +270,17 @@ function safe_package_path(pkg) return replace(pkg, '@', '_') } +// Resolve a locator string to its canonical form +// Handles '.', './', '../', and existing directory paths +Shop.resolve_locator = function(locator) { + var resolved = null + if (locator == '.' || starts_with(locator, './') || starts_with(locator, '../') || fd.is_dir(locator)) { + resolved = fd.realpath(locator) + if (resolved) return resolved + } + return locator +} + function package_cache_path(pkg) { return global_shop_path + '/cache/' + replace(replace(pkg, '/', '_'), '@', '_') @@ -297,7 +308,8 @@ Shop.load_lock = function() { // Save lock.toml configuration (to global shop) Shop.save_lock = function(lock) { var path = global_shop_path + '/lock.toml' - fd.slurpwrite(path, stone(blob(toml.encode(lock)))); + fd.slurpwrite(path, stone(blob(toml.encode(lock)))) + _lock = lock } @@ -1317,8 +1329,6 @@ Shop.use = function use(path, package_context) { return use_cache[info.cache_key] } -Shop.resolve_locator = resolve_locator - // Resolve a use() module path to a filesystem path without compiling. // Returns the absolute path string, or null if not found. Shop.resolve_use_path = function(path, ctx) { @@ -1335,14 +1345,13 @@ 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 + // Find best matching package from lock or infer from path 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 pkg_info = null var _auto = null arrfor(array(lock), function(pkg_name) { if (starts_with(prog, pkg_name + '/')) { @@ -1353,8 +1362,7 @@ Shop.resolve_program = function(prog, package_context) { } }) - // If not in lock, check if this looks like a fetchable package - // For gitea-style URLs, the package root is host/owner/repo (3 components) + // If not in lock, try gitea-style 3-component package (host/owner/repo) if (!best_pkg && length(parts) > 3) { candidate = text(array(parts, 0, 3), '/') pkg_info = Shop.resolve_package_info(candidate) @@ -1364,78 +1372,19 @@ Shop.resolve_program = function(prog, package_context) { } } - if (best_pkg && best_remainder) { - log.console('fetching ' + best_pkg + '...') - _auto = function() { - // Install the package itself first - Shop.update(best_pkg) - Shop.fetch(best_pkg) - Shop.extract(best_pkg) - // Install dependencies iteratively (each dep must be extracted before reading its deps) - var all_deps = {} - var queue = [best_pkg] - var qi = 0 - var current = null - var direct_deps = null - var dep_locator = null - var dep_dir = null - var build_mod = null - var target = null - var _build_c = null - var _read_deps = null - while (qi < length(queue)) { - current = queue[qi] - qi = qi + 1 - _read_deps = function() { - direct_deps = pkg_tools.dependencies(current) - } disruption { - direct_deps = null - } - _read_deps() - if (direct_deps) { - arrfor(array(direct_deps), function(alias) { - dep_locator = direct_deps[alias] - if (!all_deps[dep_locator]) { - all_deps[dep_locator] = true - dep_dir = pkg_tools.get_dir(dep_locator) - if (!fd.is_dir(dep_dir)) { - log.console(' installing dependency: ' + dep_locator) - Shop.update(dep_locator) - Shop.fetch(dep_locator) - Shop.extract(dep_locator) - } - push(queue, dep_locator) - } - }) - } - } - // Build scripts for all packages - Shop.build_package_scripts(best_pkg) - arrfor(array(all_deps), function(dep) { - Shop.build_package_scripts(dep) - }) - // Build C modules - build_mod = use_cache['core/build'] - if (build_mod) { - _build_c = function() { - target = build_mod.detect_host_target() - arrfor(array(all_deps), function(dep) { - build_mod.build_dynamic(dep, target, 'release') - }) - build_mod.build_dynamic(best_pkg, target, 'release') - } disruption {} - _build_c() - } - } disruption { - return null - } - _auto() - // Retry resolution - info = resolve_path(prog + '.ce', package_context) - if (info) return info - } + if (!best_pkg || !best_remainder) return null - return null + // Auto-install the package and all its dependencies + log.console('fetching ' + best_pkg + '...') + _auto = function() { + Shop.sync_with_deps(best_pkg) + } disruption { + return null + } + _auto() + + info = resolve_path(prog + '.ce', package_context) + return info } // Resolve a use() module path to {resolved_path, package, type} without compiling. @@ -1713,6 +1662,82 @@ Shop.update = function(pkg) { return new_entry } +// Sync a package: ensure it's in lock, fetched, extracted, and compiled +// opts.refresh - check remote for updates even if lock entry exists +// opts.no_build - skip C module build step +// opts.target - explicit build target (auto-detected if not provided) +// opts.buildtype - 'release'|'debug'|'minsize' (default 'release') +Shop.sync = function(pkg, opts) { + var lock = Shop.load_lock() + var info = Shop.resolve_package_info(pkg) + var build_mod = null + var target = null + var _build_c = null + + // Step 1: Ensure lock entry (update if refresh or not in lock) + if ((opts && opts.refresh) || !lock[pkg]) + Shop.update(pkg) + + // Step 2: Fetch zip (no-op for local packages) + if (info && info != 'local') + Shop.fetch(pkg) + + // Step 3: Extract to packages dir + Shop.extract(pkg) + + // Step 4: Compile scripts + Shop.build_package_scripts(pkg) + + // Step 5: Build C modules + if (!opts || !opts.no_build) { + build_mod = use_cache['core/build'] + if (build_mod) { + target = (opts && opts.target) ? opts.target : build_mod.detect_host_target() + _build_c = function() { + build_mod.build_dynamic(pkg, target, (opts && opts.buildtype) ? opts.buildtype : 'release') + } disruption { + // Not all packages have C code + } + _build_c() + } + } +} + +// Sync a package and all its dependencies (BFS) +Shop.sync_with_deps = function(pkg, opts) { + var visited = {} + var queue = [pkg] + var qi = 0 + var current = null + var deps = null + var dep_locator = null + var _read_deps = null + + while (qi < length(queue)) { + current = queue[qi] + qi = qi + 1 + if (visited[current]) continue + visited[current] = true + + Shop.sync(current, opts) + + _read_deps = function() { + deps = pkg_tools.dependencies(current) + } disruption { + deps = null + } + _read_deps() + + if (deps) { + arrfor(array(deps), function(alias) { + dep_locator = deps[alias] + if (!visited[dep_locator]) + push(queue, dep_locator) + }) + } + } +} + function install_zip(zip_blob, target_dir) { var zip = miniz.read(zip_blob) if (!zip) { print("Failed to read zip archive"); disrupt } @@ -1779,34 +1804,6 @@ Shop.remove = function(pkg) { return true } -Shop.get = function(pkg) { - var lock = Shop.load_lock() - var info = null - var commit = null - - if (!lock[pkg]) { - info = Shop.resolve_package_info(pkg) - if (!info) { - print("Invalid package: " + pkg); disrupt - } - - commit = null - if (info != 'local') { - commit = fetch_remote_hash(pkg) - if (!commit) { - print("Could not resolve commit for " + pkg); disrupt - } - } - - lock[pkg] = { - type: info, - commit: commit, - updated: time.number() - } - Shop.save_lock(lock) - } -} - // Compile a module // List all files in a package @@ -1883,6 +1880,7 @@ Shop.build_package_scripts = function(package) ok = ok + 1 } disruption { push(errors, script) + log.console(" compile error: " + package + '/' + script) } _try() }) diff --git a/link.ce b/link.ce index 2c552a5e..234c0c65 100644 --- a/link.ce +++ b/link.ce @@ -28,7 +28,6 @@ var target = null var start_idx = 0 var arg1 = null var arg2 = null -var cwd = null var toml_path = null var content = null var _restore = null @@ -123,30 +122,14 @@ if (cmd == 'list') { target = arg2 // Resolve target if it's a local path - if (target == '.' || fd.is_dir(target)) { - target = fd.realpath(target) - } else if (starts_with(target, './') || starts_with(target, '../')) { - // Relative path that doesn't exist yet - try to resolve anyway - cwd = fd.realpath('.') - if (starts_with(target, './')) { - target = cwd + text(target, 1) - } else { - // For ../ paths, let fd.realpath handle it if possible - target = fd.realpath(target) || target - } - } - // Otherwise target is a package name (e.g., github.com/prosperon) + target = shop.resolve_locator(target) } else { // One argument: assume it's a local path, infer package name from cell.toml target = arg1 // Resolve path - if (target == '.' || fd.is_dir(target)) { - target = fd.realpath(target) - } else if (starts_with(target, './') || starts_with(target, '../')) { - target = fd.realpath(target) || target - } + target = shop.resolve_locator(target) // Must be a local path with cell.toml toml_path = target + '/cell.toml' diff --git a/list.ce b/list.ce index 262f873a..283c6e82 100644 --- a/list.ce +++ b/list.ce @@ -8,11 +8,9 @@ var shop = use('internal/shop') var pkg = use('package') var link = use('link') -var fd = use('fd') var mode = 'local' var target_pkg = null -var resolved = null var i = 0 var deps = null var packages = null @@ -37,13 +35,7 @@ if (args && length(args) > 0) { mode = 'package' target_pkg = args[0] - // Resolve local paths - if (target_pkg == '.' || starts_with(target_pkg, './') || starts_with(target_pkg, '../') || fd.is_dir(target_pkg)) { - resolved = fd.realpath(target_pkg) - if (resolved) { - target_pkg = resolved - } - } + target_pkg = shop.resolve_locator(target_pkg) } } diff --git a/remove.ce b/remove.ce index 09f6d76d..a7f31e9b 100644 --- a/remove.ce +++ b/remove.ce @@ -17,7 +17,6 @@ var target_pkg = null var prune = false var dry_run = false var i = 0 -var resolved = null for (i = 0; i < length(args); i++) { if (args[i] == '--prune') { @@ -43,13 +42,7 @@ if (!target_pkg) { $stop() } -// Resolve relative paths to absolute paths -if (target_pkg == '.' || starts_with(target_pkg, './') || starts_with(target_pkg, '../') || fd.is_dir(target_pkg)) { - resolved = fd.realpath(target_pkg) - if (resolved) { - target_pkg = resolved - } -} +target_pkg = shop.resolve_locator(target_pkg) var packages_to_remove = [target_pkg] diff --git a/resolve.ce b/resolve.ce index e67dba7f..c69d787b 100644 --- a/resolve.ce +++ b/resolve.ce @@ -21,7 +21,6 @@ var target_triple = null var show_locked = false var refresh_first = false var i = 0 -var resolved = null for (i = 0; i < length(args); i++) { if (args[i] == '--target' || args[i] == '-t') { @@ -56,12 +55,7 @@ if (!target_locator) { } // Resolve local paths -if (target_locator == '.' || starts_with(target_locator, './') || starts_with(target_locator, '../') || fd.is_dir(target_locator)) { - resolved = fd.realpath(target_locator) - if (resolved) { - target_locator = resolved - } -} +target_locator = shop.resolve_locator(target_locator) // Check if it's a valid package var pkg_dir = null diff --git a/update.ce b/update.ce index 3c40e2f7..db653ac7 100644 --- a/update.ce +++ b/update.ce @@ -1,30 +1,28 @@ // cell update [] - Update packages from remote sources // // Usage: -// cell update Update all packages in shop +// cell update Update all packages // cell update . Update current directory package // cell update Update a specific package // // Options: -// --build Run build after updating -// --target Target platform for build (requires --build) +// --target Target platform for build // --follow-links Update link targets instead of origins // --git Run git pull on local packages before updating var shop = use('internal/shop') -var build = use('build') var fd = use('fd') var os = use('os') +var link = use('link') var target_pkg = null -var run_build = false var target_triple = null var follow_links = false var git_pull = false var i = 0 -var resolved = null +var updated = 0 +var packages = null -// Parse arguments for (i = 0; i < length(args); i++) { if (args[i] == '--help' || args[i] == '-h') { log.console("Usage: cell update [] [options]") @@ -32,13 +30,10 @@ for (i = 0; i < length(args); i++) { log.console("Update packages from remote sources.") log.console("") log.console("Options:") - log.console(" --build Run build after updating") - log.console(" --target Target platform for build (requires --build)") + log.console(" --target Target platform for build") log.console(" --follow-links Update link targets instead of origins") log.console(" --git Run git pull on local packages") $stop() - } else if (args[i] == '--build') { - run_build = true } else if (args[i] == '--target' || args[i] == '-t') { if (i + 1 < length(args)) { target_triple = args[++i] @@ -52,33 +47,22 @@ for (i = 0; i < length(args); i++) { git_pull = true } else if (!starts_with(args[i], '-')) { target_pkg = args[i] - // Resolve relative paths to absolute paths - if (target_pkg == '.' || starts_with(target_pkg, './') || starts_with(target_pkg, '../') || fd.is_dir(target_pkg)) { - resolved = fd.realpath(target_pkg) - if (resolved) { - target_pkg = resolved - } - } } } -// Default target if building -if (run_build && !target_triple) { - target_triple = build.detect_host_target() -} +if (target_pkg) + target_pkg = shop.resolve_locator(target_pkg) -var link = use('link') - -function update_and_fetch(pkg) { +function update_one(pkg) { + var effective_pkg = pkg + var link_target = null var lock = shop.load_lock() var old_entry = lock[pkg] var old_commit = old_entry ? old_entry.commit : null - var effective_pkg = pkg - var link_target = null + var info = shop.resolve_package_info(pkg) var new_entry = null var old_str = null - // Handle follow-links option if (follow_links) { link_target = link.get_target(pkg) if (link_target) { @@ -87,80 +71,47 @@ function update_and_fetch(pkg) { } } + // For local packages with --git, pull first + if (git_pull && info == 'local' && fd.is_dir(effective_pkg + '/.git')) { + log.console(" " + effective_pkg + " (git pull)") + os.system('git -C "' + effective_pkg + '" pull') + } + + // Check for update (sets lock entry if changed) new_entry = shop.update(effective_pkg) - if (new_entry) { - if (new_entry.commit) { - old_str = old_commit ? text(old_commit, 0, 8) : "(new)" - log.console(" " + effective_pkg + " " + old_str + " -> " + text(new_entry.commit, 0, 8)) - shop.fetch(effective_pkg) - } else { - // Local package - run git pull if requested - if (git_pull && fd.is_dir(effective_pkg + '/.git')) { - log.console(" " + effective_pkg + " (git pull)") - os.system('git -C "' + effective_pkg + '" pull') - } else { - log.console(" " + effective_pkg + " (local)") - } - } - shop.extract(effective_pkg) - shop.build_package_scripts(effective_pkg) - return effective_pkg + if (new_entry && new_entry.commit) { + old_str = old_commit ? text(old_commit, 0, 8) : "(new)" + log.console(" " + effective_pkg + " " + old_str + " -> " + text(new_entry.commit, 0, 8)) } - return null + + // Sync: fetch, extract, build + shop.sync(effective_pkg, {target: target_triple}) + + return new_entry } -var updated_packages = [] - -var updated = null -var packages = null -var pkg_count = 0 -var pkg = null if (target_pkg) { - updated = update_and_fetch(target_pkg) - if (updated) { - push(updated_packages, updated) + if (update_one(target_pkg)) { log.console("Updated " + target_pkg + ".") } else { log.console(target_pkg + " is up to date.") } } else { packages = shop.list_packages() - pkg_count = length(packages) - log.console("Checking for updates (" + text(pkg_count) + " package" + (pkg_count == 1 ? "" : "s") + ")...") + log.console("Checking for updates (" + text(length(packages)) + " packages)...") - for (i = 0; i < length(packages); i++) { - pkg = packages[i] - if (pkg == 'core') continue + arrfor(packages, function(pkg) { + if (pkg == 'core') return + if (update_one(pkg)) + updated = updated + 1 + }) - updated = update_and_fetch(pkg) - if (updated) { - push(updated_packages, updated) - } - } - - if (length(updated_packages) > 0) { - log.console("Updated " + text(length(updated_packages)) + " package" + (length(updated_packages) == 1 ? "" : "s") + ".") + if (updated > 0) { + log.console("Updated " + text(updated) + " package(s).") } else { log.console("All packages are up to date.") } } -// Run build if requested -if (run_build && length(updated_packages) > 0) { - log.console("") - log.console("Building updated packages...") - - arrfor(updated_packages, function(pkg) { - var _build = function() { - var lib = build.build_dynamic(pkg, target_triple, 'release') - if (lib) - log.console(" Built: " + lib) - } disruption { - log.error(" Failed to build " + pkg) - } - _build() - }) -} - $stop() diff --git a/verify.ce b/verify.ce index cda42bba..5e9d2e30 100644 --- a/verify.ce +++ b/verify.ce @@ -21,7 +21,6 @@ var scope = null var deep = false var target_triple = null var i = 0 -var resolved = null for (i = 0; i < length(args); i++) { if (args[i] == '--deep') { @@ -206,13 +205,7 @@ if (scope == 'shop') { // Single package locator = scope - // Resolve local paths - if (locator == '.' || starts_with(locator, './') || starts_with(locator, '../') || fd.is_dir(locator)) { - resolved = fd.realpath(locator) - if (resolved) { - locator = resolved - } - } + locator = shop.resolve_locator(locator) if (deep) { // Gather all dependencies