centralized ensure dir
This commit is contained in:
92
add.ce
92
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")
|
||||
|
||||
9
audit.ce
9
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
|
||||
|
||||
15
build.ce
15
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
|
||||
|
||||
22
build.cm
22
build.cm
@@ -118,19 +118,7 @@ function get_build_dir() {
|
||||
return shop.get_build_dir()
|
||||
}
|
||||
|
||||
function ensure_dir(path) {
|
||||
if (fd.stat(path).isDirectory) return
|
||||
var parts = array(path, '/')
|
||||
var current = starts_with(path, '/') ? '/' : ''
|
||||
var i = 0
|
||||
for (i = 0; i < length(parts); i++) {
|
||||
if (parts[i] == '') continue
|
||||
current += parts[i] + '/'
|
||||
if (!fd.stat(current).isDirectory) fd.mkdir(current)
|
||||
}
|
||||
}
|
||||
|
||||
Build.ensure_dir = ensure_dir
|
||||
Build.ensure_dir = fd.ensure_dir
|
||||
|
||||
// ============================================================================
|
||||
// Dependency scanning helpers
|
||||
@@ -267,7 +255,7 @@ Build.compile_file = function(pkg, file, target, opts) {
|
||||
var fail_path = cache_path(quick_content, SALT_FAIL)
|
||||
|
||||
var build_dir = get_build_dir()
|
||||
ensure_dir(build_dir)
|
||||
fd.ensure_dir(build_dir)
|
||||
|
||||
// Check for cached failure (skip files that previously failed to compile)
|
||||
if (fd.is_file(fail_path)) {
|
||||
@@ -429,7 +417,7 @@ Build.build_module_dylib = function(pkg, file, target, opts) {
|
||||
all_objects = array(all_objects, _extra)
|
||||
var link_content = compute_link_content(all_objects, resolved_ldflags, target_ldflags, {target: _target, cc: cc})
|
||||
var build_dir = get_build_dir()
|
||||
ensure_dir(build_dir)
|
||||
fd.ensure_dir(build_dir)
|
||||
var dylib_path = cache_path(link_content, SALT_DYLIB)
|
||||
var cmd_parts = null
|
||||
var cmd_str = null
|
||||
@@ -703,7 +691,7 @@ Build.compile_native = function(src_path, target, buildtype, pkg) {
|
||||
var src = text(fd.slurp(src_path))
|
||||
var native_key = native_cache_content(src, _target, san_flags)
|
||||
var build_dir = get_build_dir()
|
||||
ensure_dir(build_dir)
|
||||
fd.ensure_dir(build_dir)
|
||||
|
||||
var dylib_path = cache_path(native_key, SALT_NATIVE)
|
||||
if (fd.is_file(dylib_path))
|
||||
@@ -775,7 +763,7 @@ Build.compile_native_ir = function(optimized, src_path, opts) {
|
||||
var src = text(fd.slurp(src_path))
|
||||
var native_key = native_cache_content(src, _target, san_flags)
|
||||
var build_dir = get_build_dir()
|
||||
ensure_dir(build_dir)
|
||||
fd.ensure_dir(build_dir)
|
||||
|
||||
var dylib_path = cache_path(native_key, SALT_NATIVE)
|
||||
if (fd.is_file(dylib_path))
|
||||
|
||||
8
clean.ce
8
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 = []
|
||||
|
||||
67
clone.ce
67
clone.ce
@@ -5,11 +5,6 @@ var shop = use('internal/shop')
|
||||
var link = use('link')
|
||||
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 <origin> <path>")
|
||||
@@ -21,24 +16,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)) {
|
||||
@@ -73,48 +51,9 @@ if (!entry || !entry.commit) {
|
||||
var download_url = shop.get_download_url(origin, entry.commit)
|
||||
log.console("Downloading from " + download_url)
|
||||
|
||||
var zip_blob = null
|
||||
var zip = null
|
||||
var count = 0
|
||||
var i = 0
|
||||
var filename = null
|
||||
var first_slash = null
|
||||
var rel_path = null
|
||||
var full_path = null
|
||||
var dir_path = null
|
||||
|
||||
var _clone = function() {
|
||||
zip_blob = http.fetch(download_url)
|
||||
|
||||
// Extract zip to target path
|
||||
zip = miniz.read(zip_blob)
|
||||
if (!zip) {
|
||||
log.console("Error: Failed to read zip archive")
|
||||
$stop()
|
||||
}
|
||||
|
||||
// Create target directory
|
||||
fd.mkdir(target_path)
|
||||
|
||||
count = zip.count()
|
||||
for (i = 0; i < count; i++) {
|
||||
if (zip.is_directory(i)) continue
|
||||
filename = zip.get_filename(i)
|
||||
first_slash = search(filename, '/')
|
||||
if (first_slash == null) continue
|
||||
if (first_slash + 1 >= length(filename)) continue
|
||||
|
||||
rel_path = text(filename, first_slash + 1)
|
||||
full_path = target_path + '/' + rel_path
|
||||
dir_path = fd.dirname(full_path)
|
||||
|
||||
// Ensure directory exists
|
||||
if (!fd.is_dir(dir_path)) {
|
||||
fd.mkdir(dir_path)
|
||||
}
|
||||
fd.slurpwrite(full_path, zip.slurp(filename))
|
||||
}
|
||||
|
||||
var zip_blob = http.fetch(download_url)
|
||||
shop.install_zip(zip_blob, target_path)
|
||||
log.console("Extracted to " + target_path)
|
||||
|
||||
// Link the origin to the cloned path
|
||||
|
||||
49
diff.ce
49
diff.ce
@@ -8,6 +8,7 @@ var shop = use('internal/shop')
|
||||
var pkg = use('package')
|
||||
var fd = use('fd')
|
||||
var time = use('time')
|
||||
var testlib = use('internal/testlib')
|
||||
|
||||
var _args = args == null ? [] : args
|
||||
|
||||
@@ -27,10 +28,7 @@ if (length(_args) > 0) {
|
||||
target_test = _args[0]
|
||||
}
|
||||
|
||||
function is_valid_package(dir) {
|
||||
var _dir = dir == null ? '.' : dir
|
||||
return fd.is_file(_dir + '/cell.toml')
|
||||
}
|
||||
var is_valid_package = testlib.is_valid_package
|
||||
|
||||
if (!is_valid_package('.')) {
|
||||
log.console('No cell.toml found in current directory')
|
||||
@@ -63,47 +61,8 @@ function collect_tests(specific_test) {
|
||||
return test_files
|
||||
}
|
||||
|
||||
// Deep comparison of two values
|
||||
function values_equal(a, b) {
|
||||
var i = 0
|
||||
var ka = null
|
||||
var kb = null
|
||||
if (a == b) return true
|
||||
if (is_null(a) && is_null(b)) return true
|
||||
if (is_null(a) || is_null(b)) return false
|
||||
if (is_array(a) && is_array(b)) {
|
||||
if (length(a) != length(b)) return false
|
||||
i = 0
|
||||
while (i < length(a)) {
|
||||
if (!values_equal(a[i], b[i])) return false
|
||||
i = i + 1
|
||||
}
|
||||
return true
|
||||
}
|
||||
if (is_object(a) && is_object(b)) {
|
||||
ka = array(a)
|
||||
kb = array(b)
|
||||
if (length(ka) != length(kb)) return false
|
||||
i = 0
|
||||
while (i < length(ka)) {
|
||||
if (!values_equal(a[ka[i]], b[ka[i]])) return false
|
||||
i = i + 1
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function describe(val) {
|
||||
if (is_null(val)) return "null"
|
||||
if (is_text(val)) return `"${val}"`
|
||||
if (is_number(val)) return text(val)
|
||||
if (is_logical(val)) return text(val)
|
||||
if (is_function(val)) return "<function>"
|
||||
if (is_array(val)) return `[array length=${text(length(val))}]`
|
||||
if (is_object(val)) return `{record keys=${text(length(array(val)))}}`
|
||||
return "<unknown>"
|
||||
}
|
||||
var values_equal = testlib.values_equal
|
||||
var describe = testlib.describe
|
||||
|
||||
// Run a single test file through both paths
|
||||
function diff_test_file(file_path) {
|
||||
|
||||
20
fd.cm
20
fd.cm
@@ -97,4 +97,24 @@ fd.globfs = function(globs, dir) {
|
||||
return results
|
||||
}
|
||||
|
||||
fd.ensure_dir = function ensure_dir(path) {
|
||||
if (fd.is_dir(path)) return true
|
||||
var parts = array(path, '/')
|
||||
var current = starts_with(path, '/') ? '/' : ''
|
||||
var i = 0
|
||||
for (i = 0; i < length(parts); i++) {
|
||||
if (parts[i] == '') continue
|
||||
current = current + parts[i] + '/'
|
||||
if (!fd.is_dir(current))
|
||||
fd.mkdir(current)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fd.safe_package_path = function safe_package_path(pkg) {
|
||||
if (pkg && starts_with(pkg, '/'))
|
||||
return replace(replace(pkg, '/', '_'), '@', '_')
|
||||
return replace(pkg, '@', '_')
|
||||
}
|
||||
|
||||
return fd
|
||||
|
||||
100
fetch.ce
100
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 <package> - Fetch a specific package
|
||||
// cell fetch - Sync all packages
|
||||
// cell fetch <package> - 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()
|
||||
|
||||
46
fuzz.ce
46
fuzz.ce
@@ -12,6 +12,7 @@
|
||||
var fd = use('fd')
|
||||
var time = use('time')
|
||||
var json = use('json')
|
||||
var testlib = use('internal/testlib')
|
||||
|
||||
var os_ref = use('internal/os')
|
||||
var analyze = os_ref.analyze
|
||||
@@ -54,48 +55,9 @@ if (!run_ast_noopt_fn) {
|
||||
// Ensure failures directory exists
|
||||
var failures_dir = "tests/fuzz_failures"
|
||||
|
||||
function ensure_dir(path) {
|
||||
if (fd.is_dir(path)) return
|
||||
var parts = array(path, '/')
|
||||
var current = ''
|
||||
var j = 0
|
||||
while (j < length(parts)) {
|
||||
if (parts[j] != '') {
|
||||
current = current + parts[j] + '/'
|
||||
if (!fd.is_dir(current)) {
|
||||
fd.mkdir(current)
|
||||
}
|
||||
}
|
||||
j = j + 1
|
||||
}
|
||||
}
|
||||
|
||||
// Deep comparison
|
||||
function values_equal(a, b) {
|
||||
var j = 0
|
||||
if (a == b) return true
|
||||
if (is_null(a) && is_null(b)) return true
|
||||
if (is_null(a) || is_null(b)) return false
|
||||
if (is_array(a) && is_array(b)) {
|
||||
if (length(a) != length(b)) return false
|
||||
j = 0
|
||||
while (j < length(a)) {
|
||||
if (!values_equal(a[j], b[j])) return false
|
||||
j = j + 1
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function describe(val) {
|
||||
if (is_null(val)) return "null"
|
||||
if (is_text(val)) return `"${val}"`
|
||||
if (is_number(val)) return text(val)
|
||||
if (is_logical(val)) return text(val)
|
||||
if (is_function(val)) return "<function>"
|
||||
return "<other>"
|
||||
}
|
||||
var ensure_dir = fd.ensure_dir
|
||||
var values_equal = testlib.values_equal
|
||||
var describe = testlib.describe
|
||||
|
||||
// Run a single fuzz iteration
|
||||
function run_fuzz(seed_val) {
|
||||
|
||||
10
graph.ce
10
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)
|
||||
}
|
||||
|
||||
210
install.ce
210
install.ce
@@ -1,4 +1,4 @@
|
||||
// cell install <locator> - Install a package to the shop
|
||||
// cell install <locator> - Install a package and its dependencies
|
||||
//
|
||||
// Usage:
|
||||
// cell install <locator> Install a package and its dependencies
|
||||
@@ -6,34 +6,23 @@
|
||||
//
|
||||
// Options:
|
||||
// --target <triple> 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 <locator> [options]")
|
||||
log.console("")
|
||||
log.console("Options:")
|
||||
log.console(" --target <triple> 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 <locator> [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 <triple> 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 <locator>")
|
||||
log.console("Usage: cell install <locator> [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()
|
||||
|
||||
@@ -1466,20 +1466,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
|
||||
}
|
||||
|
||||
283
internal/shop.cm
283
internal/shop.cm
@@ -40,20 +40,6 @@ function put_into_cache(content, obj)
|
||||
fd.slurpwrite(path, obj)
|
||||
}
|
||||
|
||||
function ensure_dir(path) {
|
||||
if (fd.stat(path).isDirectory) return
|
||||
var parts = array(path, '/')
|
||||
var current = starts_with(path, '/') ? '/' : ''
|
||||
var i = 0
|
||||
for (i = 0; i < length(parts); i++) {
|
||||
if (parts[i] == '') continue
|
||||
current = current + parts[i] + '/'
|
||||
if (!fd.stat(current).isDirectory) {
|
||||
fd.mkdir(current)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hash_path(content, salt)
|
||||
{
|
||||
var s = salt || 'mach'
|
||||
@@ -88,11 +74,6 @@ Shop.get_core_dir = function() {
|
||||
return get_packages_dir() + '/' + core_package
|
||||
}
|
||||
|
||||
// Get the links file path (in the global shop)
|
||||
function get_links_path() {
|
||||
return global_shop_path + '/link.toml'
|
||||
}
|
||||
|
||||
// Get the reports directory (in the global shop)
|
||||
Shop.get_reports_dir = function() {
|
||||
return global_shop_path + '/reports'
|
||||
@@ -131,7 +112,7 @@ function split_explicit_package_import(path)
|
||||
mod_path = text(array(parts, i), '/')
|
||||
if (!mod_path || length(mod_path) == 0) continue
|
||||
|
||||
candidate_dir = get_packages_dir() + '/' + safe_package_path(pkg_candidate)
|
||||
candidate_dir = get_packages_dir() + '/' + fd.safe_package_path(pkg_candidate)
|
||||
if (fd.is_file(candidate_dir + '/cell.toml'))
|
||||
return {package: pkg_candidate, path: mod_path}
|
||||
|
||||
@@ -259,15 +240,15 @@ function get_canonical_package(alias, package_context) {
|
||||
return null
|
||||
}
|
||||
|
||||
// return the safe path for the package
|
||||
// guaranteed to be validated
|
||||
function safe_package_path(pkg)
|
||||
{
|
||||
// For absolute paths, replace / with _ to create a valid directory name
|
||||
// Also replace @ with _
|
||||
if (pkg && starts_with(pkg, '/'))
|
||||
return replace(replace(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)
|
||||
@@ -297,7 +278,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
|
||||
}
|
||||
|
||||
|
||||
@@ -447,7 +429,7 @@ function try_native_mod_dylib(pkg, stem) {
|
||||
var build_mod = use_cache['core/build']
|
||||
if (!build_mod) return null
|
||||
|
||||
var src_path = get_packages_dir() + '/' + safe_package_path(pkg) + '/' + stem
|
||||
var src_path = get_packages_dir() + '/' + fd.safe_package_path(pkg) + '/' + stem
|
||||
if (!fd.is_file(src_path)) return null
|
||||
|
||||
var src = text(fd.slurp(src_path))
|
||||
@@ -665,7 +647,7 @@ Shop.all_script_paths = function() {
|
||||
packages = array(packages, ['core'])
|
||||
}
|
||||
for (i = 0; i < length(packages); i++) {
|
||||
pkg_dir = starts_with(packages[i], '/') ? packages[i] : get_packages_dir() + '/' + safe_package_path(packages[i])
|
||||
pkg_dir = starts_with(packages[i], '/') ? packages[i] : get_packages_dir() + '/' + fd.safe_package_path(packages[i])
|
||||
scripts = get_package_scripts(packages[i])
|
||||
for (j = 0; j < length(scripts); j++) {
|
||||
result[] = {
|
||||
@@ -710,7 +692,7 @@ function resolve_mod_fn(path, pkg) {
|
||||
|
||||
// Compute _pkg_dir and _stem early so all paths can use them
|
||||
if (pkg) {
|
||||
_pkg_dir = get_packages_dir() + '/' + safe_package_path(pkg)
|
||||
_pkg_dir = get_packages_dir() + '/' + fd.safe_package_path(pkg)
|
||||
if (starts_with(path, _pkg_dir + '/')) {
|
||||
_stem = text(path, length(_pkg_dir) + 1)
|
||||
}
|
||||
@@ -772,7 +754,7 @@ function resolve_mod_fn(path, pkg) {
|
||||
mcode_json = shop_json.encode(optimized)
|
||||
|
||||
// Cache mcode (architecture-independent) in content-addressed store
|
||||
ensure_dir(global_shop_path + '/build')
|
||||
fd.ensure_dir(global_shop_path + '/build')
|
||||
fd.slurpwrite(cached_mcode_path, stone(blob(mcode_json)))
|
||||
|
||||
// Cache mach blob
|
||||
@@ -813,7 +795,7 @@ function resolve_path(path, ctx)
|
||||
explicit = null
|
||||
}
|
||||
if (explicit) {
|
||||
explicit_path = get_packages_dir() + '/' + safe_package_path(explicit.package) + '/' + explicit.path
|
||||
explicit_path = get_packages_dir() + '/' + fd.safe_package_path(explicit.package) + '/' + explicit.path
|
||||
if (fd.is_file(explicit_path))
|
||||
return {path: explicit_path, scope: SCOPE_PACKAGE, pkg: explicit.package}
|
||||
}
|
||||
@@ -829,7 +811,7 @@ function resolve_path(path, ctx)
|
||||
if (starts_with(ctx, '/'))
|
||||
ctx_dir = ctx
|
||||
else
|
||||
ctx_dir = get_packages_dir() + '/' + safe_package_path(ctx)
|
||||
ctx_dir = get_packages_dir() + '/' + fd.safe_package_path(ctx)
|
||||
ctx_path = ctx_dir + '/' + path
|
||||
|
||||
if (fd.is_file(ctx_path)) {
|
||||
@@ -843,12 +825,12 @@ function resolve_path(path, ctx)
|
||||
|
||||
alias = pkg_tools.split_alias(ctx, path)
|
||||
if (alias) {
|
||||
alias_path = get_packages_dir() + '/' + safe_package_path(alias.package) + '/' + alias.path
|
||||
alias_path = get_packages_dir() + '/' + fd.safe_package_path(alias.package) + '/' + alias.path
|
||||
if (fd.is_file(alias_path))
|
||||
return {path: alias_path, scope: SCOPE_PACKAGE, pkg: ctx}
|
||||
}
|
||||
|
||||
package_path = get_packages_dir() + '/' + safe_package_path(path)
|
||||
package_path = get_packages_dir() + '/' + fd.safe_package_path(path)
|
||||
if (fd.is_file(package_path))
|
||||
return {path: package_path, scope: SCOPE_PACKAGE, pkg: ctx}
|
||||
|
||||
@@ -865,7 +847,7 @@ function resolve_path(path, ctx)
|
||||
}
|
||||
})
|
||||
if (best_pkg && best_remainder) {
|
||||
shop_dir = get_packages_dir() + '/' + safe_package_path(best_pkg)
|
||||
shop_dir = get_packages_dir() + '/' + fd.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}
|
||||
@@ -1283,7 +1265,7 @@ function get_module(path, package_context) {
|
||||
|
||||
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
|
||||
_ctx_dir = package_context ? (starts_with(package_context, '/') ? package_context : get_packages_dir() + '/' + fd.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'`)
|
||||
@@ -1319,7 +1301,7 @@ Shop.use = function use(path, package_context) {
|
||||
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
|
||||
_ctx_dir2 = package_context ? (starts_with(package_context, '/') ? package_context : get_packages_dir() + '/' + fd.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'`)
|
||||
@@ -1336,8 +1318,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) {
|
||||
@@ -1354,14 +1334,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 + '/')) {
|
||||
@@ -1372,8 +1351,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)
|
||||
@@ -1383,78 +1361,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.
|
||||
@@ -1486,7 +1405,7 @@ function get_cache_path(pkg, commit) {
|
||||
|
||||
function get_package_abs_dir(package)
|
||||
{
|
||||
return get_packages_dir() + '/' + safe_package_path(package)
|
||||
return get_packages_dir() + '/' + fd.safe_package_path(package)
|
||||
}
|
||||
|
||||
// Fetch the latest commit hash from remote for a package
|
||||
@@ -1510,7 +1429,7 @@ function fetch_remote_hash(pkg) {
|
||||
// Returns the zip blob or null on failure
|
||||
function download_zip(pkg, commit_hash) {
|
||||
var cache_path = get_cache_path(pkg, commit_hash)
|
||||
ensure_dir(global_shop_path + '/cache')
|
||||
fd.ensure_dir(global_shop_path + '/cache')
|
||||
|
||||
var download_url = Shop.get_download_url(pkg, commit_hash)
|
||||
if (!download_url) {
|
||||
@@ -1732,6 +1651,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 }
|
||||
@@ -1740,7 +1735,7 @@ function install_zip(zip_blob, target_dir) {
|
||||
if (fd.is_dir(target_dir)) fd.rmdir(target_dir, 1)
|
||||
|
||||
log.shop("Extracting to " + target_dir)
|
||||
ensure_dir(target_dir)
|
||||
fd.ensure_dir(target_dir)
|
||||
|
||||
var count = zip.count()
|
||||
var created_dirs = {}
|
||||
@@ -1763,7 +1758,7 @@ function install_zip(zip_blob, target_dir) {
|
||||
dir_path = fd.dirname(full_path)
|
||||
|
||||
if (!created_dirs[dir_path]) {
|
||||
ensure_dir(dir_path)
|
||||
fd.ensure_dir(dir_path)
|
||||
created_dirs[dir_path] = true
|
||||
}
|
||||
file_data = zip.slurp(filename)
|
||||
@@ -1784,7 +1779,7 @@ Shop.remove = function(pkg) {
|
||||
}
|
||||
|
||||
// Remove package symlink/directory
|
||||
var pkg_dir = get_packages_dir() + '/' + safe_package_path(pkg)
|
||||
var pkg_dir = get_packages_dir() + '/' + fd.safe_package_path(pkg)
|
||||
if (fd.is_link(pkg_dir)) {
|
||||
fd.unlink(pkg_dir)
|
||||
} else if (fd.is_dir(pkg_dir)) {
|
||||
@@ -1798,34 +1793,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
|
||||
|
||||
@@ -1902,6 +1869,7 @@ Shop.build_package_scripts = function(package)
|
||||
ok = ok + 1
|
||||
} disruption {
|
||||
push(errors, script)
|
||||
log.console(" compile error: " + package + '/' + script)
|
||||
}
|
||||
_try()
|
||||
})
|
||||
@@ -1922,7 +1890,8 @@ Shop.get_lib_dir = function() {
|
||||
return global_shop_path + '/lib'
|
||||
}
|
||||
|
||||
Shop.ensure_dir = ensure_dir
|
||||
Shop.ensure_dir = fd.ensure_dir
|
||||
Shop.install_zip = install_zip
|
||||
Shop.ensure_package_dylibs = ensure_package_dylibs
|
||||
|
||||
Shop.get_local_dir = function() {
|
||||
@@ -1936,7 +1905,7 @@ Shop.get_build_dir = function() {
|
||||
|
||||
// Get the absolute path for a package
|
||||
Shop.get_package_dir = function(pkg) {
|
||||
return get_packages_dir() + '/' + safe_package_path(pkg)
|
||||
return get_packages_dir() + '/' + fd.safe_package_path(pkg)
|
||||
}
|
||||
|
||||
// Generate C symbol name for a file within a package
|
||||
@@ -1957,7 +1926,7 @@ Shop.c_symbol_prefix = function(pkg) {
|
||||
|
||||
// Get the library directory name for a package
|
||||
Shop.lib_name_for_package = function(pkg) {
|
||||
return safe_package_path(pkg)
|
||||
return fd.safe_package_path(pkg)
|
||||
}
|
||||
|
||||
// Load a module explicitly as mach bytecode, bypassing dylib resolution.
|
||||
@@ -2011,7 +1980,7 @@ Shop.load_as_mach = function(path, pkg) {
|
||||
optimized = _streamline_mod(ir)
|
||||
mcode_json = shop_json.encode(optimized)
|
||||
cached_mcode_path = hash_path(content_key, 'mcode')
|
||||
ensure_dir(global_shop_path + '/build')
|
||||
fd.ensure_dir(global_shop_path + '/build')
|
||||
fd.slurpwrite(cached_mcode_path, stone(blob(mcode_json)))
|
||||
compiled = mach_compile_mcode_bin(file_path, mcode_json)
|
||||
put_into_cache(content_key, compiled)
|
||||
@@ -2048,7 +2017,7 @@ Shop.load_as_dylib = function(path, pkg) {
|
||||
}
|
||||
if (!real_pkg) return null
|
||||
|
||||
pkg_dir = get_packages_dir() + '/' + safe_package_path(real_pkg)
|
||||
pkg_dir = get_packages_dir() + '/' + fd.safe_package_path(real_pkg)
|
||||
if (!starts_with(file_path, pkg_dir + '/')) return null
|
||||
stem = text(file_path, length(pkg_dir) + 1)
|
||||
result = try_native_mod_dylib(real_pkg, stem)
|
||||
|
||||
@@ -33,26 +33,54 @@ function get_pkg_dir(package_name) {
|
||||
return shop.get_package_dir(package_name)
|
||||
}
|
||||
|
||||
// Ensure directory exists
|
||||
function ensure_dir(path) {
|
||||
if (fd.is_dir(path)) return true
|
||||
|
||||
var parts = array(path, '/')
|
||||
var current = starts_with(path, '/') ? '/' : ''
|
||||
// Deep comparison of two values (handles arrays and objects)
|
||||
function values_equal(a, b) {
|
||||
var i = 0
|
||||
for (i = 0; i < length(parts); i++) {
|
||||
if (parts[i] == '') continue
|
||||
current = current + parts[i] + '/'
|
||||
if (!fd.is_dir(current)) {
|
||||
fd.mkdir(current)
|
||||
var ka = null
|
||||
var kb = null
|
||||
if (a == b) return true
|
||||
if (is_null(a) && is_null(b)) return true
|
||||
if (is_null(a) || is_null(b)) return false
|
||||
if (is_array(a) && is_array(b)) {
|
||||
if (length(a) != length(b)) return false
|
||||
i = 0
|
||||
while (i < length(a)) {
|
||||
if (!values_equal(a[i], b[i])) return false
|
||||
i = i + 1
|
||||
}
|
||||
return true
|
||||
}
|
||||
return true
|
||||
if (is_object(a) && is_object(b)) {
|
||||
ka = array(a)
|
||||
kb = array(b)
|
||||
if (length(ka) != length(kb)) return false
|
||||
i = 0
|
||||
while (i < length(ka)) {
|
||||
if (!values_equal(a[ka[i]], b[ka[i]])) return false
|
||||
i = i + 1
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Describe a value for error messages
|
||||
function describe(val) {
|
||||
if (is_null(val)) return "null"
|
||||
if (is_text(val)) return `"${val}"`
|
||||
if (is_number(val)) return text(val)
|
||||
if (is_logical(val)) return text(val)
|
||||
if (is_function(val)) return "<function>"
|
||||
if (is_array(val)) return `[array length=${text(length(val))}]`
|
||||
if (is_object(val)) return `{record keys=${text(length(array(val)))}}`
|
||||
return "<unknown>"
|
||||
}
|
||||
|
||||
return {
|
||||
is_valid_package: is_valid_package,
|
||||
get_current_package_name: get_current_package_name,
|
||||
get_pkg_dir: get_pkg_dir,
|
||||
ensure_dir: ensure_dir
|
||||
ensure_dir: fd.ensure_dir,
|
||||
values_equal: values_equal,
|
||||
describe: describe
|
||||
}
|
||||
|
||||
21
link.ce
21
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'
|
||||
|
||||
28
link.cm
28
link.cm
@@ -20,30 +20,8 @@ function get_packages_dir() {
|
||||
return global_shop_path + '/packages'
|
||||
}
|
||||
|
||||
// return the safe path for the package
|
||||
function safe_package_path(pkg) {
|
||||
// For absolute paths, replace / with _ to create a valid directory name
|
||||
if (pkg && starts_with(pkg, '/'))
|
||||
return replace(replace(pkg, '/', '_'), '@', '_')
|
||||
return replace(pkg, '@', '_')
|
||||
}
|
||||
|
||||
function get_package_abs_dir(package) {
|
||||
return get_packages_dir() + '/' + safe_package_path(package)
|
||||
}
|
||||
|
||||
function ensure_dir(path) {
|
||||
if (fd.stat(path).isDirectory) return
|
||||
var parts = array(path, '/')
|
||||
var current = starts_with(path, '/') ? '/' : ''
|
||||
var i = 0
|
||||
for (i = 0; i < length(parts); i++) {
|
||||
if (parts[i] == '') continue
|
||||
current = current + parts[i] + '/'
|
||||
if (!fd.stat(current).isDirectory) {
|
||||
fd.mkdir(current)
|
||||
}
|
||||
}
|
||||
return get_packages_dir() + '/' + fd.safe_package_path(package)
|
||||
}
|
||||
|
||||
// Resolve a link target to its actual path
|
||||
@@ -54,7 +32,7 @@ function resolve_link_target(target) {
|
||||
return target
|
||||
}
|
||||
// Target is another package - resolve to its directory
|
||||
return get_packages_dir() + '/' + safe_package_path(target)
|
||||
return get_packages_dir() + '/' + fd.safe_package_path(target)
|
||||
}
|
||||
|
||||
var Link = {}
|
||||
@@ -194,7 +172,7 @@ Link.sync_one = function(canonical, target, shop) {
|
||||
|
||||
// Ensure parent directories exist
|
||||
var parent = fd.dirname(target_dir)
|
||||
ensure_dir(parent)
|
||||
fd.ensure_dir(parent)
|
||||
|
||||
// Check current state
|
||||
var current_link = null
|
||||
|
||||
10
list.ce
10
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
14
package.cm
14
package.cm
@@ -6,16 +6,6 @@ var link = use('link')
|
||||
|
||||
var global_shop_path = runtime.shop_path
|
||||
|
||||
// Convert package name to a safe directory name
|
||||
// For absolute paths (local packages), replace / with _
|
||||
// For remote packages, keep slashes as they use nested directories
|
||||
function safe_package_path(pkg) {
|
||||
if (!pkg) return pkg
|
||||
if (starts_with(pkg, '/'))
|
||||
return replace(replace(pkg, '/', '_'), '@', '_')
|
||||
return replace(pkg, '@', '_')
|
||||
}
|
||||
|
||||
function get_path(name)
|
||||
{
|
||||
// If name is null, return the current project directory
|
||||
@@ -33,11 +23,11 @@ function get_path(name)
|
||||
if (starts_with(link_target, '/'))
|
||||
return link_target
|
||||
// Otherwise it's another package name, resolve that
|
||||
return global_shop_path + '/packages/' + replace(replace(link_target, '/', '_'), '@', '_')
|
||||
return global_shop_path + '/packages/' + fd.safe_package_path(link_target)
|
||||
}
|
||||
|
||||
// Remote packages use nested directories, so don't transform slashes
|
||||
return global_shop_path + '/packages/' + replace(name, '@', '_')
|
||||
return global_shop_path + '/packages/' + fd.safe_package_path(name)
|
||||
}
|
||||
|
||||
var config_cache = {}
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
75
test.ce
75
test.ce
@@ -7,6 +7,7 @@ var time = use('time')
|
||||
var json = use('json')
|
||||
var blob = use('blob')
|
||||
var dbg = use('js')
|
||||
var testlib = use('internal/testlib')
|
||||
|
||||
// run gc with dbg.gc()
|
||||
|
||||
@@ -29,23 +30,8 @@ def ACTOR_TEST_TIMEOUT = 30000 // 30 seconds timeout for actor tests
|
||||
var pending_actor_tests = []
|
||||
var actor_test_results = []
|
||||
|
||||
// Check if current directory is a valid cell package
|
||||
function is_valid_package(dir) {
|
||||
var _dir = dir == null ? '.' : dir
|
||||
return fd.is_file(_dir + '/cell.toml')
|
||||
}
|
||||
|
||||
// Get current package name from cell.toml or null
|
||||
function get_current_package_name() {
|
||||
if (!is_valid_package('.')) return null
|
||||
var _load = function() {
|
||||
var config = pkg.load_config(null)
|
||||
return config.package || 'local'
|
||||
} disruption {
|
||||
return 'local'
|
||||
}
|
||||
return _load()
|
||||
}
|
||||
var is_valid_package = testlib.is_valid_package
|
||||
var get_current_package_name = testlib.get_current_package_name
|
||||
|
||||
// Parse arguments
|
||||
// Usage:
|
||||
@@ -192,32 +178,8 @@ if (diff_mode && !run_ast_noopt_fn) {
|
||||
return
|
||||
}
|
||||
|
||||
// Diff mode: deep comparison helper
|
||||
function values_equal(a, b) {
|
||||
var i = 0
|
||||
if (a == b) return true
|
||||
if (is_null(a) && is_null(b)) return true
|
||||
if (is_null(a) || is_null(b)) return false
|
||||
if (is_array(a) && is_array(b)) {
|
||||
if (length(a) != length(b)) return false
|
||||
i = 0
|
||||
while (i < length(a)) {
|
||||
if (!values_equal(a[i], b[i])) return false
|
||||
i = i + 1
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function describe(val) {
|
||||
if (is_null(val)) return "null"
|
||||
if (is_text(val)) return `"${val}"`
|
||||
if (is_number(val)) return text(val)
|
||||
if (is_logical(val)) return text(val)
|
||||
if (is_function(val)) return "<function>"
|
||||
return "<other>"
|
||||
}
|
||||
var values_equal = testlib.values_equal
|
||||
var describe = testlib.describe
|
||||
|
||||
// Diff mode: run a test function through noopt and compare
|
||||
var diff_mismatches = 0
|
||||
@@ -251,31 +213,8 @@ function diff_check(test_name, file_path, opt_fn, noopt_fn) {
|
||||
}
|
||||
}
|
||||
|
||||
function ensure_dir(path) {
|
||||
if (fd.is_dir(path)) return true
|
||||
|
||||
var parts = array(path, '/')
|
||||
var current = starts_with(path, '/') ? '/' : ''
|
||||
var i = 0
|
||||
for (i = 0; i < length(parts); i++) {
|
||||
if (parts[i] == '') continue
|
||||
current = current + parts[i] + '/'
|
||||
if (!fd.is_dir(current))
|
||||
fd.mkdir(current)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Get the directory for a package
|
||||
function get_pkg_dir(package_name) {
|
||||
if (!package_name) {
|
||||
return fd.realpath('.')
|
||||
}
|
||||
if (starts_with(package_name, '/')) {
|
||||
return package_name
|
||||
}
|
||||
return shop.get_package_dir(package_name)
|
||||
}
|
||||
var ensure_dir = fd.ensure_dir
|
||||
var get_pkg_dir = testlib.get_pkg_dir
|
||||
|
||||
// Collect .ce actor tests from a package
|
||||
function collect_actor_tests(package_name, specific_test) {
|
||||
|
||||
121
update.ce
121
update.ce
@@ -1,30 +1,28 @@
|
||||
// cell update [<locator>] - 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 <locator> Update a specific package
|
||||
//
|
||||
// Options:
|
||||
// --build Run build after updating
|
||||
// --target <triple> Target platform for build (requires --build)
|
||||
// --target <triple> 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('internal/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 [<locator>] [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 <triple> Target platform for build (requires --build)")
|
||||
log.console(" --target <triple> 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()
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user