commit e8b4e9c992a28ccf1d8e98baca3afe781d3e072e
parent de82b496b581e60f9de25c414b7c05bd7cdbdf44
Author: Drew DeVault <sir@cmpwn.com>
Date: Tue, 7 Jun 2022 23:19:37 +0200
Mostly implement cross-compiling
The main thing which is still an issue here is that the Hare cache needs
to be broken if the tags don't match up.
Signed-off-by: Drew DeVault <sir@cmpwn.com>
Diffstat:
9 files changed, 173 insertions(+), 44 deletions(-)
diff --git a/Makefile b/Makefile
@@ -28,7 +28,8 @@ hare_srcs = \
./cmd/hare/progress.ha \
./cmd/hare/release.ha \
./cmd/hare/schedule.ha \
- ./cmd/hare/subcmds.ha
+ ./cmd/hare/subcmds.ha \
+ ./cmd/hare/target.ha
harec_srcs = \
./cmd/harec/main.ha \
@@ -43,21 +44,17 @@ haredoc_srcs = \
./cmd/haredoc/sort.ha \
./cmd/haredoc/resolver.ha
+include targets.mk
+
$(HARECACHE)/hare.ssa: $(hare_srcs) $(stdlib_deps_any) $(stdlib_deps_$(PLATFORM)) scripts/version
@printf 'HAREC\t%s\n' "$@"
@HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) \
- -D PLATFORM:str='"'"$(PLATFORM)"'"' \
- -D VERSION:str='"'"$$(./scripts/version)"'"' \
- -D HAREPATH:str='"'"$(HAREPATH)"'"' \
- -o $@ $(hare_srcs)
+ $(HARE_DEFINES) -o $@ $(hare_srcs)
$(TESTCACHE)/hare.ssa: $(hare_srcs) $(testlib_deps_any) $(testlib_deps_$(PLATFORM)) scripts/version
@printf 'HAREC\t%s\n' "$@"
@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) \
- -D PLATFORM:str='"'"$(PLATFORM)"'"' \
- -D VERSION:str='"'"$$(./scripts/version)"'"' \
- -D HAREPATH:str='"'"$(HAREPATH)"'"' \
- -o $@ $(hare_srcs)
+ $(HARE_DEFINES) -o $@ $(hare_srcs)
$(BINOUT)/hare: $(HARECACHE)/hare.o
@mkdir -p $(BINOUT)
@@ -74,15 +71,14 @@ $(BINOUT)/hare-tests: $(TESTCACHE)/hare.o
$(BINOUT)/harec2: $(BINOUT)/hare $(harec_srcs)
@mkdir -p $(BINOUT)
@printf 'HARE\t%s\n' "$@"
- @env HAREPATH=. HAREC=$(HAREC) QBE=$(QBE) $(BINOUT)/hare build -o $(BINOUT)/harec2 cmd/harec
+ @env HAREPATH=. HAREC=$(HAREC) QBE=$(QBE) $(BINOUT)/hare build \
+ $(HARE_DEFINES) -o $(BINOUT)/harec2 cmd/harec
$(BINOUT)/haredoc: $(BINOUT)/hare $(haredoc_srcs)
@mkdir -p $(BINOUT)
@printf 'HARE\t%s\n' "$@"
@env HAREPATH=. HAREC=$(HAREC) QBE=$(QBE) $(BINOUT)/hare build \
- -D PLATFORM:str='"'"$(PLATFORM)"'"' \
- -D HAREPATH:str='"'"$(HAREPATH)"'"' \
- -o $(BINOUT)/haredoc ./cmd/haredoc
+ $(HARE_DEFINES) -o $(BINOUT)/haredoc ./cmd/haredoc
docs/html: $(BINOUT)/haredoc scripts/gen-docs
./scripts/gen-docs
diff --git a/cmd/hare/plan.ha b/cmd/hare/plan.ha
@@ -50,6 +50,7 @@ type modcache = struct {
type plan = struct {
context: *module::context,
+ target: *target,
workdir: str,
counter: uint,
scheduled: []*task,
@@ -69,7 +70,11 @@ type plan_progress = struct {
maxwidth: size,
};
-fn mkplan(ctx: *module::context, libs: []str) plan = {
+fn mkplan(
+ ctx: *module::context,
+ libs: []str,
+ target: *target,
+) plan = {
const rtdir = match (module::lookup(ctx, ["rt"])) {
case let err: module::error =>
fmt::fatal("Error resolving rt:", module::strerror(err));
@@ -99,6 +104,7 @@ fn mkplan(ctx: *module::context, libs: []str) plan = {
return plan {
context = ctx,
+ target = target,
workdir = os::tryenv("HARE_DEBUG_WORKDIR", temp::dir()),
script = path::allocate(&buf),
environ = alloc([
diff --git a/cmd/hare/schedule.ha b/cmd/hare/schedule.ha
@@ -66,14 +66,19 @@ fn sched_module(plan: *plan, ident: ast::ident, link: *[]*task) *task = {
// Schedules a task which compiles objects into an executable.
fn sched_ld(plan: *plan, output: str, depend: *task...) *task = {
+ const ld_cmd = os::tryenv("LD", if (len(plan.libs) > 0) {
+ // C compiler is used as linker if we -l something
+ yield plan.target.cc_cmd;
+ } else {
+ yield plan.target.ld_cmd;
+ });
+
let task = alloc(task {
status = status::SCHEDULED,
output = output,
depend = alloc(depend...),
cmd = alloc([
- // C compiler is used as linker if we -l something
- os::tryenv("LD",
- if (len(plan.libs) > 0) "cc" else "ld"),
+ ld_cmd,
"-T", plan.script,
"-o", output,
]),
@@ -105,13 +110,12 @@ fn sched_ld(plan: *plan, output: str, depend: *task...) *task = {
// Schedules a task which merges objects into an archive.
fn sched_ar(plan: *plan, output: str, depend: *task...) *task = {
+ const ar_cmd = os::tryenv("AR", plan.target.ar_cmd);
let task = alloc(task {
status = status::SCHEDULED,
output = output,
depend = alloc(depend...),
- cmd = alloc([
- os::tryenv("AR", "ar"), "-csr", output,
- ]),
+ cmd = alloc([ar_cmd, "-csr", output]),
module = void,
});
for (let i = 0z; i < len(depend); i += 1) {
@@ -124,13 +128,12 @@ fn sched_ar(plan: *plan, output: str, depend: *task...) *task = {
// Schedules a task which compiles assembly into an object.
fn sched_as(plan: *plan, output: str, input: str, depend: *task...) *task = {
+ const as_cmd = os::tryenv("AS", plan.target.as_cmd);
let task = alloc(task {
status = status::SCHEDULED,
output = output,
depend = alloc(depend...),
- cmd = alloc([
- os::tryenv("AS", "as"), "-g", "-o", output, input,
- ]),
+ cmd = alloc([as_cmd, "-g", "-o", output, input]),
module = void,
});
append(plan.scheduled, task);
@@ -139,12 +142,16 @@ fn sched_as(plan: *plan, output: str, input: str, depend: *task...) *task = {
// Schedules a task which compiles an SSA file into assembly.
fn sched_qbe(plan: *plan, output: str, depend: *task) *task = {
+ const qbe_cmd = os::tryenv("QBE", "qbe");
let task = alloc(task {
status = status::SCHEDULED,
output = output,
depend = alloc([depend]),
cmd = alloc([
- os::tryenv("QBE", "qbe"), "-o", output, depend.output,
+ qbe_cmd,
+ "-t", plan.target.qbe_target,
+ "-o", output,
+ depend.output,
]),
module = void,
});
diff --git a/cmd/hare/subcmds.ha b/cmd/hare/subcmds.ha
@@ -18,16 +18,6 @@ use path;
use strings;
use unix::tty;
-fn default_tags() []module::tag = {
- return alloc([module::tag {
- name = strings::dup(os::machine()),
- mode = module::tag_mode::INCLUSIVE,
- }, module::tag {
- name = strings::dup(PLATFORM),
- mode = module::tag_mode::INCLUSIVE,
- }]);
-};
-
fn addtags(tags: []module::tag, in: str) ([]module::tag | void) = {
let in = match (module::parsetags(in)) {
case void =>
@@ -87,7 +77,8 @@ fn build(args: []str) void = {
const cmd = getopt::parse(args, help...);
defer getopt::finish(&cmd);
- let tags = default_tags();
+ let target = default_target();
+ let tags = module::tags_dup(target.tags);
defer module::tags_free(tags);
let verbose = false;
@@ -113,7 +104,17 @@ fn build(args: []str) void = {
case 'o' =>
output = opt.1;
case 't' =>
- abort("-t option not implemented yet."); // TODO
+ for (let i = 0z; i < len(targets); i += 1) {
+ if (targets[i].name == opt.1) {
+ target = &targets[i];
+ module::tags_free(tags);
+ tags = module::tags_dup(targets[i].tags);
+ break;
+ };
+ };
+ if (target == null) {
+ fmt::fatal("Unsupported target '{}'", opt.1);
+ };
case 'T' =>
tags = match (addtags(tags, opt.1)) {
case void =>
@@ -153,7 +154,7 @@ fn build(args: []str) void = {
const ctx = module::context_init(tags, defines, HAREPATH);
defer module::context_finish(&ctx);
- const plan = mkplan(&ctx, libs);
+ const plan = mkplan(&ctx, libs, target);
defer plan_finish(&plan);
const ver = match (module::scan(&ctx, input)) {
@@ -286,7 +287,8 @@ fn run(args: []str) void = {
const cmd = getopt::parse(args, help...);
defer getopt::finish(&cmd);
- let tags = default_tags();
+ const target = default_target();
+ let tags = module::tags_dup(target.tags);
defer module::tags_free(tags);
let verbose = false;
@@ -345,7 +347,7 @@ fn run(args: []str) void = {
const ctx = module::context_init(tags, defines, HAREPATH);
defer module::context_finish(&ctx);
- const plan = mkplan(&ctx, libs);
+ const plan = mkplan(&ctx, libs, target);
defer plan_finish(&plan);
const ver = match (module::scan(&ctx, input)) {
@@ -396,8 +398,8 @@ fn test(args: []str) void = {
const cmd = getopt::parse(args, help...);
defer getopt::finish(&cmd);
- let tags = default_tags();
- defer module::tags_free(tags);
+ const target = default_target();
+ let tags = module::tags_dup(target.tags);
append(tags, module::tag {
name = strings::dup("test"),
mode = module::tag_mode::INCLUSIVE,
@@ -462,7 +464,7 @@ fn test(args: []str) void = {
const ctx = module::context_init(tags, defines, HAREPATH);
defer module::context_finish(&ctx);
- const plan = mkplan(&ctx, libs);
+ const plan = mkplan(&ctx, libs, target);
defer plan_finish(&plan);
let depends: []*task = [];
@@ -534,7 +536,8 @@ fn version(args: []str) void = {
if (verbose) {
fmt::errorln()!;
fmt::printf("Build tags\t")!;
- const tags = default_tags();
+ const target = default_target();
+ const tags = target.tags;
for (let i = 0z; i < len(tags); i += 1) {
const tag = tags[i];
const inclusive = (tag.mode & module::tag_mode::INCLUSIVE) == 0;
diff --git a/cmd/hare/target.ha b/cmd/hare/target.ha
@@ -0,0 +1,73 @@
+use hare::module;
+use hare::module::{tag_mode};
+
+type target = struct {
+ name: str,
+ ar_cmd: str,
+ as_cmd: str,
+ cc_cmd: str,
+ ld_cmd: str,
+ qbe_target: str,
+ tags: []module::tag,
+};
+
+fn default_target() *target = {
+ for (let i = 0z; i < len(targets); i += 1) {
+ if (targets[i].tags[0].name == ARCH) {
+ return &targets[i];
+ };
+ };
+ abort("Build configuration error - unknown default target");
+};
+
+// TODO:
+// - Source default target information from here
+// - Implement cross compiling to other kernels (e.g. Linux => FreeBSD)
+// - sysroots
+const targets: [_]target = [
+ target {
+ name = "aarch64",
+ ar_cmd = AARCH64_AR,
+ as_cmd = AARCH64_AS,
+ cc_cmd = AARCH64_CC,
+ ld_cmd = AARCH64_LD,
+ qbe_target = "arm64",
+ tags = [module::tag {
+ name = "aarch64",
+ mode = tag_mode::INCLUSIVE,
+ }, module::tag {
+ name = PLATFORM,
+ mode = module::tag_mode::INCLUSIVE,
+ }],
+ },
+ target {
+ name = "riscv64",
+ ar_cmd = RISCV64_AR,
+ as_cmd = RISCV64_AS,
+ cc_cmd = RISCV64_CC,
+ ld_cmd = RISCV64_LD,
+ qbe_target = "rv64",
+ tags = [module::tag {
+ name = "riscv64",
+ mode = tag_mode::INCLUSIVE,
+ }, module::tag {
+ name = PLATFORM,
+ mode = module::tag_mode::INCLUSIVE,
+ }],
+ },
+ target {
+ name = "x86_64",
+ ar_cmd = X86_64_AR,
+ as_cmd = X86_64_AS,
+ cc_cmd = X86_64_CC,
+ ld_cmd = X86_64_LD,
+ qbe_target = "amd64_sysv",
+ tags = [module::tag {
+ name = "x86_64",
+ mode = tag_mode::INCLUSIVE,
+ }, module::tag {
+ name = PLATFORM,
+ mode = module::tag_mode::INCLUSIVE,
+ }],
+ },
+];
diff --git a/config.example.mk b/config.example.mk
@@ -30,3 +30,19 @@ SCDOC = scdoc
# Where to store build artifacts
HARECACHE = .cache
BINOUT = .bin
+
+# Cross-compiling settings
+AARCH64_AS=aarch64-as
+AARCH64_AR=aarch64-ar
+AARCH64_CC=aarch64-cc
+AARCH64_LD=aarch64-ld
+
+RISCV64_AS=riscv64-as
+RISCV64_AR=riscv64-ar
+RISCV64_CC=riscv64-cc
+RISCV64_LD=riscv64-ld
+
+X86_64_AS=x86_64-as
+X86_64_AR=x86_64-ar
+X86_64_CC=x86_64-cc
+X86_64_LD=x86_64-ld
diff --git a/hare/module/scan.ha b/hare/module/scan.ha
@@ -443,6 +443,18 @@ export fn tags_free(tags: []tag) void = {
free(tags);
};
+// Duplicates a set of tags.
+export fn tags_dup(tags: []tag) []tag = {
+ let new: []tag = alloc([], len(tags));
+ for (let i = 0z; i < len(tags); i += 1) {
+ append(new, tag {
+ name = strings::dup(tags[i].name),
+ mode = tags[i].mode,
+ });
+ };
+ return new;
+};
+
// Compares two tag sets and tells you if they are compatible.
export fn tagcompat(have: []tag, want: []tag) bool = {
// XXX: O(n²), lame
diff --git a/rt/+linux/start+aarch64-libc.s b/rt/+linux/start+aarch64-libc.s
@@ -5,5 +5,4 @@ _start:
mov x30, #0
mov x0, sp
add sp, x0, #-16
- and sp, sp, #-16
b rt.start_linux
diff --git a/targets.mk b/targets.mk
@@ -0,0 +1,17 @@
+HARE_DEFINES:=\
+ -D PLATFORM:str='"'"$(PLATFORM)"'"' \
+ -D ARCH:str='"'"$(ARCH)"'"' \
+ -D VERSION:str='"'"$$(./scripts/version)"'"' \
+ -D HAREPATH:str='"'"$(HAREPATH)"'"' \
+ -D AARCH64_AS:str='"'"$(AARCH64_AS)"'"' \
+ -D AARCH64_AR:str='"'"$(AARCH64_AR)"'"' \
+ -D AARCH64_CC:str='"'"$(AARCH64_CC)"'"' \
+ -D AARCH64_LD:str='"'"$(AARCH64_LD)"'"' \
+ -D RISCV64_AS:str='"'"$(RISCV64_AS)"'"' \
+ -D RISCV64_AR:str='"'"$(RISCV64_AR)"'"' \
+ -D RISCV64_CC:str='"'"$(RISCV64_CC)"'"' \
+ -D RISCV64_LD:str='"'"$(RISCV64_LD)"'"' \
+ -D X86_64_AS:str='"'"$(X86_64_AS)"'"' \
+ -D X86_64_AR:str='"'"$(X86_64_AR)"'"' \
+ -D X86_64_CC:str='"'"$(X86_64_CC)"'"' \
+ -D X86_64_LD:str='"'"$(X86_64_LD)"'"'