fix links, fix hot reload
This commit is contained in:
4
Makefile
4
Makefile
@@ -9,6 +9,10 @@
|
|||||||
CELL_SHOP = $(HOME)/.cell
|
CELL_SHOP = $(HOME)/.cell
|
||||||
CELL_CORE_PACKAGE = $(CELL_SHOP)/packages/core
|
CELL_CORE_PACKAGE = $(CELL_SHOP)/packages/core
|
||||||
|
|
||||||
|
makecell:
|
||||||
|
cell pack core -o cell
|
||||||
|
cp cell /opt/homebrew/bin/
|
||||||
|
|
||||||
# Install core: symlink this directory to ~/.cell/core
|
# Install core: symlink this directory to ~/.cell/core
|
||||||
install: bootstrap $(CELL_SHOP)
|
install: bootstrap $(CELL_SHOP)
|
||||||
@echo "Linking cell core to $(CELL_CORE_PACKAGE)"
|
@echo "Linking cell core to $(CELL_CORE_PACKAGE)"
|
||||||
|
|||||||
12
build.ce
12
build.ce
@@ -40,15 +40,6 @@ for (var i = 0; i < args.length; i++) {
|
|||||||
log.error('-b requires a buildtype (release, debug, minsize)')
|
log.error('-b requires a buildtype (release, debug, minsize)')
|
||||||
$_.stop()
|
$_.stop()
|
||||||
}
|
}
|
||||||
log.console('Usage: cell build [options]')
|
|
||||||
log.console('')
|
|
||||||
log.console('Options:')
|
|
||||||
log.console(' -p, --package <pkg> Build specific package only')
|
|
||||||
log.console(' -t, --target <target> Cross-compile for target platform')
|
|
||||||
log.console(' -b, --buildtype <type> Build type: release, debug, minsize (default: release)')
|
|
||||||
log.console('')
|
|
||||||
log.console('Available targets: ' + build.list_targets().join(', '))
|
|
||||||
$_.stop()
|
|
||||||
} else if (args[i] == '--list-targets') {
|
} else if (args[i] == '--list-targets') {
|
||||||
log.console('Available targets:')
|
log.console('Available targets:')
|
||||||
var targets = build.list_targets()
|
var targets = build.list_targets()
|
||||||
@@ -105,8 +96,7 @@ if (target_package) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.console('')
|
log.console(`Build complete: ${success} libraries built${failed > 0 ? `, ${failed} failed` : ''}`)
|
||||||
log.console('Build complete: ' + success + ' libraries built' + (failed > 0 ? ', ' + failed + ' failed' : ''))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$_.stop()
|
$_.stop()
|
||||||
|
|||||||
29
build.cm
29
build.cm
@@ -194,6 +194,7 @@ Build.build_package = function(pkg, target, exclude_main, buildtype = 'release')
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Build a dynamic library for a package
|
// Build a dynamic library for a package
|
||||||
// Output goes to .cell/lib/<package_name>.<ext>
|
// Output goes to .cell/lib/<package_name>.<ext>
|
||||||
|
// Dynamic libraries do NOT link against core; undefined symbols are resolved at dlopen time
|
||||||
Build.build_dynamic = function(pkg, target, buildtype = 'release') {
|
Build.build_dynamic = function(pkg, target, buildtype = 'release') {
|
||||||
target = target || Build.detect_host_target()
|
target = target || Build.detect_host_target()
|
||||||
|
|
||||||
@@ -222,15 +223,26 @@ Build.build_dynamic = function(pkg, target, buildtype = 'release') {
|
|||||||
// Build link command
|
// Build link command
|
||||||
var cmd_parts = [cc, '-shared', '-fPIC']
|
var cmd_parts = [cc, '-shared', '-fPIC']
|
||||||
|
|
||||||
// Add rpath to find libraries in .cell/local at runtime
|
// Platform-specific flags for undefined symbols (resolved at dlopen) and size optimization
|
||||||
if (tc.system == 'darwin') {
|
if (tc.system == 'darwin') {
|
||||||
|
// Allow undefined symbols - they will be resolved when dlopen'd into the main executable
|
||||||
|
cmd_parts.push('-undefined', 'dynamic_lookup')
|
||||||
|
// Dead-strip unused code
|
||||||
|
cmd_parts.push('-Wl,-dead_strip')
|
||||||
|
// rpath for .cell/local libraries
|
||||||
cmd_parts.push('-Wl,-rpath,@loader_path/../local')
|
cmd_parts.push('-Wl,-rpath,@loader_path/../local')
|
||||||
cmd_parts.push('-Wl,-rpath,' + local_dir)
|
cmd_parts.push('-Wl,-rpath,' + local_dir)
|
||||||
} else if (tc.system == 'linux') {
|
} else if (tc.system == 'linux') {
|
||||||
|
// Allow undefined symbols at link time
|
||||||
|
cmd_parts.push('-Wl,--allow-shlib-undefined')
|
||||||
|
// Garbage collect unused sections
|
||||||
|
cmd_parts.push('-Wl,--gc-sections')
|
||||||
|
// rpath for .cell/local libraries
|
||||||
cmd_parts.push('-Wl,-rpath,$ORIGIN/../local')
|
cmd_parts.push('-Wl,-rpath,$ORIGIN/../local')
|
||||||
cmd_parts.push('-Wl,-rpath,' + local_dir)
|
cmd_parts.push('-Wl,-rpath,' + local_dir)
|
||||||
} else if (tc.system == 'windows') {
|
} else if (tc.system == 'windows') {
|
||||||
// Windows uses PATH or same directory, add -L for link time
|
// Windows DLLs: use --allow-shlib-undefined for mingw
|
||||||
|
cmd_parts.push('-Wl,--allow-shlib-undefined')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add .cell/local to library search path
|
// Add .cell/local to library search path
|
||||||
@@ -240,14 +252,7 @@ Build.build_dynamic = function(pkg, target, buildtype = 'release') {
|
|||||||
cmd_parts.push('"' + objects[i] + '"')
|
cmd_parts.push('"' + objects[i] + '"')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Link against core library (all dynamic libs depend on core)
|
// Do NOT link against core library - symbols resolved at dlopen time
|
||||||
if (pkg != 'core') {
|
|
||||||
var core_lib_name = shop.lib_name_for_package('core')
|
|
||||||
var core_lib_path = lib_dir + '/' + core_lib_name + dylib_ext
|
|
||||||
if (fd.is_file(core_lib_path)) {
|
|
||||||
cmd_parts.push('"' + core_lib_path + '"')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add LDFLAGS (resolve relative -L paths)
|
// Add LDFLAGS (resolve relative -L paths)
|
||||||
for (var i = 0; i < ldflags.length; i++) {
|
for (var i = 0; i < ldflags.length; i++) {
|
||||||
@@ -375,7 +380,7 @@ Build.build_all_dynamic = function(target, buildtype = 'release') {
|
|||||||
// Build core first
|
// Build core first
|
||||||
if (packages.indexOf('core') >= 0) {
|
if (packages.indexOf('core') >= 0) {
|
||||||
try {
|
try {
|
||||||
var lib = Build.build_dynamic('core', target)
|
var lib = Build.build_dynamic('core', target, buildtype)
|
||||||
results.push({ package: 'core', library: lib })
|
results.push({ package: 'core', library: lib })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.error('Failed to build core: ' + e)
|
log.error('Failed to build core: ' + e)
|
||||||
@@ -389,7 +394,7 @@ Build.build_all_dynamic = function(target, buildtype = 'release') {
|
|||||||
if (pkg == 'core') continue
|
if (pkg == 'core') continue
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var lib = Build.build_dynamic(pkg, target)
|
var lib = Build.build_dynamic(pkg, target, buildtype)
|
||||||
results.push({ package: pkg, library: lib })
|
results.push({ package: pkg, library: lib })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.error('Failed to build ' + pkg + ': ')
|
log.error('Failed to build ' + pkg + ': ')
|
||||||
|
|||||||
@@ -279,7 +279,7 @@ function mount_package(name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var shop = use('shop')
|
var shop = use('shop')
|
||||||
var dir = shop.get_module_dir(name)
|
var dir = shop.get_package_dir(name)
|
||||||
|
|
||||||
if (!dir) {
|
if (!dir) {
|
||||||
throw new Error("Package not found: " + name)
|
throw new Error("Package not found: " + name)
|
||||||
|
|||||||
@@ -215,6 +215,7 @@ var $_ = create_actor()
|
|||||||
|
|
||||||
os.use_cache = use_cache
|
os.use_cache = use_cache
|
||||||
os.global_shop_path = shop_path
|
os.global_shop_path = shop_path
|
||||||
|
os.$_ = $_
|
||||||
|
|
||||||
var shop = use('shop')
|
var shop = use('shop')
|
||||||
|
|
||||||
|
|||||||
164
link.ce
164
link.ce
@@ -1,15 +1,18 @@
|
|||||||
// link <command> [args]
|
// link <command> [args]
|
||||||
// Commands:
|
// Commands:
|
||||||
// list
|
// list List all active links
|
||||||
// delete <alias>
|
// sync Ensure all symlinks are in place
|
||||||
// clear
|
// delete <package> Remove a link
|
||||||
// [package] <path|target>
|
// clear Remove all links
|
||||||
|
// <path> Link the package in <path> to that path
|
||||||
|
// <package> <target> Link <package> to <target> (path or another package)
|
||||||
//
|
//
|
||||||
// Examples:
|
// Examples:
|
||||||
// cell link ../cell-steam (Links package defined in ../cell-steam to that path)
|
// cell link ../cell-steam (Links package in ../cell-steam)
|
||||||
// cell link my-pkg ../cell-steam (Links my-pkg to ../cell-steam)
|
// cell link gitea.pockle.world/john/prosperon ../prosperon (Links prosperon to local path)
|
||||||
// cell link my-pkg other-pkg (Links my-pkg to other-pkg)
|
// cell link gitea.pockle.world/john/prosperon github.com/prosperon (Links to another remote)
|
||||||
|
|
||||||
|
var link = use('link')
|
||||||
var shop = use('shop')
|
var shop = use('shop')
|
||||||
var fd = use('fd')
|
var fd = use('fd')
|
||||||
var toml = use('toml')
|
var toml = use('toml')
|
||||||
@@ -18,10 +21,11 @@ if (args.length < 1) {
|
|||||||
log.console("Usage: link <command> [args] or link [package] <target>")
|
log.console("Usage: link <command> [args] or link [package] <target>")
|
||||||
log.console("Commands:")
|
log.console("Commands:")
|
||||||
log.console(" list List all active links")
|
log.console(" list List all active links")
|
||||||
log.console(" delete <alias> Remove a link")
|
log.console(" sync Ensure all symlinks are in place")
|
||||||
|
log.console(" delete <package> Remove a link and restore original")
|
||||||
log.console(" clear Remove all links")
|
log.console(" clear Remove all links")
|
||||||
log.console(" <path> Link the package in <path> to <path>")
|
log.console(" <path> Link the package in <path> to that path")
|
||||||
log.console(" <alias> <target> Link <alias> to <target> (path or package)")
|
log.console(" <package> <target> Link <package> to <target> (path or package)")
|
||||||
$_.stop()
|
$_.stop()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -29,38 +33,57 @@ if (args.length < 1) {
|
|||||||
var cmd = args[0]
|
var cmd = args[0]
|
||||||
|
|
||||||
if (cmd == 'list') {
|
if (cmd == 'list') {
|
||||||
var links = shop.load_links()
|
var links = link.load()
|
||||||
var count = 0
|
var count = 0
|
||||||
for (var k in links) {
|
for (var k in links) {
|
||||||
log.console(k + " -> " + links[k])
|
log.console(k + " -> " + links[k])
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
if (count == 0) log.console("No links.")
|
if (count == 0) log.console("No links.")
|
||||||
|
|
||||||
|
} else if (cmd == 'sync') {
|
||||||
|
log.console("Syncing links...")
|
||||||
|
var result = link.sync_all(shop)
|
||||||
|
log.console("Synced " + result.synced + " link(s)")
|
||||||
|
if (result.errors.length > 0) {
|
||||||
|
log.console("Errors:")
|
||||||
|
for (var i = 0; i < result.errors.length; i++) {
|
||||||
|
log.console(" " + result.errors[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else if (cmd == 'delete' || cmd == 'rm') {
|
} else if (cmd == 'delete' || cmd == 'rm') {
|
||||||
if (args.length < 2) {
|
if (args.length < 2) {
|
||||||
log.console("Usage: link delete <alias>")
|
log.console("Usage: link delete <package>")
|
||||||
$_.stop()
|
$_.stop()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var target = args[1]
|
var pkg = args[1]
|
||||||
|
|
||||||
// Try to remove directly
|
if (link.remove(pkg)) {
|
||||||
if (shop.remove_link(target)) {
|
// Try to restore the original package
|
||||||
// Update to restore original if possible
|
log.console("Restoring " + pkg + "...")
|
||||||
log.console("Restoring " + target + "...")
|
try {
|
||||||
shop.update(target)
|
shop.fetch(pkg)
|
||||||
|
shop.extract(pkg)
|
||||||
|
log.console("Restored " + pkg)
|
||||||
|
} catch (e) {
|
||||||
|
log.console("Could not restore: " + e.message)
|
||||||
|
log.console("Run 'cell update " + pkg + "' to restore")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log.console("No link found for " + target)
|
log.console("No link found for " + pkg)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (cmd == 'clear') {
|
} else if (cmd == 'clear') {
|
||||||
shop.clear_links()
|
link.clear()
|
||||||
log.console("Links cleared. Run 'cell update' to restore packages if needed.")
|
log.console("Links cleared. Run 'cell update' to restore packages.")
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Linking logic
|
// Linking logic
|
||||||
var pkg_name = null
|
var pkg_name = null
|
||||||
var target_path = null
|
var target = null
|
||||||
|
|
||||||
// Check for 'add' compatibility
|
// Check for 'add' compatibility
|
||||||
var start_idx = 0
|
var start_idx = 0
|
||||||
@@ -68,18 +91,11 @@ if (cmd == 'list') {
|
|||||||
start_idx = 1
|
start_idx = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse arguments
|
|
||||||
// usage: link [pkg] <target>
|
|
||||||
// valid inputs:
|
|
||||||
// link ../foo
|
|
||||||
// link pkg ../foo
|
|
||||||
// link pkg remote-pkg
|
|
||||||
|
|
||||||
var arg1 = args[start_idx]
|
var arg1 = args[start_idx]
|
||||||
var arg2 = (args.length > start_idx + 1) ? args[start_idx + 1] : null
|
var arg2 = (args.length > start_idx + 1) ? args[start_idx + 1] : null
|
||||||
|
|
||||||
if (!arg1) {
|
if (!arg1) {
|
||||||
log.console("Error: specific target or package required")
|
log.console("Error: target or package required")
|
||||||
$_.stop()
|
$_.stop()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -87,34 +103,50 @@ if (cmd == 'list') {
|
|||||||
if (arg2) {
|
if (arg2) {
|
||||||
// Two arguments: explicit package name and target
|
// Two arguments: explicit package name and target
|
||||||
pkg_name = arg1
|
pkg_name = arg1
|
||||||
target_path = arg2
|
target = arg2
|
||||||
|
|
||||||
|
// Resolve target if it's a local path
|
||||||
|
if (target == '.' || fd.is_dir(target)) {
|
||||||
|
target = fd.realpath(target)
|
||||||
|
} else if (target.startsWith('./') || target.startsWith('../')) {
|
||||||
|
// Relative path that doesn't exist yet - try to resolve anyway
|
||||||
|
var cwd = fd.realpath('.')
|
||||||
|
if (target.startsWith('./')) {
|
||||||
|
target = cwd + target.substring(1)
|
||||||
} else {
|
} else {
|
||||||
// One argument: assume it's a path, and we need to infer the package name
|
// For ../ paths, let fd.realpath handle it if possible
|
||||||
target_path = arg1
|
target = fd.realpath(target) || target
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Otherwise target is a package name (e.g., github.com/prosperon)
|
||||||
|
|
||||||
// Resolve path to check for cell.toml
|
} else {
|
||||||
var resolved = target_path
|
// One argument: assume it's a local path, infer package name from cell.toml
|
||||||
if (fd.is_dir(resolved)) resolved = fd.realpath(resolved)
|
target = arg1
|
||||||
|
|
||||||
var toml_path = resolved + '/cell.toml'
|
// Resolve path
|
||||||
|
if (target == '.' || fd.is_dir(target)) {
|
||||||
|
target = fd.realpath(target)
|
||||||
|
} else if (target.startsWith('./') || target.startsWith('../')) {
|
||||||
|
target = fd.realpath(target) || target
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must be a local path with cell.toml
|
||||||
|
var toml_path = target + '/cell.toml'
|
||||||
if (!fd.is_file(toml_path)) {
|
if (!fd.is_file(toml_path)) {
|
||||||
log.console("Error: No cell.toml found at " + resolved + ". Cannot infer package name.")
|
log.console("Error: No cell.toml found at " + target)
|
||||||
log.console("If you meant to link an alias, provide the target: link <alias> <target>")
|
log.console("For linking to another package, use: link <package> <target>")
|
||||||
$_.stop()
|
$_.stop()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read package name
|
// Read package name from cell.toml
|
||||||
try {
|
try {
|
||||||
var content = toml.decode(text(fd.slurp(toml_path)))
|
var content = toml.decode(text(fd.slurp(toml_path)))
|
||||||
if (content.package) {
|
if (content.package) {
|
||||||
pkg_name = content.package
|
pkg_name = content.package
|
||||||
// If package name has version, strip it?
|
|
||||||
// Usually package = "name", version = "..." in cell.toml?
|
|
||||||
// Or package = "name"
|
|
||||||
// Standard is just name.
|
|
||||||
} else {
|
} else {
|
||||||
log.console("Error: cell.toml at " + resolved + " does not define a 'package' name.")
|
log.console("Error: cell.toml at " + target + " does not define 'package'")
|
||||||
$_.stop()
|
$_.stop()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -123,39 +155,25 @@ if (cmd == 'list') {
|
|||||||
$_.stop()
|
$_.stop()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure target path is fully resolved since we inferred it
|
|
||||||
if (fd.is_dir(target_path)) target_path = fd.realpath(target_path)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the link
|
// Validate: if target is a local path, it must have cell.toml
|
||||||
|
if (target.startsWith('/')) {
|
||||||
// 1. Resolve target if it is a directory
|
if (!fd.is_file(target + '/cell.toml')) {
|
||||||
if (target_path != "." && fd.is_dir(target_path)) {
|
log.console("Error: " + target + " is not a valid package (no cell.toml)")
|
||||||
target_path = fd.realpath(target_path)
|
$_.stop()
|
||||||
} else if (target_path == ".") {
|
return
|
||||||
target_path = fd.realpath(target_path)
|
|
||||||
} else if (target_path.startsWith('/') || target_path.startsWith('./') || target_path.startsWith('../')) {
|
|
||||||
// It looks like a path but doesn't exist?
|
|
||||||
log.console("Warning: Link target '" + target_path + "' does not exist locally. Linking as alias anyway.")
|
|
||||||
if (target_path.startsWith('./') || target_path.startsWith('../')) {
|
|
||||||
// Resolve relative path roughly?
|
|
||||||
var cwd = fd.realpath('.')
|
|
||||||
// simple concat (fd.realpath typically converts to abspath, but if file missing it might fail or return same)
|
|
||||||
// Assuming user wants strict path if they used relative.
|
|
||||||
// Using fd.realpath on CWD + path is safer if we want absolute.
|
|
||||||
// But we don't have path manipulation lib easily exposed except implicit logic.
|
|
||||||
// Leaving as provided string if not existing dir.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Add link
|
// Add the link (this also creates the symlink)
|
||||||
shop.add_link(pkg_name, target_path)
|
try {
|
||||||
|
link.add(pkg_name, target, shop)
|
||||||
// 3. Update shop
|
} catch (e) {
|
||||||
// "Doing this effectively adds another item to the shop"
|
log.console("Error: " + e.message)
|
||||||
// We trigger update to ensure the shop recognizes the new link
|
$_.stop()
|
||||||
shop.update(pkg_name)
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$_.stop()
|
$_.stop()
|
||||||
219
link.cm
Normal file
219
link.cm
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
// Link management module for cell packages
|
||||||
|
// Handles creating, removing, and syncing symlinks for local development
|
||||||
|
|
||||||
|
var toml = use('toml')
|
||||||
|
var fd = use('fd')
|
||||||
|
var utf8 = use('utf8')
|
||||||
|
var os = use('os')
|
||||||
|
|
||||||
|
var global_shop_path = os.global_shop_path
|
||||||
|
|
||||||
|
// Get the links file path (in the global shop)
|
||||||
|
function get_links_path() {
|
||||||
|
return global_shop_path + '/link.toml'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the packages directory (in the global shop)
|
||||||
|
function get_packages_dir() {
|
||||||
|
return global_shop_path + '/packages'
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the safe path for the package
|
||||||
|
function safe_package_path(pkg) {
|
||||||
|
return pkg.replaceAll('@', '_')
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_package_abs_dir(package) {
|
||||||
|
return get_packages_dir() + '/' + package
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensure_dir(path) {
|
||||||
|
if (fd.stat(path).isDirectory) return
|
||||||
|
var parts = path.split('/')
|
||||||
|
var current = path.startsWith('/') ? '/' : ''
|
||||||
|
for (var i = 0; i < parts.length; i++) {
|
||||||
|
if (parts[i] == '') continue
|
||||||
|
current += parts[i] + '/'
|
||||||
|
if (!fd.stat(current).isDirectory) {
|
||||||
|
fd.mkdir(current)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve a link target to its actual path
|
||||||
|
// If target is a local path (starts with /), return it directly
|
||||||
|
// If target is a package name, return the package directory
|
||||||
|
function resolve_link_target(target) {
|
||||||
|
if (target.startsWith('/')) {
|
||||||
|
return target
|
||||||
|
}
|
||||||
|
// Target is another package - resolve to its directory
|
||||||
|
return get_packages_dir() + '/' + safe_package_path(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
var Link = {}
|
||||||
|
|
||||||
|
var link_cache = null
|
||||||
|
|
||||||
|
Link.load = function() {
|
||||||
|
if (link_cache) return link_cache
|
||||||
|
var path = get_links_path()
|
||||||
|
if (!fd.is_file(path)) {
|
||||||
|
link_cache = {}
|
||||||
|
return link_cache
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var content = text(fd.slurp(path))
|
||||||
|
var cfg = toml.decode(content)
|
||||||
|
link_cache = cfg.links || {}
|
||||||
|
} catch (e) {
|
||||||
|
log.console("Warning: Failed to load link.toml: " + e)
|
||||||
|
link_cache = {}
|
||||||
|
}
|
||||||
|
return link_cache
|
||||||
|
}
|
||||||
|
|
||||||
|
Link.save = function(links) {
|
||||||
|
link_cache = links
|
||||||
|
var cfg = { links: links }
|
||||||
|
var path = get_links_path()
|
||||||
|
fd.slurpwrite(path, utf8.encode(toml.encode(cfg)))
|
||||||
|
}
|
||||||
|
|
||||||
|
Link.add = function(canonical, target, shop) {
|
||||||
|
// Validate canonical package exists in shop
|
||||||
|
var lock = shop.load_lock()
|
||||||
|
if (!lock[canonical]) {
|
||||||
|
throw new Error('Package ' + canonical + ' is not installed. Install it first with: cell get ' + canonical)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate target is a valid package
|
||||||
|
if (target.startsWith('/')) {
|
||||||
|
// Local path - must have cell.toml
|
||||||
|
if (!fd.is_file(target + '/cell.toml')) {
|
||||||
|
throw new Error('Target ' + target + ' is not a valid package (no cell.toml)')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Remote package target - ensure it's installed
|
||||||
|
shop.get(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
var links = Link.load()
|
||||||
|
links[canonical] = target
|
||||||
|
Link.save(links)
|
||||||
|
|
||||||
|
// Create the symlink immediately
|
||||||
|
Link.sync_one(canonical, target, shop)
|
||||||
|
|
||||||
|
log.console("Linked " + canonical + " -> " + target)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
Link.remove = function(canonical) {
|
||||||
|
var links = Link.load()
|
||||||
|
if (!links[canonical]) return false
|
||||||
|
|
||||||
|
// Remove the symlink if it exists
|
||||||
|
var target_dir = get_package_abs_dir(canonical)
|
||||||
|
if (fd.is_link(target_dir)) {
|
||||||
|
fd.unlink(target_dir)
|
||||||
|
log.console("Removed symlink at " + target_dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
delete links[canonical]
|
||||||
|
Link.save(links)
|
||||||
|
log.console("Unlinked " + canonical)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
Link.clear = function() {
|
||||||
|
// Remove all symlinks first
|
||||||
|
var links = Link.load()
|
||||||
|
for (var canonical in links) {
|
||||||
|
var target_dir = get_package_abs_dir(canonical)
|
||||||
|
if (fd.is_link(target_dir)) {
|
||||||
|
fd.unlink(target_dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Link.save({})
|
||||||
|
log.console("Cleared all links")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync a single link - ensure the symlink is in place
|
||||||
|
Link.sync_one = function(canonical, target, shop) {
|
||||||
|
var target_dir = get_package_abs_dir(canonical)
|
||||||
|
var link_target = resolve_link_target(target)
|
||||||
|
|
||||||
|
// Ensure parent directories exist
|
||||||
|
var parent = target_dir.substring(0, target_dir.lastIndexOf('/'))
|
||||||
|
ensure_dir(parent)
|
||||||
|
|
||||||
|
// Check current state
|
||||||
|
var current_link = null
|
||||||
|
if (fd.is_link(target_dir)) {
|
||||||
|
current_link = fd.readlink(target_dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If already correctly linked, nothing to do
|
||||||
|
if (current_link == link_target) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove existing file/dir/link
|
||||||
|
if (fd.is_link(target_dir)) {
|
||||||
|
fd.unlink(target_dir)
|
||||||
|
} else if (fd.is_dir(target_dir)) {
|
||||||
|
fd.rmdir(target_dir, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create symlink
|
||||||
|
fd.symlink(link_target, target_dir)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync all links - ensure all symlinks are in place
|
||||||
|
Link.sync_all = function(shop) {
|
||||||
|
var links = Link.load()
|
||||||
|
var count = 0
|
||||||
|
var errors = []
|
||||||
|
|
||||||
|
for (var canonical in links) {
|
||||||
|
var target = links[canonical]
|
||||||
|
try {
|
||||||
|
// Validate target exists
|
||||||
|
var link_target = resolve_link_target(target)
|
||||||
|
if (!fd.is_dir(link_target)) {
|
||||||
|
errors.push(canonical + ': target ' + link_target + ' does not exist')
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (!fd.is_file(link_target + '/cell.toml')) {
|
||||||
|
errors.push(canonical + ': target ' + link_target + ' is not a valid package')
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
Link.sync_one(canonical, target, shop)
|
||||||
|
count++
|
||||||
|
} catch (e) {
|
||||||
|
errors.push(canonical + ': ' + e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { synced: count, errors: errors }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a package is currently linked
|
||||||
|
Link.is_linked = function(canonical) {
|
||||||
|
var links = Link.load()
|
||||||
|
return canonical in links
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the link target for a package (or null if not linked)
|
||||||
|
Link.get_target = function(canonical) {
|
||||||
|
var links = Link.load()
|
||||||
|
return links[canonical] || null
|
||||||
|
}
|
||||||
|
|
||||||
|
return Link
|
||||||
@@ -41,6 +41,13 @@ package.find_alias = function(name, locator)
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
package.alias_to_package = function(name, alias)
|
||||||
|
{
|
||||||
|
var config = package.load_config(name)
|
||||||
|
if (!config.dependencies) return null
|
||||||
|
return config.dependencies[alias]
|
||||||
|
}
|
||||||
|
|
||||||
// alias is optional
|
// alias is optional
|
||||||
package.add_dependency = function(name, locator, alias = locator)
|
package.add_dependency = function(name, locator, alias = locator)
|
||||||
{
|
{
|
||||||
|
|||||||
214
shop.cm
214
shop.cm
@@ -10,6 +10,7 @@ var utf8 = use('utf8')
|
|||||||
var blob = use('blob')
|
var blob = use('blob')
|
||||||
var pkg_tools = use('package')
|
var pkg_tools = use('package')
|
||||||
var os = use('os')
|
var os = use('os')
|
||||||
|
var link = use('link')
|
||||||
|
|
||||||
var core = "core"
|
var core = "core"
|
||||||
|
|
||||||
@@ -58,10 +59,15 @@ var SCOPE_CORE = 2
|
|||||||
var MOD_EXT = '.cm'
|
var MOD_EXT = '.cm'
|
||||||
var ACTOR_EXT = '.ce'
|
var ACTOR_EXT = '.ce'
|
||||||
|
|
||||||
var dylib_ext = '.so' // Default extension
|
var dylib_ext = '.dylib' // Default extension
|
||||||
|
|
||||||
var use_cache = os.use_cache
|
var use_cache = os.use_cache
|
||||||
var global_shop_path = os.global_shop_path
|
var global_shop_path = os.global_shop_path
|
||||||
|
var $_ = os.$_
|
||||||
|
|
||||||
|
Shop.get_package_dir = function(name) {
|
||||||
|
return global_shop_path + '/packages/' + name
|
||||||
|
}
|
||||||
|
|
||||||
// Get the packages directory (in the global shop)
|
// Get the packages directory (in the global shop)
|
||||||
function get_packages_dir() {
|
function get_packages_dir() {
|
||||||
@@ -85,8 +91,6 @@ Shop.get_reports_dir = function() {
|
|||||||
return global_shop_path + '/reports'
|
return global_shop_path + '/reports'
|
||||||
}
|
}
|
||||||
|
|
||||||
var open_dl = {}
|
|
||||||
|
|
||||||
function get_import_package(name) {
|
function get_import_package(name) {
|
||||||
var parts = name.split('/')
|
var parts = name.split('/')
|
||||||
if (parts.length > 1)
|
if (parts.length > 1)
|
||||||
@@ -113,6 +117,16 @@ function abs_path_to_package(package_dir)
|
|||||||
if (package_in_shop(package_dir))
|
if (package_in_shop(package_dir))
|
||||||
return package_dir
|
return package_dir
|
||||||
|
|
||||||
|
// For local directories (e.g., linked targets), read the package name from cell.toml
|
||||||
|
try {
|
||||||
|
var content = text(fd.slurp(package_dir + '/cell.toml'))
|
||||||
|
var cfg = toml.decode(content)
|
||||||
|
if (cfg.package)
|
||||||
|
return cfg.package
|
||||||
|
} catch (e) {
|
||||||
|
// Fall through
|
||||||
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,9 +152,9 @@ Shop.file_info = function(file) {
|
|||||||
|
|
||||||
info.name = file.substring(pkg_dir.length + 1)
|
info.name = file.substring(pkg_dir.length + 1)
|
||||||
if (info.is_actor)
|
if (info.is_actor)
|
||||||
info.name = info.name.substring(0, info.name.length - ACTOR_EXT.length)
|
info.name = info.path.substring(0, info.path.length - ACTOR_EXT.length)
|
||||||
else if (info.is_module)
|
else if (info.is_module)
|
||||||
info.name = info.name.substring(0, info.name.length - MOD_EXT.length)
|
info.name = info.path.substring(0, info.path.length - MOD_EXT.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
return info
|
return info
|
||||||
@@ -212,56 +226,6 @@ Shop.save_lock = function(lock) {
|
|||||||
fd.slurpwrite(path, utf8.encode(toml.encode(lock)));
|
fd.slurpwrite(path, utf8.encode(toml.encode(lock)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* links functionality */
|
|
||||||
var link_cache = null
|
|
||||||
Shop.load_links = function() {
|
|
||||||
if (link_cache) return link_cache
|
|
||||||
var path = global_shop_path + '/link.toml'
|
|
||||||
if (!fd.is_file(path)) {
|
|
||||||
link_cache = {}
|
|
||||||
return link_cache
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
var content = text(fd.slurp(path))
|
|
||||||
var cfg = toml.decode(content)
|
|
||||||
link_cache = cfg.links || {}
|
|
||||||
} catch (e) {
|
|
||||||
log.console("Warning: Failed to load link.toml: " + e)
|
|
||||||
link_cache = {}
|
|
||||||
}
|
|
||||||
return link_cache
|
|
||||||
}
|
|
||||||
|
|
||||||
Shop.save_links = function(links) {
|
|
||||||
link_cache = links
|
|
||||||
var cfg = { links: links }
|
|
||||||
var path = global_shop_path + '/link.toml'
|
|
||||||
fd.slurpwrite(path, utf8.encode(toml.encode(cfg)))
|
|
||||||
}
|
|
||||||
|
|
||||||
Shop.add_link = function(canonical, target) {
|
|
||||||
var links = Shop.load_links()
|
|
||||||
links[canonical] = target
|
|
||||||
Shop.save_links(links)
|
|
||||||
log.console("Linked " + canonical + " -> " + target)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
Shop.remove_link = function(canonical) {
|
|
||||||
var links = Shop.load_links()
|
|
||||||
if (!links[canonical]) return false
|
|
||||||
delete links[canonical]
|
|
||||||
Shop.save_links(links)
|
|
||||||
log.console("Unlinked " + canonical)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
Shop.clear_links = function() {
|
|
||||||
Shop.save_links({})
|
|
||||||
log.console("Cleared all links")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get information about how to resolve a package
|
// Get information about how to resolve a package
|
||||||
// Local packages always start with /
|
// Local packages always start with /
|
||||||
@@ -334,8 +298,8 @@ var open_dls = {}
|
|||||||
// for script forms, path is the canonical path of the module
|
// for script forms, path is the canonical path of the module
|
||||||
var script_form = function(path, script, pkg) {
|
var script_form = function(path, script, pkg) {
|
||||||
var pkg_arg = pkg ? `'${pkg}'` : 'null'
|
var pkg_arg = pkg ? `'${pkg}'` : 'null'
|
||||||
var relative_use_fn = `def use = function(path) { return globalThis.use(path, ${pkg_arg});}`
|
var relative_use_fn = `def PACKAGE = ${pkg_arg}; def use = function(path) { return globalThis.use(path, ${pkg_arg});}`
|
||||||
var fn = `(function setup_module($_, args){ ${relative_use_fn}; ${script}})`
|
var fn = `(function setup_module($_, args){ def arg = args; ${relative_use_fn}; ${script}})`
|
||||||
return fn
|
return fn
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,8 +308,10 @@ var script_form = function(path, script, pkg) {
|
|||||||
function resolve_mod_fn(path, pkg) {
|
function resolve_mod_fn(path, pkg) {
|
||||||
if (!fd.is_file(path)) throw new Error(`path ${path} is not a file`)
|
if (!fd.is_file(path)) throw new Error(`path ${path} is not a file`)
|
||||||
|
|
||||||
|
var file_info = Shop.file_info(path)
|
||||||
|
var file_pkg = file_info.package
|
||||||
var content = text(fd.slurp(path))
|
var content = text(fd.slurp(path))
|
||||||
var script = script_form(path, content, pkg);
|
var script = script_form(path, content, file_pkg);
|
||||||
|
|
||||||
var obj = pull_from_cache(utf8.encode(script))
|
var obj = pull_from_cache(utf8.encode(script))
|
||||||
if (obj) {
|
if (obj) {
|
||||||
@@ -415,24 +381,48 @@ function resolve_locator(path, ctx)
|
|||||||
|
|
||||||
// Generate symbol name for a C module file
|
// Generate symbol name for a C module file
|
||||||
// Uses the same format as Shop.c_symbol_for_file
|
// Uses the same format as Shop.c_symbol_for_file
|
||||||
|
// Resolves linked packages to their actual target first
|
||||||
function make_c_symbol(pkg, file) {
|
function make_c_symbol(pkg, file) {
|
||||||
var pkg_safe = pkg.replace(/\//g, '_').replace(/\./g, '_').replace(/-/g, '_')
|
// Check if this package is linked - if so, use the link target for symbol name
|
||||||
|
var link_target = link.get_target(pkg)
|
||||||
|
var resolved_pkg = link_target ? link_target : pkg
|
||||||
|
|
||||||
|
var pkg_safe = resolved_pkg.replace(/\//g, '_').replace(/\./g, '_').replace(/-/g, '_')
|
||||||
var file_safe = file.replace(/\//g, '_').replace(/\./g, '_').replace(/-/g, '_')
|
var file_safe = file.replace(/\//g, '_').replace(/\./g, '_').replace(/-/g, '_')
|
||||||
return 'js_' + pkg_safe + '_' + file_safe + '_use'
|
return 'js_' + pkg_safe + '_' + file_safe + '_use'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the library path for a package in .cell/lib
|
// Get the library path for a package in .cell/lib
|
||||||
|
// Resolves linked packages to their actual target first
|
||||||
function get_lib_path(pkg) {
|
function get_lib_path(pkg) {
|
||||||
var lib_name = pkg.replace(/\//g, '_').replace(/\./g, '_').replace(/-/g, '_')
|
// Check if this package is linked - if so, use the link target
|
||||||
|
var link_target = link.get_target(pkg)
|
||||||
|
var resolved_pkg = link_target ? link_target : pkg
|
||||||
|
|
||||||
|
var lib_name = resolved_pkg.replace(/\//g, '_').replace(/\./g, '_').replace(/-/g, '_')
|
||||||
return global_shop_path + '/lib/' + lib_name + dylib_ext
|
return global_shop_path + '/lib/' + lib_name + dylib_ext
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve a C symbol by searching:
|
||||||
|
// 1. If package_context is null, only check core internal symbols
|
||||||
|
// 2. Otherwise: own package (internal then dylib) -> other packages (internal then dylib) -> core (internal only)
|
||||||
|
// Core is never loaded as a dynamic library via dlopen
|
||||||
function resolve_c_symbol(path, package_context)
|
function resolve_c_symbol(path, package_context)
|
||||||
{
|
{
|
||||||
var static_only = cell.static_only
|
// If no package context, only check core internal symbols
|
||||||
|
if (!package_context) {
|
||||||
|
var core_sym = `js_${path}_use`
|
||||||
|
if (os.internal_exists(core_sym)) {
|
||||||
|
return {
|
||||||
|
symbol: function() { return os.load_internal(core_sym) },
|
||||||
|
scope: SCOPE_CORE,
|
||||||
|
path: core_sym
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
// 1. Check internal symbols (statically linked) in package context
|
// 1. Check own package first (internal, then dylib)
|
||||||
if (package_context) {
|
|
||||||
var sym = make_c_symbol(package_context, path)
|
var sym = make_c_symbol(package_context, path)
|
||||||
if (os.internal_exists(sym)) {
|
if (os.internal_exists(sym)) {
|
||||||
return {
|
return {
|
||||||
@@ -443,6 +433,7 @@ function resolve_c_symbol(path, package_context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
var dl_path = get_lib_path(package_context)
|
var dl_path = get_lib_path(package_context)
|
||||||
|
|
||||||
if (fd.is_file(dl_path)) {
|
if (fd.is_file(dl_path)) {
|
||||||
if (!open_dls[dl_path]) open_dls[dl_path] = os.dylib_open(dl_path)
|
if (!open_dls[dl_path]) open_dls[dl_path] = os.dylib_open(dl_path)
|
||||||
if (open_dls[dl_path] && os.dylib_has_symbol(open_dls[dl_path], sym)) {
|
if (open_dls[dl_path] && os.dylib_has_symbol(open_dls[dl_path], sym)) {
|
||||||
@@ -453,9 +444,8 @@ function resolve_c_symbol(path, package_context)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Check if valid package import (e.g. 'prosperon/sprite')
|
// 2. Check aliased package imports (e.g. 'prosperon/sprite')
|
||||||
var pkg_alias = get_import_package(path)
|
var pkg_alias = get_import_package(path)
|
||||||
if (pkg_alias) {
|
if (pkg_alias) {
|
||||||
var canon_pkg = get_aliased_package(path, package_context)
|
var canon_pkg = get_aliased_package(path, package_context)
|
||||||
@@ -463,6 +453,7 @@ function resolve_c_symbol(path, package_context)
|
|||||||
var mod_name = get_import_name(path)
|
var mod_name = get_import_name(path)
|
||||||
var sym = make_c_symbol(canon_pkg, mod_name)
|
var sym = make_c_symbol(canon_pkg, mod_name)
|
||||||
|
|
||||||
|
// Check internal first
|
||||||
if (os.internal_exists(sym)) {
|
if (os.internal_exists(sym)) {
|
||||||
return {
|
return {
|
||||||
symbol: function() { return os.load_internal(sym) },
|
symbol: function() { return os.load_internal(sym) },
|
||||||
@@ -472,6 +463,7 @@ function resolve_c_symbol(path, package_context)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Then check dylib
|
||||||
var dl_path = get_lib_path(canon_pkg)
|
var dl_path = get_lib_path(canon_pkg)
|
||||||
if (fd.is_file(dl_path)) {
|
if (fd.is_file(dl_path)) {
|
||||||
if (!open_dls[dl_path]) open_dls[dl_path] = os.dylib_open(dl_path)
|
if (!open_dls[dl_path]) open_dls[dl_path] = os.dylib_open(dl_path)
|
||||||
@@ -487,7 +479,7 @@ function resolve_c_symbol(path, package_context)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Check core fallback
|
// 3. Check core internal symbols (core is never a dynamic library)
|
||||||
var core_sym = `js_${path}_use`
|
var core_sym = `js_${path}_use`
|
||||||
if (os.internal_exists(core_sym)) {
|
if (os.internal_exists(core_sym)) {
|
||||||
return {
|
return {
|
||||||
@@ -497,21 +489,6 @@ function resolve_c_symbol(path, package_context)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try core dynamic library
|
|
||||||
if (!static_only) {
|
|
||||||
var core_dl = get_lib_path('core')
|
|
||||||
if (fd.is_file(core_dl)) {
|
|
||||||
if (!open_dls[core_dl]) open_dls[core_dl] = os.dylib_open(core_dl)
|
|
||||||
if (open_dls[core_dl] && os.dylib_has_symbol(open_dls[core_dl], core_sym)) {
|
|
||||||
return {
|
|
||||||
symbol: function() { return os.dylib_symbol(open_dls[core_dl], core_sym) },
|
|
||||||
scope: SCOPE_CORE,
|
|
||||||
path: core_sym
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -523,9 +500,27 @@ function resolve_module_info(path, package_context) {
|
|||||||
if (min_scope == 999)
|
if (min_scope == 999)
|
||||||
return null
|
return null
|
||||||
|
|
||||||
// Cache key is now package/file format
|
// Cache key is based on the realpath of the resolved file
|
||||||
// e.g., "core/shop", "gitea.pockle.world/john/prosperon/sprite"
|
// This ensures linked packages resolve to the same cache entry
|
||||||
|
// whether accessed via symlink or directly
|
||||||
var cache_key
|
var cache_key
|
||||||
|
if (mod_resolve.scope < 900 && mod_resolve.path) {
|
||||||
|
// Use realpath to resolve symlinks and get the actual file location
|
||||||
|
var real_path = fd.realpath(mod_resolve.path)
|
||||||
|
if (real_path) {
|
||||||
|
// Derive cache key from the real path's package info
|
||||||
|
var real_info = Shop.file_info(real_path)
|
||||||
|
if (real_info.package && real_info.name) {
|
||||||
|
cache_key = real_info.package + '/' + real_info.name
|
||||||
|
} else {
|
||||||
|
// Fallback to the realpath itself
|
||||||
|
cache_key = real_path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback for C-only modules or if realpath failed
|
||||||
|
if (!cache_key) {
|
||||||
if (min_scope == SCOPE_CORE) {
|
if (min_scope == SCOPE_CORE) {
|
||||||
cache_key = 'core/' + path
|
cache_key = 'core/' + path
|
||||||
} else if (min_scope == SCOPE_LOCAL && package_context) {
|
} else if (min_scope == SCOPE_LOCAL && package_context) {
|
||||||
@@ -547,6 +542,7 @@ function resolve_module_info(path, package_context) {
|
|||||||
} else {
|
} else {
|
||||||
cache_key = path
|
cache_key = path
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cache_key: cache_key,
|
cache_key: cache_key,
|
||||||
@@ -570,7 +566,6 @@ function execute_module(info)
|
|||||||
{
|
{
|
||||||
var c_resolve = info.c_resolve
|
var c_resolve = info.c_resolve
|
||||||
var mod_resolve = info.mod_resolve
|
var mod_resolve = info.mod_resolve
|
||||||
var cache_key = info.cache_key
|
|
||||||
|
|
||||||
var used
|
var used
|
||||||
|
|
||||||
@@ -607,7 +602,6 @@ Shop.use = function(path, package_context) {
|
|||||||
|
|
||||||
if (use_cache[info.cache_key])
|
if (use_cache[info.cache_key])
|
||||||
return use_cache[info.cache_key]
|
return use_cache[info.cache_key]
|
||||||
|
|
||||||
use_cache[info.cache_key] = execute_module(info)
|
use_cache[info.cache_key] = execute_module(info)
|
||||||
return use_cache[info.cache_key]
|
return use_cache[info.cache_key]
|
||||||
}
|
}
|
||||||
@@ -709,13 +703,23 @@ Shop.fetch = function(pkg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract: Extract a package to its target directory
|
// Extract: Extract a package to its target directory
|
||||||
// For local packages, creates a symlink
|
// For linked packages, creates a symlink to the link target
|
||||||
|
// For local packages, creates a symlink to the local path
|
||||||
// For remote packages, extracts from the provided zip blob
|
// For remote packages, extracts from the provided zip blob
|
||||||
// Returns true on success
|
// Returns true on success
|
||||||
Shop.extract = function(pkg) {
|
Shop.extract = function(pkg) {
|
||||||
var info = Shop.resolve_package_info(pkg)
|
|
||||||
var target_dir = get_package_abs_dir(pkg)
|
var target_dir = get_package_abs_dir(pkg)
|
||||||
|
|
||||||
|
// Check if this package is linked
|
||||||
|
var link_target = link.get_target(pkg)
|
||||||
|
if (link_target) {
|
||||||
|
// Use the link - create symlink to link target
|
||||||
|
link.sync_one(pkg, link_target)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var info = Shop.resolve_package_info(pkg)
|
||||||
|
|
||||||
if (info == 'local') {
|
if (info == 'local') {
|
||||||
if (fd.is_link(target_dir))
|
if (fd.is_link(target_dir))
|
||||||
fd.unlink(target_dir)
|
fd.unlink(target_dir)
|
||||||
@@ -831,11 +835,47 @@ Shop.remove = function(pkg) {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Shop.get = function(pkg) {
|
||||||
|
var lock = Shop.load_lock()
|
||||||
|
|
||||||
|
if (!lock[pkg]) {
|
||||||
|
var info = Shop.resolve_package_info(pkg)
|
||||||
|
if (!info) {
|
||||||
|
throw new Error("Invalid package: " + pkg)
|
||||||
|
}
|
||||||
|
|
||||||
|
var commit = null
|
||||||
|
if (info != 'local') {
|
||||||
|
commit = fetch_remote_hash(pkg)
|
||||||
|
if (!commit) {
|
||||||
|
throw new Error("Could not resolve commit for " + pkg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lock[pkg] = {
|
||||||
|
type: info,
|
||||||
|
commit: commit,
|
||||||
|
updated: time.number()
|
||||||
|
}
|
||||||
|
Shop.save_lock(lock)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Compile a module
|
// Compile a module
|
||||||
// List all files in a package
|
// List all files in a package
|
||||||
|
|
||||||
var debug = use('debug')
|
var debug = use('debug')
|
||||||
|
|
||||||
|
Shop.file_reload = function(file)
|
||||||
|
{
|
||||||
|
var info = Shop.file_info(file)
|
||||||
|
if (!info.is_module) return
|
||||||
|
|
||||||
|
var pkg = info.package
|
||||||
|
|
||||||
|
Shop.module_reload(info.name, pkg)
|
||||||
|
}
|
||||||
|
|
||||||
Shop.module_reload = function(path, package) {
|
Shop.module_reload = function(path, package) {
|
||||||
if (!Shop.is_loaded(path,package)) return
|
if (!Shop.is_loaded(path,package)) return
|
||||||
var info = resolve_module_info(path, package)
|
var info = resolve_module_info(path, package)
|
||||||
|
|||||||
@@ -900,7 +900,7 @@ static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val,
|
|||||||
int argc, JSValueConst *argv);
|
int argc, JSValueConst *argv);
|
||||||
|
|
||||||
static const JSClassExoticMethods js_string_exotic_methods;
|
static const JSClassExoticMethods js_string_exotic_methods;
|
||||||
static JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT;
|
JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT;
|
||||||
|
|
||||||
static void js_trigger_gc(JSRuntime *rt, size_t size)
|
static void js_trigger_gc(JSRuntime *rt, size_t size)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -496,6 +496,8 @@ JSClassID JS_GetClassID(JSValue v);
|
|||||||
int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def);
|
int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def);
|
||||||
int JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id);
|
int JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id);
|
||||||
|
|
||||||
|
extern JSClassID js_class_id_alloc;
|
||||||
|
|
||||||
/* value handling */
|
/* value handling */
|
||||||
|
|
||||||
static js_force_inline JSValue JS_NewBool(JSContext *ctx, JS_BOOL val)
|
static js_force_inline JSValue JS_NewBool(JSContext *ctx, JS_BOOL val)
|
||||||
|
|||||||
Reference in New Issue
Block a user