~sircmpwn/hare unlisted

286ff8408c12ad43f2c261090fa72f95ab3154f3 — Drew DeVault a month ago d0ddab2 haiku
Initial pass on Haiku port
19 files changed, 418 insertions(+), 78 deletions(-)

M config.sh
A lib/sys/abort+haiku.ha
A lib/sys/abort+linux.ha
M lib/sys/assert.ha
R lib/sys/{errno.ha => errno+linux.ha}
A lib/sys/segmalloc+haiku.ha
A lib/sys/start/hart0+haiku+x86_64.s
M lib/sys/start/hart1.ha
A lib/sys/syscallno+haiku+x86_64.ha
R lib/sys/{syscallno+aarch64.ha => syscallno+linux+aarch64.ha}
R lib/sys/{syscallno+x86_64.ha => syscallno+linux+x86_64.ha}
A lib/sys/syscalls+haiku.ha
R lib/sys/{syscalls.ha => syscalls+linux.ha}
M src/gen.c
M src/hare.c
M src/import.c
M src/manifest.c
M src/unparse.c
M src/util.c
M config.sh => config.sh +49 -8
@@ 76,6 76,7 @@ append_cflags() {
}

test_cflags() {
	printf "Checking for $@... "
	[ ! -e "$outdir"/check.c ] && cat <<-EOF > "$outdir"/check.c
	int main(void) { return 0; }
	EOF


@@ 88,8 89,28 @@ test_cflags() {
	if $CC $werror "$@" -o /dev/null "$outdir"/check.c >/dev/null 2>&1
	then
		append_cflags "$@"
		echo yes
	else
		return 1
		echo no
	fi
}

test_prog() {
	printf "Checking for $1... "
	shift
	cat >"$outdir"/prog.c
	werror=""
	case "$CFLAGS" in
		*-Werror*)
			werror="-Werror"
			;;
	esac
	if $CC $werror "$@" -o /dev/null "$outdir"/prog.c >/dev/null 2>&1
	then
		echo yes
		append_cflags "$@"
	else
		echo no
	fi
}



