feat(pkg): phase 15 - a package can carry a native library
native "<target>" "<path>" in a package's package.ludic; the compiler records the libraries of every package a program imports and writes them into the IR (; ludic-native:), so ludicc -o, ludic build, ludic test and ludic bundle all link one list. macOS: an rpath to the package and to Contents/Frameworks, where ludic bundle copies and signs each library and drops the build machine's rpath. Windows: the import library, the .dll copied beside the exe (--natives-out for the bundle). tools/native/lib.sh builds from a pinned, checksummed source with clang on both machines; ludic.nativeecho is the worked example; the shim rules are in packages/README.md. Linked at build time rather than dlopen (docs/PACKAGES.md says why). Reseeded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
5df0747642
commit
5d3a799e33
25 changed files with 54474 additions and 51707 deletions
105
selfhost/frontend/natives.ludic
Normal file
105
selfhost/frontend/natives.ludic
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
# natives.ludic - a package can carry a native library (phase 15).
|
||||
#
|
||||
# A package's package.ludic says `native "<target>" "<path>"` for each library it links, per
|
||||
# target. When an import is found in a package, its manifest is read once and the libraries for
|
||||
# this build's target are kept, as absolute paths. ir_flush writes them into the IR as
|
||||
# `; ludic-native: <path>` lines, so every link - ludicc's own, `ludic build`, `ludic test` and
|
||||
# `ludic bundle` - reads one list, and a program that imports no such package links nothing new.
|
||||
var g_native_libs: []pointer = new []pointer
|
||||
var g_native_pkgs: []pointer = new []pointer
|
||||
|
||||
# the target a `native` line names: windows-x64, macos-arm64 or linux-x64.
|
||||
# $LUDIC_NATIVE_TARGET names another (an Intel Mac says macos-x64).
|
||||
function native_target() -> pointer {
|
||||
let env = getenv("LUDIC_NATIVE_TARGET")
|
||||
if (env != null) { return env }
|
||||
if g_target_win { return "windows-x64" }
|
||||
if Fs.is_dir("/System/Library") { return "macos-arm64" }
|
||||
return "linux-x64"
|
||||
}
|
||||
|
||||
# the words of one manifest line, a quoted word as one word without its quotes
|
||||
function native_words(line: pointer) -> []pointer {
|
||||
let out = new []pointer
|
||||
let n = len(line)
|
||||
var i = 0
|
||||
while i < n {
|
||||
while (i < n) and ((line[i] == ' ') or (line[i] == 9)) { i += 1 }
|
||||
if (i >= n) or (line[i] == '#') { return out }
|
||||
if line[i] == '"' {
|
||||
let s = i + 1
|
||||
i = s
|
||||
while (i < n) and (line[i] != '"') { i += 1 }
|
||||
push(out, line[s..i])
|
||||
i += 1
|
||||
} else {
|
||||
let s = i
|
||||
while (i < n) and (line[i] != ' ') and (line[i] != 9) { i += 1 }
|
||||
push(out, line[s..i])
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
function native_have(xs: []pointer, s: pointer) -> bool {
|
||||
var i = 0
|
||||
while i < len(xs) { if xs[i] == s { return true }; i += 1 }
|
||||
return false
|
||||
}
|
||||
|
||||
# `full` was found as `rel` under a package root (ludic_modules or $LUDIC_HOME/packages):
|
||||
# read that package's manifest once and keep its libraries for this target
|
||||
function native_note_pkg(full: pointer, rel: pointer) -> void {
|
||||
var n = 0
|
||||
while (n < len(rel)) and (rel[n] != '/') { n += 1 }
|
||||
let root = full[0..len(full) - len(rel) + n] + "/"
|
||||
if native_have(g_native_pkgs, root) { return }
|
||||
push(g_native_pkgs, root)
|
||||
let txt = read_file(root + "package.ludic")
|
||||
if (txt == null) { return }
|
||||
let want = native_target()
|
||||
var i = 0
|
||||
let tn = len(txt)
|
||||
while i < tn {
|
||||
var e = i
|
||||
while (e < tn) and (txt[e] != '\n') { e += 1 }
|
||||
var le = e
|
||||
if (le > i) and (txt[le - 1] == 13) { le -= 1 }
|
||||
let ws = native_words(txt[i..le])
|
||||
if (len(ws) == 3) and (ws[0] == "native") and (ws[1] == want) {
|
||||
let lib = root + ws[2]
|
||||
if not native_have(g_native_libs, lib) { push(g_native_libs, lib) }
|
||||
}
|
||||
i = e + 1
|
||||
}
|
||||
}
|
||||
|
||||
# the `; ludic-native:` lines ir_flush puts at the head of the IR
|
||||
function native_ir_lines() -> pointer {
|
||||
var s = ""
|
||||
var i = 0
|
||||
while i < len(g_native_libs) { s = s + `; ludic-native: {g_native_libs[i]}\n`; i += 1 }
|
||||
return s
|
||||
}
|
||||
|
||||
# what ludicc's own link adds: on macOS the library and an rpath to its directory (and to where
|
||||
# a bundle keeps it); on Windows the import library beside the .dll, the .dll copied beside the exe
|
||||
function native_link_args(out: pointer) -> pointer {
|
||||
var s = ""
|
||||
var i = 0
|
||||
while i < len(g_native_libs) {
|
||||
let lib = g_native_libs[i]
|
||||
let dir = dir_of(lib)
|
||||
if g_target_win {
|
||||
let n = len(lib)
|
||||
s = s + ` "{lib[0..n - 4]}.lib"`
|
||||
var od = dir_of(out)
|
||||
if len(od) == 0 { od = "." }
|
||||
run(`copy /y "{win_path(lib)}" "{win_path(od)}" >nul 2>nul`)
|
||||
} else {
|
||||
s = s + ` "{lib}" -Wl,-rpath,{dir} -Wl,-rpath,@executable_path/../Frameworks`
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
|
@ -1228,6 +1228,7 @@ function do_import(rel: pointer) -> void {
|
|||
if is_runtime_path(rel) or not beside or (beside and unsafe_trusted(g_parse_file)) { trust_file(full) }
|
||||
if beside and not is_runtime_path(rel) and not (module_of(g_parse_file) == "") { module_set(full, module_of(g_parse_file)) }
|
||||
if not beside and not is_runtime_path(rel) { pkg_file_set(full, pkg_name_of(rel)) } # modules.ludic
|
||||
if not beside and not is_runtime_path(rel) and (src != null) { native_note_pkg(full, rel) } # natives.ludic
|
||||
if beside and not is_runtime_path(rel) and not (pkg_of_file(g_parse_file) == "") { pkg_file_set(full, pkg_of_file(g_parse_file)) }
|
||||
if (src == null) { perr(`cannot open import {full}`) }
|
||||
# the audio runtime can arrive through atlas.ludic's own import or the Assets
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue