fix building C

This commit is contained in:
2026-02-16 18:47:43 -06:00
parent a1ee7dd458
commit 17e35f023f
5 changed files with 72 additions and 17 deletions

View File

@@ -106,7 +106,7 @@ if (target_package) {
_build = function() {
lib = build.build_dynamic(target_package, target, buildtype)
if (lib) {
log.console('Built: ' + lib)
log.console(`Built ${text(length(lib))} module(s)`)
}
} disruption {
log.error('Build failed')

View File

@@ -105,22 +105,23 @@ Build.compile_file = function(pkg, file, target, buildtype) {
var _buildtype = buildtype || 'release'
var pkg_dir = shop.get_package_dir(pkg)
var src_path = pkg_dir + '/' + file
var core_dir = null
if (!fd.is_file(src_path)) {
print('Source file not found: ' + src_path); disrupt
}
// Get flags (with sigil replacement)
var cflags = replace_sigils_array(pkg_tools.get_flags(pkg, 'CFLAGS', target))
var target_cflags = toolchains[target].c_args || []
var cc = toolchains[target].c
// Symbol name for this file
var sym_name = shop.c_symbol_for_file(pkg, file)
// Build command
var cmd_parts = [cc, '-c', '-fPIC']
// Add buildtype-specific flags
if (_buildtype == 'release') {
cmd_parts = array(cmd_parts, ['-O3', '-DNDEBUG'])
@@ -129,10 +130,16 @@ Build.compile_file = function(pkg, file, target, buildtype) {
} else if (_buildtype == 'minsize') {
cmd_parts = array(cmd_parts, ['-Os', '-DNDEBUG'])
}
push(cmd_parts, '-DCELL_USE_NAME=' + sym_name)
push(cmd_parts, '-I"' + pkg_dir + '"')
// External packages need core's source dir for cell.h, quickjs.h, blob.h
if (pkg != 'core') {
core_dir = shop.get_package_dir('core')
push(cmd_parts, '-I"' + core_dir + '/source"')
}
// Add package CFLAGS (resolve relative -I paths)
arrfor(cflags, function(flag) {
var f = flag
@@ -221,9 +228,11 @@ function compute_link_key(objects, ldflags, target_ldflags, opts) {
// Build a per-module dynamic library for a single C file
// Returns the content-addressed dylib path in .cell/build/<hash>.<target>.dylib
Build.build_module_dylib = function(pkg, file, target, buildtype) {
Build.build_module_dylib = function(pkg, file, target, opts) {
var _opts = opts || {}
var _target = target || Build.detect_host_target()
var _buildtype = buildtype || 'release'
var _buildtype = _opts.buildtype || 'release'
var _extra = _opts.extra_objects || []
var obj = Build.compile_file(pkg, file, _target, _buildtype)
var tc = toolchains[_target]
@@ -244,8 +253,10 @@ Build.build_module_dylib = function(pkg, file, target, buildtype) {
push(resolved_ldflags, f)
})
// Content-addressed output: hash of (object + link flags + target)
var link_key = compute_link_key([obj], resolved_ldflags, target_ldflags, {target: _target, cc: cc})
// Content-addressed output: hash of (all objects + link flags + target)
var all_objects = [obj]
all_objects = array(all_objects, _extra)
var link_key = compute_link_key(all_objects, resolved_ldflags, target_ldflags, {target: _target, cc: cc})
var build_dir = get_build_dir()
ensure_dir(build_dir)
var dylib_path = build_dir + '/' + link_key + '.' + _target + dylib_ext
@@ -275,6 +286,9 @@ Build.build_module_dylib = function(pkg, file, target, buildtype) {
push(cmd_parts, '-L"' + local_dir + '"')
push(cmd_parts, '"' + obj + '"')
arrfor(_extra, function(extra_obj) {
push(cmd_parts, '"' + extra_obj + '"')
})
cmd_parts = array(cmd_parts, resolved_ldflags)
cmd_parts = array(cmd_parts, target_ldflags)
push(cmd_parts, '-o')
@@ -288,7 +302,10 @@ Build.build_module_dylib = function(pkg, file, target, buildtype) {
}
// Install to deterministic lib/<pkg>/<stem>.dylib
// Strip .c/.cpp extension so the loader can find it by module name
var file_stem = file
if (ends_with(file_stem, '.cpp')) file_stem = text(file_stem, 0, -4)
else if (ends_with(file_stem, '.c')) file_stem = text(file_stem, 0, -2)
var install_dir = shop.get_lib_dir() + '/' + shop.lib_name_for_package(pkg)
var stem_dir = fd.dirname(file_stem)
if (stem_dir && stem_dir != '.') {
@@ -310,9 +327,17 @@ Build.build_dynamic = function(pkg, target, buildtype) {
var c_files = pkg_tools.get_c_files(pkg, _target, true)
var results = []
// Compile support sources to cached objects
var sources = pkg_tools.get_sources(pkg)
var support_objects = []
arrfor(sources, function(src_file) {
var obj = Build.compile_file(pkg, src_file, _target, _buildtype)
push(support_objects, obj)
})
arrfor(c_files, function(file) {
var sym_name = shop.c_symbol_for_file(pkg, file)
var dylib = Build.build_module_dylib(pkg, file, _target, _buildtype)
var dylib = Build.build_module_dylib(pkg, file, _target, {buildtype: _buildtype, extra_objects: support_objects})
push(results, {file: file, symbol: sym_name, dylib: dylib})
})

View File

@@ -244,6 +244,20 @@ audio_emscripten.c # Web/Emscripten
ƿit selects the appropriate file based on the target platform.
## Multi-File C Modules
If your module wraps a C library, place the library's source files in a `src/` directory. Files in `src/` are compiled as support objects and linked into your module's dylib — they are not treated as standalone modules.
```
mypackage/
rtree.c # module (exports js_mypackage_rtree_use)
src/
rtree.c # support file (linked into rtree.dylib)
rtree.h # header
```
The module file (`rtree.c`) includes the library header and uses `cell.h` as usual. The support files are plain C — they don't need any cell macros.
## Static Declarations
Keep internal functions and variables `static`:

View File

@@ -713,14 +713,14 @@ function make_c_symbol(pkg, file) {
return 'js_' + pkg_id + '_' + file_safe + '_use'
}
// Get the deterministic dylib path for a module in lib/<pkg>/<stem>.dylib
// Get the deterministic dylib path for a module in lib/<pkg_id>/<stem>.dylib
function get_dylib_path(pkg, stem) {
return global_shop_path + '/lib/' + safe_package_path(pkg) + '/' + stem + dylib_ext
return global_shop_path + '/lib/' + get_package_id(pkg) + '/' + stem + dylib_ext
}
// Get the deterministic mach path for a module in lib/<pkg>/<stem>.mach
// Get the deterministic mach path for a module in lib/<pkg_id>/<stem>.mach
function get_mach_path(pkg, stem) {
return global_shop_path + '/lib/' + safe_package_path(pkg) + '/' + stem + '.mach'
return global_shop_path + '/lib/' + get_package_id(pkg) + '/' + stem + '.mach'
}
// Open a per-module dylib and return the dlopen handle

View File

@@ -379,10 +379,26 @@ package.get_c_files = function(name, target, exclude_main) {
push(result, selected)
}
})
// Exclude src/ files (support files, not modules)
var sources = package.get_sources(name)
if (length(sources) > 0) {
result = filter(result, function(f) {
return find(sources, function(s) { return s == f }) == null
})
}
return result
}
// Get support source files: C files in src/ directories (not modules)
package.get_sources = function(name) {
var files = package.list_files(name)
return filter(files, function(f) {
return (ends_with(f, '.c') || ends_with(f, '.cpp')) && starts_with(f, 'src/')
})
}
// Get the absolute path for a package
package.get_dir = function(name) {
return get_path(name)