@@ 101,15 122,35 @@ run_configure() {
		-Wall -Wextra -Werror -Wno-unused-parameter -pedantic \
		-Wno-unused-function # for flex
	do
		printf "Checking for $flag... "
		if test_cflags "$flag"
		then
			echo yes
		else
			echo no
		fi
		test_cflags "$flag"
	done

	# https://dev.haiku-os.org/ticket/16259
	test_prog asprintf "-DHAVE_ASPRINTF=1" <<-EOF
	#include <stdio.h>
	#include <stdlib.h>

	int main(void) {
		char *f;
		asprintf(&f, "Hello %s", "world");
		free(f);
		return 0;
	}
	EOF

	# https://dev.haiku-os.org/ticket/16258
	test_prog fexecve "-DHAVE_FEXECVE=1" <<-EOF
	#include <sys/stat.h>
	#include <fcntl.h>
	#include <unistd.h>

	int main(void) {
		int f = open("/dev/null", O_RDONLY);
		fexecve(f, NULL, NULL);
		return 0;
	}
	EOF

	printf "Creating $outdir/config.mk... "
	cat <<-EOF > "$outdir"/config.mk
	CC=$CC

A lib/sys/abort+haiku.ha => lib/sys/abort+haiku.ha +2 -0
@@ 0,0 1,2 @@
/*** Raises SIGABRT, causing an abnormal process termination */
export fn abort void = exit(1); /* TODO: actually abort */

A lib/sys/abort+linux.ha => lib/sys/abort+linux.ha +2 -0
@@ 0,0 1,2 @@
/*** Raises SIGABRT, causing an abnormal process termination */
export fn abort void = kill(getpid(), SIGABRT);

M lib/sys/assert.ha => lib/sys/assert.ha +0 -3
@@ 1,6 1,3 @@
/*** Raises SIGABRT, causing an abnormal process termination */
export fn abort void = kill(getpid(), SIGABRT);

/* TODO: Pass in the filename and line number */
export @symbol("sys.assert") fn assert_(cond: bool, msg: *str) void =
{

R lib/sys/errno.ha => lib/sys/errno+linux.ha +0 -0

A lib/sys/segmalloc+haiku.ha => lib/sys/segmalloc+haiku.ha +2 -0
@@ 0,0 1,2 @@
fn segmalloc(n: size) nullable *void = null; /* TODO */
fn segfree(p: *void, s: size) int = 0; /* TODO */

A lib/sys/start/hart0+haiku+x86_64.s => lib/sys/start/hart0+haiku+x86_64.s +30 -0
@@ 0,0 1,30 @@
.text
.global _start
_start:
	xor %rbp, %rbp
	movq %rsp, %rdi
	jmp sys.start.start_ha

# TODO: Move these into hart1.ha (need attributes for symbols first)
.data
.balign 8
.globl _gSharedObjectHaikuABI
.type _gSharedObjectHaikuABI,@object
.size _gSharedObjectHaikuABI,4
_gSharedObjectHaikuABI:
        .int 4194304

.data
.balign 8
.globl _gSharedObjectHaikuVersion
.type _gSharedObjectHaikuVersion,@object
.size _gSharedObjectHaikuVersion,4
_gSharedObjectHaikuVersion:
	.int 1537

.section .comment
# HACK
.ascii "GCC: (2019_05_24) 8.3.0"
.byte 0
.ascii "GCC: (GNU) 8.3.0"
.byte 0

M lib/sys/start/hart1.ha => lib/sys/start/hart1.ha +11 -48
@@ 1,56 1,19 @@
use sys;

@symbol("main") fn main(args: []*str) int;
@symbol("main") fn main int;

fn cstr_len(cstr: *char) size =
{
	let b = cstr: [*]u8;
	let sz: size = 0;
	while (b[sz] != 0) {
		sz += 1;
	};
	return sz;
};

fn str_from_cstr(cstr: *char) &str =
{
	const l = cstr_len(cstr);
	let s = sys::must_malloc(size(size) + l + 1): &struct {
		sz: size,
		data: u8,
	};
	s.sz = l;
/* TODO
def B_HAIKU_ABI_GCC_4: u32 = 0x00400000;
def B_HAIKU_VERSION_1_PRE_BETA_3: u32 = 0x00000601;

	const in = cstr: [*]u8;
	let out = &s.data: [*]u8;
	for (let i = 0; i <= l; i += 1) {
		out[i] = in[i];
	};
export @symbol("_gSharedObjectHaikuABI") let
	abi_version: u32 = B_HAIKU_ABI_GCC_4;

	return ^s: &str;
};
export @symbol("_gSharedObjectHaikuVersion") let
	haiku_version: u32 = B_HAIKU_VERSION_1_PRE_BETA_3;
*/

export fn start_ha(iv: [*]uintptr) void =
export fn start_ha void =
{
	let argc = iv[0]: size;
	let argv = &iv[1]: [*]*char;
	let envp = &iv[1 + argc]: [*]nullable *char;
	/*
	 * TODO:
	 * - Parse & store auxv somewhere useful
	 * - Parse & store envp somewhere useful
	 */

	let argsv = sys::must_malloc(argc * size(&str)): [*]&str;
	for (let i = 0; i < argc; i += 1) {
		argsv[i] = str_from_cstr(argv[i]);
	};

	let args = struct {
		l: size = argc,
		c: size = argc,
		b: *void = argsv: *void,
	};

	sys::exit(main(&args: []*str));
	sys::exit(main());
};

A lib/sys/syscallno+haiku+x86_64.ha => lib/sys/syscallno+haiku+x86_64.ha +277 -0
@@ 0,0 1,277 @@
export def SYS_is_computer_on: u64 = 0;
export def SYS_generic_syscall: u64 = 1;
export def SYS_getrlimit: u64 = 2;
export def SYS_setrlimit: u64 = 3;
export def SYS_shutdown: u64 = 4;
export def SYS_get_safemode_option: u64 = 5;
export def SYS_wait_for_objects: u64 = 6;
export def SYS_mutex_lock: u64 = 7;
export def SYS_mutex_unlock: u64 = 8;
export def SYS_mutex_switch_lock: u64 = 9;
export def SYS_mutex_sem_acquire: u64 = 10;
export def SYS_mutex_sem_release: u64 = 11;
export def SYS_create_sem: u64 = 12;
export def SYS_delete_sem: u64 = 13;
export def SYS_switch_sem: u64 = 14;
export def SYS_switch_sem_etc: u64 = 15;
export def SYS_acquire_sem: u64 = 16;
export def SYS_acquire_sem_etc: u64 = 17;
export def SYS_release_sem: u64 = 18;
export def SYS_release_sem_etc: u64 = 19;
export def SYS_get_sem_count: u64 = 20;
export def SYS_get_sem_info: u64 = 21;
export def SYS_get_next_sem_info: u64 = 22;
export def SYS_set_sem_owner: u64 = 23;
export def SYS_realtime_sem_open: u64 = 24;
export def SYS_realtime_sem_close: u64 = 25;
export def SYS_realtime_sem_unlink: u64 = 26;
export def SYS_realtime_sem_get_value: u64 = 27;
export def SYS_realtime_sem_post: u64 = 28;
export def SYS_realtime_sem_wait: u64 = 29;
export def SYS_xsi_semget: u64 = 30;
export def SYS_xsi_semctl: u64 = 31;
export def SYS_xsi_semop: u64 = 32;
export def SYS_xsi_msgctl: u64 = 33;
export def SYS_xsi_msgget: u64 = 34;
export def SYS_xsi_msgrcv: u64 = 35;
export def SYS_xsi_msgsnd: u64 = 36;
export def SYS_load_image: u64 = 37;
export def SYS_exit_team: u64 = 38;
export def SYS_kill_team: u64 = 39;
export def SYS_get_current_team: u64 = 40;
export def SYS_wait_for_team: u64 = 41;
export def SYS_wait_for_child: u64 = 42;
export def SYS_exec: u64 = 43;
export def SYS_fork: u64 = 44;
export def SYS_process_info: u64 = 45;
export def SYS_setpgid: u64 = 46;
export def SYS_setsid: u64 = 47;
export def SYS_change_root: u64 = 48;
export def SYS_spawn_thread: u64 = 49;
export def SYS_find_thread: u64 = 50;
export def SYS_suspend_thread: u64 = 51;
export def SYS_resume_thread: u64 = 52;
export def SYS_rename_thread: u64 = 53;
export def SYS_set_thread_priority: u64 = 54;
export def SYS_kill_thread: u64 = 55;
export def SYS_exit_thread: u64 = 56;
export def SYS_cancel_thread: u64 = 57;
export def SYS_thread_yield: u64 = 58;
export def SYS_wait_for_thread: u64 = 59;
export def SYS_has_data: u64 = 60;
export def SYS_send_data: u64 = 61;
export def SYS_receive_data: u64 = 62;
export def SYS_restore_signal_frame: u64 = 63;
export def SYS_get_thread_info: u64 = 64;
export def SYS_get_next_thread_info: u64 = 65;
export def SYS_get_team_info: u64 = 66;
export def SYS_get_next_team_info: u64 = 67;
export def SYS_get_team_usage_info: u64 = 68;
export def SYS_get_extended_team_info: u64 = 69;
export def SYS_start_watching_system: u64 = 70;
export def SYS_stop_watching_system: u64 = 71;
export def SYS_block_thread: u64 = 72;
export def SYS_unblock_thread: u64 = 73;
export def SYS_unblock_threads: u64 = 74;
export def SYS_estimate_max_scheduling_latency: u64 = 75;
export def SYS_set_scheduler_mode: u64 = 76;
export def SYS_get_scheduler_mode: u64 = 77;
export def SYS_getgid: u64 = 78;
export def SYS_getuid: u64 = 79;
export def SYS_setregid: u64 = 80;
export def SYS_setreuid: u64 = 81;
export def SYS_getgroups: u64 = 82;
export def SYS_setgroups: u64 = 83;
export def SYS_send_signal: u64 = 84;
export def SYS_set_signal_mask: u64 = 85;
export def SYS_sigaction: u64 = 86;
export def SYS_sigwait: u64 = 87;
export def SYS_sigsuspend: u64 = 88;
export def SYS_sigpending: u64 = 89;
export def SYS_set_signal_stack: u64 = 90;
export def SYS_register_image: u64 = 91;
export def SYS_unregister_image: u64 = 92;
export def SYS_image_relocated: u64 = 93;
export def SYS_loading_app_failed: u64 = 94;
export def SYS_get_image_info: u64 = 95;
export def SYS_get_next_image_info: u64 = 96;
export def SYS_read_kernel_image_symbols: u64 = 97;
export def SYS_mount: u64 = 98;
export def SYS_unmount: u64 = 99;
export def SYS_read_fs_info: u64 = 100;
export def SYS_write_fs_info: u64 = 101;
export def SYS_next_device: u64 = 102;
export def SYS_sync: u64 = 103;
export def SYS_entry_ref_to_path: u64 = 104;
export def SYS_normalize_path: u64 = 105;
export def SYS_open_entry_ref: u64 = 106;
export def SYS_open: u64 = 107;
export def SYS_open_dir_entry_ref: u64 = 108;
export def SYS_open_dir: u64 = 109;
export def SYS_open_parent_dir: u64 = 110;
export def SYS_fcntl: u64 = 111;
export def SYS_fsync: u64 = 112;
export def SYS_flock: u64 = 113;
export def SYS_seek: u64 = 114;
export def SYS_create_dir_entry_ref: u64 = 115;
export def SYS_create_dir: u64 = 116;
export def SYS_remove_dir: u64 = 117;
export def SYS_read_link: u64 = 118;
export def SYS_create_symlink: u64 = 119;
export def SYS_create_link: u64 = 120;
export def SYS_unlink: u64 = 121;
export def SYS_rename: u64 = 122;
export def SYS_create_fifo: u64 = 123;
export def SYS_create_pipe: u64 = 124;
export def SYS_access: u64 = 125;
export def SYS_select: u64 = 126;
export def SYS_poll: u64 = 127;
export def SYS_open_attr_dir: u64 = 128;
export def SYS_read_attr: u64 = 129;
export def SYS_write_attr: u64 = 130;
export def SYS_stat_attr: u64 = 131;
export def SYS_open_attr: u64 = 132;
export def SYS_remove_attr: u64 = 133;
export def SYS_rename_attr: u64 = 134;
export def SYS_open_index_dir: u64 = 135;
export def SYS_create_index: u64 = 136;
export def SYS_read_index_stat: u64 = 137;
export def SYS_remove_index: u64 = 138;
export def SYS_getcwd: u64 = 139;
export def SYS_setcwd: u64 = 140;
export def SYS_open_query: u64 = 141;
export def SYS_read: u64 = 142;
export def SYS_readv: u64 = 143;
export def SYS_write: u64 = 144;
export def SYS_writev: u64 = 145;
export def SYS_ioctl: u64 = 146;
export def SYS_read_dir: u64 = 147;
export def SYS_rewind_dir: u64 = 148;
export def SYS_read_stat: u64 = 149;
export def SYS_write_stat: u64 = 150;
export def SYS_close: u64 = 151;
export def SYS_dup: u64 = 152;
export def SYS_dup2: u64 = 153;
export def SYS_lock_node: u64 = 154;
export def SYS_unlock_node: u64 = 155;
export def SYS_get_next_fd_info: u64 = 156;
export def SYS_socket: u64 = 157;
export def SYS_bind: u64 = 158;
export def SYS_shutdown_socket: u64 = 159;
export def SYS_connect: u64 = 160;
export def SYS_listen: u64 = 161;
export def SYS_accept: u64 = 162;
export def SYS_recv: u64 = 163;
export def SYS_recvfrom: u64 = 164;
export def SYS_recvmsg: u64 = 165;
export def SYS_send: u64 = 166;
export def SYS_sendto: u64 = 167;
export def SYS_sendmsg: u64 = 168;
export def SYS_getsockopt: u64 = 169;
export def SYS_setsockopt: u64 = 170;
export def SYS_getpeername: u64 = 171;
export def SYS_getsockname: u64 = 172;
export def SYS_sockatmark: u64 = 173;
export def SYS_socketpair: u64 = 174;
export def SYS_get_next_socket_stat: u64 = 175;
export def SYS_stop_notifying: u64 = 176;
export def SYS_start_watching: u64 = 177;
export def SYS_stop_watching: u64 = 178;
export def SYS_set_real_time_clock: u64 = 179;
export def SYS_set_timezone: u64 = 180;
export def SYS_get_timezone: u64 = 181;
export def SYS_set_real_time_clock_is_gmt: u64 = 182;
export def SYS_get_real_time_clock_is_gmt: u64 = 183;
export def SYS_get_clock: u64 = 184;
export def SYS_set_clock: u64 = 185;
export def SYS_system_time: u64 = 186;
export def SYS_snooze_etc: u64 = 187;
export def SYS_create_timer: u64 = 188;
export def SYS_delete_timer: u64 = 189;
export def SYS_get_timer: u64 = 190;
export def SYS_set_timer: u64 = 191;
export def SYS_create_area: u64 = 192;
export def SYS_delete_area: u64 = 193;
export def SYS_area_for: u64 = 194;
export def SYS_find_area: u64 = 195;
export def SYS_get_area_info: u64 = 196;
export def SYS_get_next_area_info: u64 = 197;
export def SYS_resize_area: u64 = 198;
export def SYS_transfer_area: u64 = 199;
export def SYS_set_area_protection: u64 = 200;
export def SYS_clone_area: u64 = 201;
export def SYS_reserve_address_range: u64 = 202;
export def SYS_unreserve_address_range: u64 = 203;
export def SYS_map_file: u64 = 204;
export def SYS_unmap_memory: u64 = 205;
export def SYS_set_memory_protection: u64 = 206;
export def SYS_sync_memory: u64 = 207;
export def SYS_memory_advice: u64 = 208;
export def SYS_get_memory_properties: u64 = 209;
export def SYS_create_port: u64 = 210;
export def SYS_close_port: u64 = 211;
export def SYS_delete_port: u64 = 212;
export def SYS_find_port: u64 = 213;
export def SYS_get_port_info: u64 = 214;
export def SYS_get_next_port_info: u64 = 215;
export def SYS_port_buffer_size_etc: u64 = 216;
export def SYS_port_count: u64 = 217;
export def SYS_read_port_etc: u64 = 218;
export def SYS_set_port_owner: u64 = 219;
export def SYS_write_port_etc: u64 = 220;
export def SYS_writev_port_etc: u64 = 221;
export def SYS_get_port_message_info_etc: u64 = 222;
export def SYS_kernel_debugger: u64 = 223;
export def SYS_register_syslog_daemon: u64 = 224;
export def SYS_debugger: u64 = 225;
export def SYS_disable_debugger: u64 = 226;
export def SYS_install_default_debugger: u64 = 227;
export def SYS_install_team_debugger: u64 = 228;
export def SYS_remove_team_debugger: u64 = 229;
export def SYS_debug_thread: u64 = 230;
export def SYS_wait_for_debugger: u64 = 231;
export def SYS_set_debugger_breakpoint: u64 = 232;
export def SYS_clear_debugger_breakpoint: u64 = 233;
export def SYS_system_profiler_start: u64 = 234;
export def SYS_system_profiler_next_buffer: u64 = 235;
export def SYS_system_profiler_stop: u64 = 236;
export def SYS_system_profiler_recorded: u64 = 237;
export def SYS_get_system_info: u64 = 238;
export def SYS_get_cpu_info: u64 = 239;
export def SYS_get_cpu_topology_info: u64 = 240;
export def SYS_analyze_scheduling: u64 = 241;
export def SYS_debug_output: u64 = 242;
export def SYS_ktrace_output: u64 = 243;
export def SYS_frame_buffer_update: u64 = 244;
export def SYS_register_messaging_service: u64 = 245;
export def SYS_unregister_messaging_service: u64 = 246;
export def SYS_clear_caches: u64 = 247;
export def SYS_cpu_enabled: u64 = 248;
export def SYS_set_cpu_enabled: u64 = 249;
export def SYS_get_cpuid: u64 = 250;
export def SYS_get_next_disk_device_id: u64 = 251;
export def SYS_find_disk_device: u64 = 252;
export def SYS_find_partition: u64 = 253;
export def SYS_find_file_disk_device: u64 = 254;
export def SYS_get_disk_device_data: u64 = 255;
export def SYS_register_file_device: u64 = 256;
export def SYS_unregister_file_device: u64 = 257;
export def SYS_get_file_disk_device_path: u64 = 258;
export def SYS_get_disk_system_info: u64 = 259;
export def SYS_get_next_disk_system_info: u64 = 260;
export def SYS_find_disk_system: u64 = 261;
export def SYS_defragment_partition: u64 = 262;
export def SYS_repair_partition: u64 = 263;
export def SYS_resize_partition: u64 = 264;
export def SYS_move_partition: u64 = 265;
export def SYS_set_partition_name: u64 = 266;
export def SYS_set_partition_content_name: u64 = 267;
export def SYS_set_partition_type: u64 = 268;
export def SYS_set_partition_parameters: u64 = 269;
export def SYS_set_partition_content_parameters: u64 = 270;
export def SYS_initialize_partition: u64 = 271;
export def SYS_uninitialize_partition: u64 = 272;
export def SYS_create_child_partition: u64 = 273;
export def SYS_delete_child_partition: u64 = 274;
export def SYS_start_watching_disks: u64 = 275;
export def SYS_stop_watching_disks: u64 = 276;

R lib/sys/syscallno+aarch64.ha => lib/sys/syscallno+linux+aarch64.ha +0 -0

R lib/sys/syscallno+x86_64.ha => lib/sys/syscallno+linux+x86_64.ha +0 -0

A lib/sys/syscalls+haiku.ha => lib/sys/syscalls+haiku.ha +14 -0
@@ 0,0 1,14 @@
fn syscall0(u64) u64;
fn syscall1(u64, u64) u64;
fn syscall2(u64, u64, u64) u64;
fn syscall3(u64, u64, u64, u64) u64;
fn syscall4(u64, u64, u64, u64, u64) u64;
fn syscall5(u64, u64, u64, u64, u64, u64) u64;
fn syscall6(u64, u64, u64, u64, u64, u64, u64) u64;

export fn exit(status: int) void = syscall1(SYS_exit_team, status: u64);

export fn write(fd: int, buf: *void, count: size) size = {
	/* TODO: Map directly onto Haiku syscall */
	return syscall4(SYS_write, fd: u64, (-1): u64, buf: uintptr: u64, count: u64);
};

R lib/sys/syscalls.ha => lib/sys/syscalls+linux.ha +0 -0

M src/gen.c => src/gen.c +2 -0
@@ 28,6 28,7 @@ static struct ha_identifier rt_abort = { "abort", &rt_sys, {0} },
			rt_must_malloc = { "must_malloc", &rt_sys, {0} },
			rt_free = { "free", &rt_sys, {0} };

#ifndef HAVE_ASPRINTF
static int
asprintf(char **strp, const char *fmt, ...)
{


@@ 47,6 48,7 @@ asprintf(char **strp, const char *fmt, ...)
	va_end(ap);
	return n;
}
#endif

static char *
gen_temporary(struct ha_qbe_context *context, const char *prefix)

M src/hare.c => src/hare.c +24 -15
@@ 13,7 13,6 @@
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <wordexp.h>
#include "ast.h"
#include "driver.h"
#include "identifier.h"


@@ 46,6 45,15 @@ get_srcdir(void)
	return SRCDIR;
}

static const char *
defenv(const char *env, const char *fallback)
{
	if (getenv(env)) {
		return getenv(env);
	}
	return fallback;
}

static char *
dupenv(const char *env, const char *fallback)
{


@@ 358,33 366,29 @@ module_from_namespace(struct ha_build_context *ctx,
	bool tagged = false, mixed = false;

	size_t nfiles = 0;
	/* TODO: Support this this properly */
	char *modpaths[] = {
		"./%s",
		"$HAREPATH/%s",
		"/usr/src/hare/%s", /* XXX: Don't hard-code this */
		"./lib/%s",
	};
	static char path[PATH_MAX+1];
	for (size_t i = 0; i < sizeof(modpaths) / sizeof(char *); ++i) {
		snprintf(path, sizeof(path), modpaths[i],
			ha_identifier_to_path(ident));

		wordexp_t p = {0};
		int r = wordexp(path, &p, WRDE_NOCMD | WRDE_UNDEF);
		if (r != 0 || p.we_wordc != 1) {
			continue;
		}

		DIR *d = opendir(p.we_wordv[0]);
		char *p = strdup(path);
		DIR *d = opendir(p);
		if (d == NULL) {
			wordfree(&p);
			free(p);
			continue;
		}

		/* TODO: move this to its own function */
		struct dirent *ent;
		while ((ent = readdir(d)) != NULL) {
			snprintf(path, sizeof(path), "%s/%s",
				p.we_wordv[0], ent->d_name);
			size_t n = snprintf(path, sizeof(path),
					"%s/%s", p, ent->d_name);
			assert(n != sizeof(path));
			/* TODO: Other source types; ssa, c, user-defined */
			if (!has_suffix(path, ".ha")
					&& !has_suffix(path, ".s")) {


@@ 393,7 397,7 @@ module_from_namespace(struct ha_build_context *ctx,

			/* TODO: Scan these for their own dependencies */
			struct stat st;
			r = stat(path, &st);
			int r = stat(path, &st);
			if (r != 0) {
				fprintf(stderr, "error: stat %s: %s\n",
					path, strerror(errno));


@@ 457,7 461,7 @@ module_from_namespace(struct ha_build_context *ctx,
			++nfiles;
		}

		wordfree(&p);
		free(p);
		closedir(d);
	}



@@ 949,7 953,12 @@ cleanup:;
	}

	if (cmd == DRIVER_RUN) {
#ifdef HAVE_FEXECVE
		r = fexecve(exe, &argv[optind - 1], environ);
#else
		errno = ENOTSUP, r = 1;
		close(exe);
#endif
		fprintf(stderr, "error: fexecve: %s\n", strerror(errno));
	}


M src/import.c => src/import.c +0 -1
@@ 2,7 2,6 @@
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <wordexp.h>
#include "check.h"
#include "manifest.h"
#include "module.h"

M src/manifest.c => src/manifest.c +1 -2
@@ 7,7 7,6 @@
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <wordexp.h>
#include "sha1.h"
#include "manifest.h"
#include "module.h"


@@ 45,7 44,7 @@ mkdirs(char *path, mode_t mode)
	if (mkdirs(dname, mode) != 0) {
		return -1;
	}
	if (mkdir(path, mode) != 0 && errno != EEXIST) {
	if (mkdir(path, mode) != 0 && errno != EEXIST && errno != EROFS) {
		return -1;
	}
	errno = 0;

M src/unparse.c => src/unparse.c +2 -0
@@ 11,6 11,7 @@
#include "check.h"
#include "util.h"

#ifndef HAVE_ASPRINTF
static int
asprintf(char **strp, const char *fmt, ...)
{


@@ 30,6 31,7 @@ asprintf(char **strp, const char *fmt, ...)
	va_end(ap);
	return n;
}
#endif

const char *
ha_binary_arithmetic_operation_unparse(enum ha_binary_arithmetic_operation op)

M src/util.c => src/util.c +2 -1
@@ 101,7 101,8 @@ has_suffix(const char *s, const char *suff)
}

char *
ha_getpath(const struct ha_pathspec *paths, size_t npaths) {
ha_getpath(const struct ha_pathspec *paths, size_t npaths)
{
	for (size_t i = 0; i < npaths; i++) {
		const char *var = "";
		if (paths[i].var) {