fix building
This commit is contained in:
60
build.cm
60
build.cm
@@ -26,9 +26,10 @@ function get_local_dir() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Replace sigils in a string
|
// Replace sigils in a string
|
||||||
// Supports: $LOCAL -> .cell/local, $PACKAGE -> package dir (if provided)
|
// Supports: $LOCAL -> absolute path to .cell/local, $PACKAGE -> package dir (if provided)
|
||||||
function replace_sigils(str, pkg_dir) {
|
function replace_sigils(str, pkg_dir) {
|
||||||
var r = replace(str, '$LOCAL', get_local_dir())
|
var local = fd.realpath('.') + '/' + get_local_dir()
|
||||||
|
var r = replace(str, '$LOCAL', local)
|
||||||
if (pkg_dir) r = replace(r, '$PACKAGE', pkg_dir)
|
if (pkg_dir) r = replace(r, '$PACKAGE', pkg_dir)
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
@@ -138,6 +139,11 @@ Build.compile_file = function(pkg, file, target, opts) {
|
|||||||
push(cmd_parts, '-DCELL_USE_NAME=' + sym_name)
|
push(cmd_parts, '-DCELL_USE_NAME=' + sym_name)
|
||||||
push(cmd_parts, '-I"' + pkg_dir + '"')
|
push(cmd_parts, '-I"' + pkg_dir + '"')
|
||||||
|
|
||||||
|
// Auto-discover include/ directory
|
||||||
|
if (fd.is_dir(pkg_dir + '/include')) {
|
||||||
|
push(cmd_parts, '-I"' + pkg_dir + '/include"')
|
||||||
|
}
|
||||||
|
|
||||||
// External packages need core's source dir for cell.h, quickjs.h, blob.h
|
// External packages need core's source dir for cell.h, quickjs.h, blob.h
|
||||||
if (pkg != 'core') {
|
if (pkg != 'core') {
|
||||||
core_dir = shop.get_package_dir('core')
|
core_dir = shop.get_package_dir('core')
|
||||||
@@ -180,13 +186,32 @@ Build.compile_file = function(pkg, file, target, opts) {
|
|||||||
return obj_path
|
return obj_path
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile
|
// Compile — capture stderr to detect missing-header vs real errors
|
||||||
var full_cmd = cmd_str + ' -o "' + obj_path + '"'
|
var err_path = '/tmp/cell_build_err_' + hash + '.log'
|
||||||
|
var full_cmd = cmd_str + ' -o "' + obj_path + '" 2>"' + err_path + '"'
|
||||||
|
var err_text = null
|
||||||
|
var missing = null
|
||||||
|
var err_lines = null
|
||||||
|
var first_err = null
|
||||||
log.console('Compiling ' + file)
|
log.console('Compiling ' + file)
|
||||||
var ret = os.system(full_cmd)
|
var ret = os.system(full_cmd)
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
print('Compilation failed: ' + file)
|
if (fd.is_file(err_path)) {
|
||||||
print('Command: ' + full_cmd)
|
err_text = text(fd.slurp(err_path))
|
||||||
|
}
|
||||||
|
if (err_text) {
|
||||||
|
missing = search(err_text, /fatal error: [''].*[''] file not found/)
|
||||||
|
if (missing == null) missing = search(err_text, /fatal error: .*: No such file or directory/)
|
||||||
|
}
|
||||||
|
if (missing != null) {
|
||||||
|
err_lines = array(err_text, "\n")
|
||||||
|
first_err = length(err_lines) > 0 ? err_lines[0] : err_text
|
||||||
|
print(file + ': ' + first_err + ' (SDK not installed?)')
|
||||||
|
} else {
|
||||||
|
print('Compilation failed: ' + file)
|
||||||
|
if (err_text) print(err_text)
|
||||||
|
else print('Command: ' + full_cmd)
|
||||||
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,8 +378,6 @@ Build.build_dynamic = function(pkg, target, buildtype) {
|
|||||||
var pkg_dir = shop.get_package_dir(pkg)
|
var pkg_dir = shop.get_package_dir(pkg)
|
||||||
var cached_cflags = replace_sigils_array(pkg_tools.get_flags(pkg, 'CFLAGS', _target), pkg_dir)
|
var cached_cflags = replace_sigils_array(pkg_tools.get_flags(pkg, 'CFLAGS', _target), pkg_dir)
|
||||||
|
|
||||||
log.console(`CFLAGS ${pkg}: ${text(cached_cflags, '|')}`)
|
|
||||||
|
|
||||||
// Compile support sources to cached objects
|
// Compile support sources to cached objects
|
||||||
var sources = pkg_tools.get_sources(pkg)
|
var sources = pkg_tools.get_sources(pkg)
|
||||||
var support_objects = []
|
var support_objects = []
|
||||||
@@ -785,6 +808,9 @@ Build.build_all_dynamic = function(target, buildtype) {
|
|||||||
var packages = shop.list_packages()
|
var packages = shop.list_packages()
|
||||||
var results = []
|
var results = []
|
||||||
var core_mods = null
|
var core_mods = null
|
||||||
|
var total_files = 0
|
||||||
|
var total_ok = 0
|
||||||
|
var total_fail = 0
|
||||||
|
|
||||||
// Build core first
|
// Build core first
|
||||||
if (find(packages, function(p) { return p == 'core' }) != null) {
|
if (find(packages, function(p) { return p == 'core' }) != null) {
|
||||||
@@ -799,6 +825,24 @@ Build.build_all_dynamic = function(target, buildtype) {
|
|||||||
push(results, {package: pkg, modules: pkg_mods})
|
push(results, {package: pkg, modules: pkg_mods})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Print build report
|
||||||
|
print('\n--- Build Report ---')
|
||||||
|
arrfor(results, function(r) {
|
||||||
|
var pkg_dir = shop.get_package_dir(r.package)
|
||||||
|
var c_files = pkg_tools.get_c_files(r.package, _target, true)
|
||||||
|
var file_count = length(c_files)
|
||||||
|
var ok_count = length(r.modules)
|
||||||
|
var fail_count = file_count - ok_count
|
||||||
|
total_files = total_files + file_count
|
||||||
|
total_ok = total_ok + ok_count
|
||||||
|
total_fail = total_fail + fail_count
|
||||||
|
if (file_count == 0) return
|
||||||
|
var status = fail_count == 0 ? 'OK' : `${ok_count}/${file_count}`
|
||||||
|
print(` ${r.package}: ${status}`)
|
||||||
|
})
|
||||||
|
print(`Total: ${total_ok}/${total_files} compiled, ${total_fail} failed`)
|
||||||
|
print('--------------------\n')
|
||||||
|
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -255,6 +255,8 @@ If your package is at `/path/to/mypkg`, this becomes `-I/path/to/mypkg/sdk/publi
|
|||||||
|
|
||||||
Absolute paths are passed through unchanged.
|
Absolute paths are passed through unchanged.
|
||||||
|
|
||||||
|
The build system also auto-discovers `include/` directories — if your package has an `include/` directory, it is automatically added to the include path. No need to add `-I$PACKAGE/include` in cell.toml.
|
||||||
|
|
||||||
### Library paths
|
### Library paths
|
||||||
|
|
||||||
Relative `-L` paths work the same way:
|
Relative `-L` paths work the same way:
|
||||||
@@ -285,9 +287,13 @@ Available targets: `macos_arm64`, `macos_x86_64`, `linux`, `linux_arm64`, `windo
|
|||||||
|
|
||||||
### Sigils
|
### Sigils
|
||||||
|
|
||||||
Use `$LOCAL` in flags to refer to the `.cell/local` directory (for prebuilt libraries):
|
Use sigils in flags to refer to standard directories:
|
||||||
|
|
||||||
|
- `$LOCAL` — absolute path to `.cell/local` (for prebuilt libraries)
|
||||||
|
- `$PACKAGE` — absolute path to the package root
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
|
CFLAGS = "-I$PACKAGE/vendor/include"
|
||||||
LDFLAGS = "-L$LOCAL -lmyprebuilt"
|
LDFLAGS = "-L$LOCAL -lmyprebuilt"
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -495,3 +501,32 @@ static int module_state = 0;
|
|||||||
```
|
```
|
||||||
|
|
||||||
This prevents symbol conflicts between packages.
|
This prevents symbol conflicts between packages.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Missing header / SDK not installed
|
||||||
|
|
||||||
|
If a package wraps a third-party SDK that isn't installed on your system, the build will show:
|
||||||
|
|
||||||
|
```
|
||||||
|
module.c: fatal error: 'sdk/header.h' file not found (SDK not installed?)
|
||||||
|
```
|
||||||
|
|
||||||
|
Install the required SDK or skip that package. These warnings are harmless — other packages continue building normally.
|
||||||
|
|
||||||
|
### CFLAGS not applied
|
||||||
|
|
||||||
|
If your `cell.toml` has a `[compilation]` section but flags aren't being picked up, check:
|
||||||
|
|
||||||
|
1. The TOML syntax is valid (strings must be quoted)
|
||||||
|
2. The section header is exactly `[compilation]` (not `[compile]` etc.)
|
||||||
|
3. Target-specific sections use valid target names: `macos_arm64`, `macos_x86_64`, `linux`, `linux_arm64`, `windows`
|
||||||
|
|
||||||
|
### API changes from older versions
|
||||||
|
|
||||||
|
If C modules fail with errors about function signatures:
|
||||||
|
|
||||||
|
- `JS_IsArray` takes one argument (the value), not two — remove the context argument
|
||||||
|
- Use `JS_GetPropertyNumber` / `JS_SetPropertyNumber` instead of `JS_GetPropertyUint32` / `JS_SetPropertyUint32`
|
||||||
|
- Use `JS_NewString` instead of `JS_NewAtomString`
|
||||||
|
- There is no `undefined` — use `JS_IsNull` and `JS_NULL` only
|
||||||
|
|||||||
118
package.cm
118
package.cm
@@ -41,6 +41,64 @@ function get_path(name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
var config_cache = {}
|
var config_cache = {}
|
||||||
|
var compilation_cache = {}
|
||||||
|
|
||||||
|
// Fallback parser for [compilation] sections when toml.decode() is unreliable.
|
||||||
|
// Stores results as flat keys in compilation_cache: "key|CFLAGS", "key|target|CFLAGS"
|
||||||
|
function parse_compilation_into_cache(content, cache_key) {
|
||||||
|
var lines = array(content, "\n")
|
||||||
|
var current_section = null
|
||||||
|
var i = 0
|
||||||
|
var line = null
|
||||||
|
var trimmed = null
|
||||||
|
var eq_pos = null
|
||||||
|
var key = null
|
||||||
|
var val_part = null
|
||||||
|
var val = null
|
||||||
|
var sub = null
|
||||||
|
var flat_key = null
|
||||||
|
for (i = 0; i < length(lines); i++) {
|
||||||
|
line = lines[i]
|
||||||
|
trimmed = trim(line)
|
||||||
|
if (length(trimmed) == 0 || starts_with(trimmed, '#')) continue
|
||||||
|
|
||||||
|
// Detect section headers
|
||||||
|
if (starts_with(trimmed, '[compilation.') && ends_with(trimmed, ']')) {
|
||||||
|
sub = text(trimmed, 13, -1)
|
||||||
|
current_section = sub
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (trimmed == '[compilation]') {
|
||||||
|
current_section = '_base_'
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (starts_with(trimmed, '[')) {
|
||||||
|
current_section = null
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse KEY = "VALUE" in a compilation section
|
||||||
|
if (current_section == null) continue
|
||||||
|
eq_pos = search(trimmed, '=')
|
||||||
|
if (eq_pos == null) continue
|
||||||
|
key = trim(text(trimmed, 0, eq_pos))
|
||||||
|
val_part = trim(text(trimmed, eq_pos + 1))
|
||||||
|
// Strip surrounding quotes
|
||||||
|
if (starts_with(val_part, '"') && ends_with(val_part, '"')) {
|
||||||
|
val = text(val_part, 1, -1)
|
||||||
|
} else {
|
||||||
|
val = val_part
|
||||||
|
}
|
||||||
|
if (current_section == '_base_') {
|
||||||
|
flat_key = cache_key + '|' + key
|
||||||
|
} else {
|
||||||
|
flat_key = cache_key + '|' + current_section + '|' + key
|
||||||
|
}
|
||||||
|
compilation_cache[flat_key] = val
|
||||||
|
}
|
||||||
|
// Mark that we parsed this package
|
||||||
|
compilation_cache[cache_key] = true
|
||||||
|
}
|
||||||
|
|
||||||
package.load_config = function(name)
|
package.load_config = function(name)
|
||||||
{
|
{
|
||||||
@@ -63,25 +121,14 @@ package.load_config = function(name)
|
|||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate: if content has [compilation] but decode result doesn't, retry
|
// If the raw TOML text has [compilation] sections, always use
|
||||||
var has_compilation = search(content, '[compilation') != null
|
// the fallback line parser (the TOML decoder is unreliable for
|
||||||
var retry = 0
|
// nested [compilation.target] sub-tables).
|
||||||
var cf = null
|
// We store it alongside the result in a separate cache since
|
||||||
if (has_compilation && !result.compilation) {
|
// toml.decode returns frozen objects.
|
||||||
print(`TOML decode missing compilation for ${config_path}, retrying`)
|
var has_compilation = search(content, /\[compilation/) != null
|
||||||
while (retry < 3 && (!result || !result.compilation)) {
|
if (has_compilation) {
|
||||||
result = toml.decode(content)
|
parse_compilation_into_cache(content, cache_key)
|
||||||
retry = retry + 1
|
|
||||||
}
|
|
||||||
if (!result) return {}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (has_compilation && result.compilation) {
|
|
||||||
cf = result.compilation.CFLAGS
|
|
||||||
if (cf == null && search(content, 'CFLAGS') != null) {
|
|
||||||
print(`TOML has CFLAGS text but decode missing it for ${config_path}`)
|
|
||||||
print(`compilation keys: ${text(array(result.compilation), ',')}`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config_cache[cache_key] = result
|
config_cache[cache_key] = result
|
||||||
@@ -292,20 +339,33 @@ package.list_programs = function(name) {
|
|||||||
// Returns an array of flag strings
|
// Returns an array of flag strings
|
||||||
package.get_flags = function(name, flag_type, target) {
|
package.get_flags = function(name, flag_type, target) {
|
||||||
var config = package.load_config(name)
|
var config = package.load_config(name)
|
||||||
|
var cache_key = name || '_project_'
|
||||||
var flags = []
|
var flags = []
|
||||||
|
|
||||||
// Base flags
|
|
||||||
var base = null
|
var base = null
|
||||||
var target_flags = null
|
var target_flags = null
|
||||||
if (config.compilation && config.compilation[flag_type]) {
|
|
||||||
base = config.compilation[flag_type]
|
|
||||||
flags = array(flags, filter(array(base, /\s+/), function(f) { return length(f) > 0 }))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Target-specific flags
|
if (compilation_cache[cache_key]) {
|
||||||
if (target && config.compilation && config.compilation[target] && config.compilation[target][flag_type]) {
|
// Use flat cache: keys are "cache_key|FLAG_TYPE" and "cache_key|target|FLAG_TYPE"
|
||||||
target_flags = config.compilation[target][flag_type]
|
base = compilation_cache[cache_key + '|' + flag_type]
|
||||||
flags = array(flags, filter(array(target_flags, /\s+/), function(f) { return length(f) > 0 }))
|
if (base) {
|
||||||
|
flags = array(flags, filter(array(base, /\s+/), function(f) { return length(f) > 0 }))
|
||||||
|
}
|
||||||
|
if (target) {
|
||||||
|
target_flags = compilation_cache[cache_key + '|' + target + '|' + flag_type]
|
||||||
|
if (target_flags) {
|
||||||
|
flags = array(flags, filter(array(target_flags, /\s+/), function(f) { return length(f) > 0 }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (config.compilation) {
|
||||||
|
// Fall back to toml.decode() result
|
||||||
|
if (config.compilation[flag_type]) {
|
||||||
|
base = config.compilation[flag_type]
|
||||||
|
flags = array(flags, filter(array(base, /\s+/), function(f) { return length(f) > 0 }))
|
||||||
|
}
|
||||||
|
if (target && config.compilation[target] && config.compilation[target][flag_type]) {
|
||||||
|
target_flags = config.compilation[target][flag_type]
|
||||||
|
flags = array(flags, filter(array(target_flags, /\s+/), function(f) { return length(f) > 0 }))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return flags
|
return flags
|
||||||
|
|||||||
Reference in New Issue
Block a user