From 17e35f023fe895755aa1dee698b1704bc2eba0b7 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Mon, 16 Feb 2026 18:47:43 -0600 Subject: [PATCH] fix building C --- build.ce | 2 +- build.cm | 47 ++++++++++++++++++++++++++++++++++++----------- docs/c-modules.md | 14 ++++++++++++++ internal/shop.cm | 8 ++++---- package.cm | 18 +++++++++++++++++- 5 files changed, 72 insertions(+), 17 deletions(-) diff --git a/build.ce b/build.ce index 6c0ebeb0..842c0178 100644 --- a/build.ce +++ b/build.ce @@ -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') diff --git a/build.cm b/build.cm index 1164f90c..4205a9e8 100644 --- a/build.cm +++ b/build.cm @@ -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/..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//.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}) }) diff --git a/docs/c-modules.md b/docs/c-modules.md index ee675176..5e1631ef 100644 --- a/docs/c-modules.md +++ b/docs/c-modules.md @@ -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`: diff --git a/internal/shop.cm b/internal/shop.cm index 1c77f55c..f488a149 100644 --- a/internal/shop.cm +++ b/internal/shop.cm @@ -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//.dylib +// Get the deterministic dylib path for a module in lib//.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//.mach +// Get the deterministic mach path for a module in lib//.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 diff --git a/package.cm b/package.cm index 210c7abc..09bb96a0 100644 --- a/package.cm +++ b/package.cm @@ -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)