// cell verify [] - Verify integrity and consistency // // Usage: // cell verify Verify current directory package // cell verify . Verify current directory package // cell verify Verify specific package // cell verify shop Verify entire shop // cell verify world Verify all world roots // // Options: // --deep Traverse full dependency closure // --target Verify builds for specific target var shop = use('internal/shop') var pkg = use('package') var link = use('link') var build = use('build') var fd = use('fd') var scope = null var deep = false var target_triple = null var i = 0 for (i = 0; i < length(args); i++) { if (args[i] == '--deep') { deep = true } else if (args[i] == '--target' || args[i] == '-t') { if (i + 1 < length(args)) { target_triple = args[++i] } else { log.error('--target requires a triple') $stop() } } else if (args[i] == '--help' || args[i] == '-h') { log.console("Usage: cell verify [] [options]") log.console("") log.console("Verify integrity and consistency.") log.console("") log.console("Scopes:") log.console(" Verify specific package") log.console(" shop Verify entire shop") log.console(" world Verify all world roots") log.console("") log.console("Options:") log.console(" --deep Traverse full dependency closure") log.console(" --target Verify builds for specific target") $stop() } else if (!starts_with(args[i], '-')) { scope = args[i] } } // Default to current directory if (!scope) { scope = '.' } // Detect target if not specified if (!target_triple) { target_triple = build.detect_host_target() } var errors = [] var warnings = [] var checked = 0 function add_error(msg) { push(errors, msg) } function add_warning(msg) { push(warnings, msg) } // Verify a single package function verify_package(locator) { var lock = null var lock_entry = null var links = null var link_target = null var pkg_dir = null var dir_exists = false var current_target = null var expected_target = null var target_dir = null var c_files = null checked++ lock = shop.load_lock() lock_entry = lock[locator] links = link.load() link_target = links[locator] // Check lock entry exists if (!lock_entry) { add_error(locator + ": not in lock") } // Check package directory exists pkg_dir = shop.get_package_dir(locator) dir_exists = fd.is_dir(pkg_dir) || fd.is_link(pkg_dir) if (!dir_exists) { add_error(locator + ": package directory missing at " + pkg_dir) return } // Check cell.toml exists if (!fd.is_file(pkg_dir + '/cell.toml')) { add_error(locator + ": missing cell.toml") return } // For linked packages, verify link target if (link_target) { if (starts_with(link_target, '/')) { // Local path target if (!fd.is_dir(link_target)) { add_error(locator + ": link target does not exist: " + link_target) } else if (!fd.is_file(link_target + '/cell.toml')) { add_error(locator + ": link target is not a valid package: " + link_target) } } else { // Package target target_dir = shop.get_package_dir(link_target) if (!fd.is_dir(target_dir) && !fd.is_link(target_dir)) { add_error(locator + ": link target package not found: " + link_target) } } // Check symlink is correct if (fd.is_link(pkg_dir)) { current_target = fd.readlink(pkg_dir) expected_target = starts_with(link_target, '/') ? link_target : shop.get_package_dir(link_target) if (current_target != expected_target) { add_warning(locator + ": symlink target mismatch (expected " + expected_target + ", got " + current_target + ")") } } else { add_warning(locator + ": linked but directory is not a symlink") } } // Check if package has C files (builds happen lazily on first use) var _check_build = function() { c_files = pkg.get_c_files(locator, target_triple, true) } disruption { // Skip build check if can't determine C files } _check_build() } // Check for link cycles function check_link_cycles() { var links = link.load() function follow_chain(origin, visited) { var target = null if (visited[origin]) { return origin // cycle detected } visited[origin] = true target = links[origin] if (target && links[target]) { return follow_chain(target, visited) } return null } arrfor(array(links), function(origin) { var cycle_start = follow_chain(origin, {}) if (cycle_start) { add_error("Link cycle detected starting from: " + origin) } }) } // Check for dangling links function check_dangling_links() { var links = link.load() arrfor(array(links), function(origin) { var target = links[origin] if (starts_with(target, '/')) { if (!fd.is_dir(target)) { add_error("Dangling link: " + origin + " -> " + target + " (target does not exist)") } } }) } // Gather packages to verify var packages_to_verify = [] var locator = null var all_deps = null if (scope == 'shop') { packages_to_verify = shop.list_packages() } else if (scope == 'world') { // For now, world is the same as shop packages_to_verify = shop.list_packages() } else { // Single package locator = scope locator = shop.resolve_locator(locator) if (deep) { // Gather all dependencies all_deps = pkg.gather_dependencies(locator) push(packages_to_verify, locator) arrfor(all_deps, function(dep) { push(packages_to_verify, dep) }) } else { push(packages_to_verify, locator) } } log.console("Verifying " + text(length(packages_to_verify)) + " package(s)...") log.console("") // Run verification check_link_cycles() check_dangling_links() arrfor(packages_to_verify, function(p) { if (p == 'core') return verify_package(p) }) // Print results if (length(warnings) > 0) { log.console("Warnings:") arrfor(warnings, function(w) { log.console(" " + w) }) log.console("") } if (length(errors) > 0) { log.console("Errors:") arrfor(errors, function(e) { log.console(" " + e) }) log.console("") log.console("Verification FAILED: " + text(length(errors)) + " error(s), " + text(length(warnings)) + " warning(s)") } else { log.console("Verification PASSED: " + text(checked) + " package(s) checked, " + text(length(warnings)) + " warning(s)") } $stop()