skip relink when content hasn't changed
This commit is contained in:
86
build.cm
86
build.cm
@@ -193,9 +193,34 @@ Build.build_package = function(pkg, target = Build.detect_host_target(), exclude
|
||||
// ============================================================================
|
||||
// Dynamic library building
|
||||
// ============================================================================
|
||||
|
||||
// Compute link key from all inputs that affect the dylib output
|
||||
function compute_link_key(objects, ldflags, target_ldflags, target, cc) {
|
||||
// Sort objects for deterministic hash
|
||||
var sorted_objects = objects.slice().sort()
|
||||
|
||||
// Build a string representing all link inputs
|
||||
var parts = []
|
||||
parts.push('target:' + target)
|
||||
parts.push('cc:' + cc)
|
||||
for (var i = 0; i < sorted_objects.length; i++) {
|
||||
// Object paths are content-addressed, so the path itself is the hash
|
||||
parts.push('obj:' + sorted_objects[i])
|
||||
}
|
||||
for (var i = 0; i < ldflags.length; i++) {
|
||||
parts.push('ldflag:' + ldflags[i])
|
||||
}
|
||||
for (var i = 0; i < target_ldflags.length; i++) {
|
||||
parts.push('target_ldflag:' + target_ldflags[i])
|
||||
}
|
||||
|
||||
return content_hash(parts.join('\n'))
|
||||
}
|
||||
|
||||
// Build a dynamic library for a package
|
||||
// Output goes to .cell/lib/<package_name>.<ext>
|
||||
// Dynamic libraries do NOT link against core; undefined symbols are resolved at dlopen time
|
||||
// Uses content-addressed store + symlink for caching
|
||||
Build.build_dynamic = function(pkg, target = Build.detect_host_target(), buildtype = 'release') {
|
||||
var objects = Build.build_package(pkg, target, true, buildtype) // exclude main.c
|
||||
|
||||
@@ -205,11 +230,13 @@ Build.build_dynamic = function(pkg, target = Build.detect_host_target(), buildty
|
||||
}
|
||||
|
||||
var lib_dir = shop.get_lib_dir()
|
||||
var store_dir = lib_dir + '/store'
|
||||
ensure_dir(lib_dir)
|
||||
ensure_dir(store_dir)
|
||||
|
||||
var lib_name = shop.lib_name_for_package(pkg)
|
||||
var dylib_ext = toolchains[target].system == 'windows' ? '.dll' : (toolchains[target].system == 'darwin' ? '.dylib' : '.so')
|
||||
var lib_path = lib_dir + '/' + lib_name + dylib_ext
|
||||
var stable_path = lib_dir + '/' + lib_name + dylib_ext
|
||||
|
||||
// Get link flags (with sigil replacement)
|
||||
var ldflags = replace_sigils_array(pkg_tools.get_flags(pkg, 'LDFLAGS', target))
|
||||
@@ -219,6 +246,37 @@ Build.build_dynamic = function(pkg, target = Build.detect_host_target(), buildty
|
||||
var local_dir = get_local_dir()
|
||||
var tc = toolchains[target]
|
||||
|
||||
// Resolve relative -L paths in ldflags for hash computation
|
||||
var resolved_ldflags = []
|
||||
for (var i = 0; i < ldflags.length; i++) {
|
||||
var flag = ldflags[i]
|
||||
if (flag.startsWith('-L') && !flag.startsWith('-L/')) {
|
||||
flag = '-L"' + pkg_dir + '/' + flag.substring(2) + '"'
|
||||
}
|
||||
resolved_ldflags.push(flag)
|
||||
}
|
||||
|
||||
// Compute link key
|
||||
var link_key = compute_link_key(objects, resolved_ldflags, target_ldflags, target, cc)
|
||||
var store_path = store_dir + '/' + lib_name + '-' + link_key + dylib_ext
|
||||
|
||||
// Check if already linked in store
|
||||
if (fd.is_file(store_path)) {
|
||||
// Ensure symlink points to the store file
|
||||
if (fd.is_link(stable_path)) {
|
||||
var current_target = fd.readlink(stable_path)
|
||||
if (current_target == store_path) {
|
||||
// Already up to date
|
||||
return stable_path
|
||||
}
|
||||
fd.unlink(stable_path)
|
||||
} else if (fd.is_file(stable_path)) {
|
||||
fd.unlink(stable_path)
|
||||
}
|
||||
fd.symlink(store_path, stable_path)
|
||||
return stable_path
|
||||
}
|
||||
|
||||
// Build link command
|
||||
var cmd_parts = [cc, '-shared', '-fPIC']
|
||||
|
||||
@@ -228,6 +286,8 @@ Build.build_dynamic = function(pkg, target = Build.detect_host_target(), buildty
|
||||
cmd_parts.push('-undefined', 'dynamic_lookup')
|
||||
// Dead-strip unused code
|
||||
cmd_parts.push('-Wl,-dead_strip')
|
||||
// Set install_name to stable path so runtime finds it correctly
|
||||
cmd_parts.push('-Wl,-install_name,' + stable_path)
|
||||
// rpath for .cell/local libraries
|
||||
cmd_parts.push('-Wl,-rpath,@loader_path/../local')
|
||||
cmd_parts.push('-Wl,-rpath,' + local_dir)
|
||||
@@ -253,30 +313,34 @@ Build.build_dynamic = function(pkg, target = Build.detect_host_target(), buildty
|
||||
|
||||
// Do NOT link against core library - symbols resolved at dlopen time
|
||||
|
||||
// Add LDFLAGS (resolve relative -L paths)
|
||||
for (var i = 0; i < ldflags.length; i++) {
|
||||
var flag = ldflags[i]
|
||||
if (flag.startsWith('-L') && !flag.startsWith('-L/')) {
|
||||
flag = '-L"' + pkg_dir + '/' + flag.substring(2) + '"'
|
||||
}
|
||||
cmd_parts.push(flag)
|
||||
// Add LDFLAGS
|
||||
for (var i = 0; i < resolved_ldflags.length; i++) {
|
||||
cmd_parts.push(resolved_ldflags[i])
|
||||
}
|
||||
|
||||
for (var i = 0; i < target_ldflags.length; i++) {
|
||||
cmd_parts.push(target_ldflags[i])
|
||||
}
|
||||
|
||||
cmd_parts.push('-o', '"' + lib_path + '"')
|
||||
cmd_parts.push('-o', '"' + store_path + '"')
|
||||
|
||||
var cmd_str = cmd_parts.join(' ')
|
||||
|
||||
log.console('Linking ' + lib_path)
|
||||
log.console('Linking ' + lib_name + dylib_ext)
|
||||
var ret = os.system(cmd_str)
|
||||
if (ret != 0) {
|
||||
throw new Error('Linking failed: ' + pkg)
|
||||
}
|
||||
|
||||
return lib_path
|
||||
// Update symlink to point to the new store file
|
||||
if (fd.is_link(stable_path)) {
|
||||
fd.unlink(stable_path)
|
||||
} else if (fd.is_file(stable_path)) {
|
||||
fd.unlink(stable_path)
|
||||
}
|
||||
fd.symlink(store_path, stable_path)
|
||||
|
||||
return stable_path
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user