mirror of
https://github.com/justinian/jsix.git
synced 2025-12-09 16:04:32 -08:00
[build] Add build knowledge of dynamic libraries
Bonnibel will now build dynamic libraries when they're dependencies for non-statically linked modules. It will also copy those shared libraries into the initrd image for programs being copied into the image.
This commit is contained in:
@@ -1,5 +1,3 @@
|
|||||||
# This file is automatically generated by bonnibel
|
|
||||||
|
|
||||||
rule compile.c
|
rule compile.c
|
||||||
command = $cc -MMD -MF $out.d $cflags $ccflags -o $out -c $in
|
command = $cc -MMD -MF $out.d $cflags $ccflags -o $out -c $in
|
||||||
description = Compiling [$target]:$name
|
description = Compiling [$target]:$name
|
||||||
@@ -42,15 +40,19 @@ rule parse.cog
|
|||||||
|
|
||||||
rule exe
|
rule exe
|
||||||
command = $ld $ldflags -o $out $in $libs
|
command = $ld $ldflags -o $out $in $libs
|
||||||
description = Linking $name
|
description = Linking exe [$target]:$name
|
||||||
|
|
||||||
rule lib
|
|
||||||
command = $ar qcs $out $in
|
|
||||||
description = Archiving [$target]:$name
|
|
||||||
|
|
||||||
rule driver
|
rule driver
|
||||||
command = $ld $ldflags -shared -o $out $in $libs
|
command = $ld $ldflags -o $out $in $libs
|
||||||
description = Linking $name
|
description = Linking driver [$target]:$name
|
||||||
|
|
||||||
|
rule lib
|
||||||
|
command = $ld -shared $ldflags -o $out $in $libs
|
||||||
|
description = Linking [$target]:$name
|
||||||
|
|
||||||
|
rule lib_static
|
||||||
|
command = $ar qcs $out $in
|
||||||
|
description = Archiving [$target]:$name
|
||||||
|
|
||||||
rule cp
|
rule cp
|
||||||
command = cp $in $out
|
command = cp $in $out
|
||||||
|
|||||||
@@ -5,6 +5,6 @@ ccflags: [
|
|||||||
|
|
||||||
ldflags: [
|
ldflags: [
|
||||||
"-pie",
|
"-pie",
|
||||||
"--dynamic-linker", "/jsix/tools/ld.so",
|
"--dynamic-linker", "/jsix/lib/ld.so",
|
||||||
"-lc++", "-lc++abi", "-lunwind",
|
"--push-state", "--as-needed", "-Bstatic", "-lc++", "-lc++abi", "-lunwind", "--pop-state",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ ccflags: [
|
|||||||
"-U__linux__",
|
"-U__linux__",
|
||||||
|
|
||||||
"--sysroot='${source_root}/sysroot'",
|
"--sysroot='${source_root}/sysroot'",
|
||||||
|
"-fpic",
|
||||||
]
|
]
|
||||||
|
|
||||||
cxxflags: [
|
cxxflags: [
|
||||||
@@ -29,5 +30,4 @@ ldflags: [
|
|||||||
"-L", "${source_root}/sysroot/lib",
|
"-L", "${source_root}/sysroot/lib",
|
||||||
"-z", "separate-code",
|
"-z", "separate-code",
|
||||||
"--no-dependent-libraries",
|
"--no-dependent-libraries",
|
||||||
"-Bstatic",
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -12,5 +12,5 @@ services:
|
|||||||
drivers:
|
drivers:
|
||||||
- drv.uart
|
- drv.uart
|
||||||
- drv.uefi_fb
|
- drv.uefi_fb
|
||||||
tools:
|
libs:
|
||||||
- ld.so
|
- ld.so
|
||||||
1
external/zstd.module
vendored
1
external/zstd.module
vendored
@@ -5,7 +5,6 @@ zstd = module("zstd",
|
|||||||
kind = "lib",
|
kind = "lib",
|
||||||
copy_headers = True,
|
copy_headers = True,
|
||||||
deps = [ "libc" ],
|
deps = [ "libc" ],
|
||||||
output = "libzstd.a",
|
|
||||||
sources = [
|
sources = [
|
||||||
"decompress/zstd_decompress.c",
|
"decompress/zstd_decompress.c",
|
||||||
"decompress/zstd_ddict.c",
|
"decompress/zstd_ddict.c",
|
||||||
|
|||||||
@@ -37,8 +37,12 @@ class Manifest:
|
|||||||
self.drivers = [self.__build_entry(modules, i)
|
self.drivers = [self.__build_entry(modules, i)
|
||||||
for i in config.get("drivers", tuple())]
|
for i in config.get("drivers", tuple())]
|
||||||
|
|
||||||
self.tools = [self.__build_entry(modules, i)
|
libs = set(config.get("libs", tuple()))
|
||||||
for i in config.get("tools", tuple())]
|
libs.update(self.__libdeps([modules[e.module] for e in self.services]))
|
||||||
|
libs.update(self.__libdeps([modules[e.module] for e in self.drivers]))
|
||||||
|
|
||||||
|
self.libs = [self.__build_entry(modules, i)
|
||||||
|
for i in libs]
|
||||||
|
|
||||||
self.flags = config.get("flags", tuple())
|
self.flags = config.get("flags", tuple())
|
||||||
|
|
||||||
@@ -74,7 +78,14 @@ class Manifest:
|
|||||||
if not f in Manifest.flags:
|
if not f in Manifest.flags:
|
||||||
raise BonnibelError(f"Manifest specifies unknown flag '{f}'")
|
raise BonnibelError(f"Manifest specifies unknown flag '{f}'")
|
||||||
|
|
||||||
return Manifest.Entry(name, target, mod.output, flags)
|
return Manifest.Entry(name, target, mod.get_output(), flags)
|
||||||
|
|
||||||
|
def __libdeps(self, modules):
|
||||||
|
deps = set([m.name for m in modules if m.kind == "lib"])
|
||||||
|
for m in modules:
|
||||||
|
if m.static: continue
|
||||||
|
deps.update(self.__libdeps(m.depmods))
|
||||||
|
return deps
|
||||||
|
|
||||||
def add_data(self, output, desc, flags=tuple()):
|
def add_data(self, output, desc, flags=tuple()):
|
||||||
e = Manifest.Entry(None, None, output, flags)
|
e = Manifest.Entry(None, None, output, flags)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from . import include_rel, mod_rel, target_rel
|
from . import include_rel, mod_rel, target_rel
|
||||||
|
from . import BonnibelError
|
||||||
|
|
||||||
def resolve(path):
|
def resolve(path):
|
||||||
if path.startswith('/') or path.startswith('$'):
|
if path.startswith('/') or path.startswith('$'):
|
||||||
@@ -17,17 +18,27 @@ class BuildOptions:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def implicit(self):
|
def implicit(self):
|
||||||
|
libfiles = list(map(lambda x: x[0], self.libs))
|
||||||
if self.ld_script is not None:
|
if self.ld_script is not None:
|
||||||
return self.libs + [self.ld_script]
|
return libfiles + [self.ld_script]
|
||||||
else:
|
else:
|
||||||
return self.libs
|
return libfiles
|
||||||
|
|
||||||
|
@property
|
||||||
|
def linker_args(self):
|
||||||
|
from pathlib import Path
|
||||||
|
def arg(path, static):
|
||||||
|
if static: return path
|
||||||
|
return "-l:" + Path(path).name
|
||||||
|
return [arg(*l) for l in self.libs]
|
||||||
|
|
||||||
|
|
||||||
class Module:
|
class Module:
|
||||||
__fields = {
|
__fields = {
|
||||||
# name: (type, default)
|
# name: (type, default)
|
||||||
"kind": (str, "exe"),
|
"kind": (str, "exe"),
|
||||||
"output": (str, None),
|
"outfile": (str, None),
|
||||||
|
"basename": (str, None),
|
||||||
"targets": (set, ()),
|
"targets": (set, ()),
|
||||||
"deps": (set, ()),
|
"deps": (set, ()),
|
||||||
"public_headers": (set, ()),
|
"public_headers": (set, ()),
|
||||||
@@ -41,6 +52,7 @@ class Module:
|
|||||||
"description": (str, None),
|
"description": (str, None),
|
||||||
"no_libc": (bool, False),
|
"no_libc": (bool, False),
|
||||||
"ld_script": (str, None),
|
"ld_script": (str, None),
|
||||||
|
"static": (bool, False),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, name, modfile, root, **kwargs):
|
def __init__(self, name, modfile, root, **kwargs):
|
||||||
@@ -62,7 +74,7 @@ class Module:
|
|||||||
|
|
||||||
for name in kwargs:
|
for name in kwargs:
|
||||||
if not name in self.__fields:
|
if not name in self.__fields:
|
||||||
raise AttributeError(f"No attribute named {name} on Module")
|
raise AttributeError(f"No attribute named {name} on Module ({modfile})")
|
||||||
|
|
||||||
if not self.no_libc:
|
if not self.no_libc:
|
||||||
self.deps.add("libc_free")
|
self.deps.add("libc_free")
|
||||||
@@ -78,24 +90,30 @@ class Module:
|
|||||||
# Filled by Module.update
|
# Filled by Module.update
|
||||||
self.depmods = set()
|
self.depmods = set()
|
||||||
|
|
||||||
def __str__(self):
|
def __repr__(self):
|
||||||
return "Module {} {}\n\t{}".format(self.kind, self.name, "\n\t".join(map(str, self.sources)))
|
return f"<Module {self.kind} {self.name}>"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def output(self):
|
def basename(self):
|
||||||
if self.__output is not None:
|
if self.__basename is not None:
|
||||||
return self.__output
|
return self.__basename
|
||||||
|
|
||||||
if self.kind == "lib":
|
if self.kind == "lib":
|
||||||
return f"lib{self.name}.a"
|
return f"lib{self.name}"
|
||||||
elif self.kind == "driver":
|
|
||||||
return f"{self.name}.drv"
|
|
||||||
else:
|
else:
|
||||||
return f"{self.name}.elf"
|
return self.name
|
||||||
|
|
||||||
@output.setter
|
@basename.setter
|
||||||
def output(self, value):
|
def basename(self, value):
|
||||||
self.__output = value
|
self.__basename = value
|
||||||
|
|
||||||
|
def get_output(self, static=False):
|
||||||
|
if self.outfile is not None:
|
||||||
|
return self.outfile
|
||||||
|
elif self.kind == "headers":
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
ext = dict(exe=".elf", driver=".drv", lib=(static and ".a" or ".so"))
|
||||||
|
return self.basename + ext.get(self.kind, "")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def update(cls, mods):
|
def update(cls, mods):
|
||||||
@@ -128,21 +146,20 @@ class Module:
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from ninja.ninja_syntax import Writer
|
from ninja.ninja_syntax import Writer
|
||||||
|
|
||||||
def walk_deps(deps):
|
def walk_deps(deps, static, results):
|
||||||
open_set = set(deps)
|
for mod in deps:
|
||||||
closed_set = set()
|
if static or mod.name not in results:
|
||||||
while open_set:
|
results[mod.name] = (mod, static)
|
||||||
dep = open_set.pop()
|
walk_deps(mod.depmods, static or mod.static, results)
|
||||||
closed_set.add(dep)
|
|
||||||
open_set |= {m for m in dep.depmods if not m in closed_set}
|
|
||||||
return closed_set
|
|
||||||
|
|
||||||
all_deps = walk_deps(self.depmods)
|
all_deps = {}
|
||||||
|
walk_deps(self.depmods, self.static, all_deps)
|
||||||
|
all_deps = all_deps.values()
|
||||||
|
|
||||||
def gather_phony(build, deps, child_rel):
|
def gather_phony(build, deps, child_rel):
|
||||||
phony = ".headers.phony"
|
phony = ".headers.phony"
|
||||||
child_phony = [child_rel(phony, module=c.name)
|
child_phony = [child_rel(phony, module=c.name)
|
||||||
for c in all_deps]
|
for c, _ in all_deps]
|
||||||
|
|
||||||
build.build(
|
build.build(
|
||||||
rule = "touch",
|
rule = "touch",
|
||||||
@@ -192,7 +209,7 @@ class Module:
|
|||||||
modopts.includes.append(str(incpath))
|
modopts.includes.append(str(incpath))
|
||||||
modopts.includes.append(destpath)
|
modopts.includes.append(destpath)
|
||||||
|
|
||||||
for dep in all_deps:
|
for dep, static in all_deps:
|
||||||
if dep.public_headers:
|
if dep.public_headers:
|
||||||
if dep.include_phase == "normal":
|
if dep.include_phase == "normal":
|
||||||
modopts.includes += [dep.root / "include", f"${{target_dir}}/{dep.name}.dir/include"]
|
modopts.includes += [dep.root / "include", f"${{target_dir}}/{dep.name}.dir/include"]
|
||||||
@@ -202,10 +219,12 @@ class Module:
|
|||||||
from . import BonnibelError
|
from . import BonnibelError
|
||||||
raise BonnibelError(f"Module {dep.name} has invalid include_phase={dep.include_phase}")
|
raise BonnibelError(f"Module {dep.name} has invalid include_phase={dep.include_phase}")
|
||||||
|
|
||||||
if dep.kind == "lib":
|
if dep.kind == "headers":
|
||||||
modopts.libs.append(target_rel(dep.output))
|
continue
|
||||||
|
elif dep.kind == "lib":
|
||||||
|
modopts.libs.append((target_rel(dep.get_output(static)), static))
|
||||||
else:
|
else:
|
||||||
modopts.order_only.append(target_rel(dep.output))
|
modopts.order_only.append(target_rel(dep.get_output(static)))
|
||||||
|
|
||||||
cc_includes = []
|
cc_includes = []
|
||||||
if modopts.local:
|
if modopts.local:
|
||||||
@@ -225,7 +244,7 @@ class Module:
|
|||||||
build.variable("asflags", ["${asflags}"] + as_includes)
|
build.variable("asflags", ["${asflags}"] + as_includes)
|
||||||
|
|
||||||
if modopts.libs:
|
if modopts.libs:
|
||||||
build.variable("libs", ["${libs}"] + modopts.libs)
|
build.variable("libs", ["-L${target_dir}", "${libs}"] + modopts.linker_args)
|
||||||
|
|
||||||
if modopts.ld_script:
|
if modopts.ld_script:
|
||||||
build.variable("ldflags", ["${ldflags}"] + ["-T", modopts.ld_script])
|
build.variable("ldflags", ["${ldflags}"] + ["-T", modopts.ld_script])
|
||||||
@@ -267,7 +286,11 @@ class Module:
|
|||||||
|
|
||||||
gather_phony(build, header_deps, target_rel)
|
gather_phony(build, header_deps, target_rel)
|
||||||
|
|
||||||
output = target_rel(self.output)
|
if self.kind == "headers":
|
||||||
|
# Header-only, don't output a build rule
|
||||||
|
return
|
||||||
|
|
||||||
|
output = target_rel(self.get_output())
|
||||||
build.newline()
|
build.newline()
|
||||||
build.build(
|
build.build(
|
||||||
rule = self.kind,
|
rule = self.kind,
|
||||||
@@ -275,6 +298,7 @@ class Module:
|
|||||||
inputs = inputs,
|
inputs = inputs,
|
||||||
implicit = modopts.implicit,
|
implicit = modopts.implicit,
|
||||||
order_only = modopts.order_only,
|
order_only = modopts.order_only,
|
||||||
|
variables = {"name": self.name},
|
||||||
)
|
)
|
||||||
|
|
||||||
dump = output + ".dump"
|
dump = output + ".dump"
|
||||||
@@ -286,6 +310,26 @@ class Module:
|
|||||||
variables = {"name": self.name},
|
variables = {"name": self.name},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
s_output = target_rel(self.get_output(static=True))
|
||||||
|
if s_output != output:
|
||||||
|
build.newline()
|
||||||
|
build.build(
|
||||||
|
rule = self.kind + "_static",
|
||||||
|
outputs = s_output,
|
||||||
|
inputs = inputs,
|
||||||
|
order_only = modopts.order_only,
|
||||||
|
variables = {"name": self.name},
|
||||||
|
)
|
||||||
|
|
||||||
|
dump = s_output + ".dump"
|
||||||
|
build.newline()
|
||||||
|
build.build(
|
||||||
|
rule = "dump",
|
||||||
|
outputs = dump,
|
||||||
|
inputs = s_output,
|
||||||
|
variables = {"name": self.name},
|
||||||
|
)
|
||||||
|
|
||||||
if self.default:
|
if self.default:
|
||||||
build.newline()
|
build.newline()
|
||||||
build.default(output)
|
build.default(output)
|
||||||
|
|||||||
@@ -154,8 +154,8 @@ class Project:
|
|||||||
for program in manifest.drivers:
|
for program in manifest.drivers:
|
||||||
add_initrd_stripped("jsix/drivers", program)
|
add_initrd_stripped("jsix/drivers", program)
|
||||||
|
|
||||||
for program in manifest.tools:
|
for program in manifest.libs:
|
||||||
add_initrd_stripped("jsix/tools", program)
|
add_initrd_stripped("jsix/lib", program)
|
||||||
|
|
||||||
syms = manifest.add_data("symbol_table.dat",
|
syms = manifest.add_data("symbol_table.dat",
|
||||||
"Symbol table", ("symbols",))
|
"Symbol table", ("symbols",))
|
||||||
@@ -166,7 +166,7 @@ class Project:
|
|||||||
build.build(
|
build.build(
|
||||||
rule = "makest",
|
rule = "makest",
|
||||||
outputs = [syms_out],
|
outputs = [syms_out],
|
||||||
inputs = [f"${{build_root}}/kernel/{modules['kernel'].output}"],
|
inputs = [f"${{build_root}}/kernel/{modules['kernel'].get_output(static=True)}"],
|
||||||
)
|
)
|
||||||
fatroot_content.append(syms_out)
|
fatroot_content.append(syms_out)
|
||||||
manifest.symbols = syms_file
|
manifest.symbols = syms_file
|
||||||
|
|||||||
@@ -2,9 +2,10 @@
|
|||||||
|
|
||||||
boot = module("boot",
|
boot = module("boot",
|
||||||
kind = "exe",
|
kind = "exe",
|
||||||
output = "boot.efi",
|
outfile = "boot.efi",
|
||||||
targets = [ "boot" ],
|
targets = [ "boot" ],
|
||||||
deps = [ "cpu", "elf", "util", "bootproto" ],
|
deps = [ "cpu", "elf", "util", "bootproto" ],
|
||||||
|
static = True,
|
||||||
sources = [
|
sources = [
|
||||||
"allocator.cpp",
|
"allocator.cpp",
|
||||||
"bootconfig.cpp",
|
"bootconfig.cpp",
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
# vim: ft=python
|
# vim: ft=python
|
||||||
|
|
||||||
kernel = module("kernel",
|
kernel = module("kernel",
|
||||||
kind = "exe",
|
|
||||||
default = True,
|
default = True,
|
||||||
output = "jsix.elf",
|
basename = "jsix",
|
||||||
targets = [ "kernel" ],
|
targets = [ "kernel" ],
|
||||||
description = "jsix kernel",
|
description = "jsix kernel",
|
||||||
deps = [ "util", "cpu", "bootproto", "j6" ],
|
deps = [ "util", "cpu", "bootproto", "j6" ],
|
||||||
|
static = True,
|
||||||
ld_script = "kernel.ld",
|
ld_script = "kernel.ld",
|
||||||
sources = [
|
sources = [
|
||||||
"apic.cpp",
|
"apic.cpp",
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# vim: ft=python
|
# vim: ft=python
|
||||||
|
|
||||||
panic = module("panic.serial",
|
panic = module("panic.serial",
|
||||||
kind = "exe",
|
|
||||||
targets = [ "kernel" ],
|
targets = [ "kernel" ],
|
||||||
deps = [ "util", "elf", "kernel" ],
|
deps = [ "util", "elf", "kernel" ],
|
||||||
|
static = True,
|
||||||
includes = [ ".." ],
|
includes = [ ".." ],
|
||||||
description = "Serial panic handler",
|
description = "Serial panic handler",
|
||||||
ld_script = "panic.serial.ld",
|
ld_script = "panic.serial.ld",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# vim: ft=python
|
# vim: ft=python
|
||||||
|
|
||||||
bp = module("bootproto",
|
bp = module("bootproto",
|
||||||
kind = "lib",
|
kind = "headers",
|
||||||
public_headers = [
|
public_headers = [
|
||||||
"bootproto/acpi.h",
|
"bootproto/acpi.h",
|
||||||
"bootproto/bootconfig.h",
|
"bootproto/bootconfig.h",
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
# vim: ft=python
|
# vim: ft=python
|
||||||
|
|
||||||
libc_free = module("libc_free",
|
libc_free = module("libc_free",
|
||||||
kind = "lib",
|
kind = "headers",
|
||||||
|
basename = "libc_free",
|
||||||
no_libc = True,
|
no_libc = True,
|
||||||
include_phase = "late",
|
include_phase = "late",
|
||||||
public_headers = [
|
public_headers = [
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
# vim: ft=python
|
# vim: ft=python
|
||||||
|
|
||||||
ldso = module("ld.so",
|
ldso = module("ld.so",
|
||||||
kind = "shared",
|
kind = "lib",
|
||||||
output = "ld.so",
|
static = True,
|
||||||
|
basename = "ld",
|
||||||
targets = [ "user" ],
|
targets = [ "user" ],
|
||||||
deps = [ "libc", "util", "elf" ],
|
deps = [ "libc", "util", "elf" ],
|
||||||
description = "Dynamic Linker",
|
description = "Dynamic Linker",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
init = module("srv.init",
|
init = module("srv.init",
|
||||||
targets = [ "init" ],
|
targets = [ "init" ],
|
||||||
deps = [ "libc", "elf", "bootproto", "zstd" ],
|
deps = [ "libc", "elf", "bootproto", "zstd" ],
|
||||||
|
static = True,
|
||||||
description = "Init server",
|
description = "Init server",
|
||||||
ld_script = "init.ld",
|
ld_script = "init.ld",
|
||||||
sources = [
|
sources = [
|
||||||
|
|||||||
Reference in New Issue
Block a user