hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit 6a749628f21d3fade9a681919550a16c6274a0d7
parent 41744ce653238bcc8e4db96133efa728b00a9605
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sat, 19 Jun 2021 13:19:10 -0400

net::udp: new module

Also simplifes and streamlines net+linux overall.

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Mnet/+linux.ha | 93+++++--------------------------------------------------------------------------
Dnet/options.ha | 30------------------------------
Mnet/tcp/+linux.ha | 86++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Anet/tcp/options.ha | 28++++++++++++++++++++++++++++
Anet/udp/+linux.ha | 123+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anet/udp/options.ha | 9+++++++++
Mnet/unix/+linux.ha | 49+++++++++++++++++++++++++++++++++++++++----------
Anet/unix/options.ha | 6++++++
Mrt/+linux/syscalls.ha | 6+++---
Mscripts/gen-stdlib | 16+++++++++++++---
Mstdlib.mk | 46++++++++++++++++++++++++++++++++++++++++------
11 files changed, 347 insertions(+), 145 deletions(-)

diff --git a/net/+linux.ha b/net/+linux.ha @@ -1,3 +1,4 @@ +// Some common code for handling sockets on +linux use errors; use fmt; use io; @@ -6,19 +7,6 @@ use os; use rt; use strings; -export fn connect_fd( - addr: rt::sockaddr, - options: connect_option... -) (int | io::error) = { - const sockfd = mksockfd(addr)?; - for (let i = 0z; i < len(options); i += 1) { - // The only option is keepalive right now - setsockopt(sockfd, rt::SO_KEEPALIVE, true)?; - }; - wrap(rt::connect(sockfd, &addr, sockasz(addr)))?; - return sockfd; -}; - export type stream_listener = struct { l: listener, fd: int, @@ -31,53 +19,15 @@ export fn listenerfd(l: *listener) (int | void) = { }; }; -export fn listen_fd( - addr: rt::sockaddr, - options: listen_option... -) (int | io::error) = { - const sockfd = mksockfd(addr)?; - - let bk: u32 = 10; - let portout: nullable *u16 = null; - - for (let i = 0z; i < len(options); i += 1) { - match (options[i]) { - reuseaddr => setsockopt(sockfd, rt::SO_REUSEADDR, true)?, - reuseport => setsockopt(sockfd, rt::SO_REUSEPORT, true)?, - keepalive => setsockopt(sockfd, rt::SO_KEEPALIVE, true)?, - b: backlog => bk = b, - p: portassignment => portout = p, - }; - }; - setfcntl(sockfd, rt::O_CLOEXEC)!; - - wrap(rt::bind(sockfd, &addr, sockasz(addr)))?; - wrap(rt::listen(sockfd, bk))?; - - match (portout) { - p: *u16 => { - if (addr.in.sin_family != rt::AF_INET && - addr.in.sin_family != rt::AF_INET6) { - return errors::unsupported; - }; - let sn = rt::sockaddr {...}; - let al = sockasz(addr); - wrap(rt::getsockname(sockfd, &sn, &al))?; - const addr = ip::from_native(sn); - *p = addr.1; - }, - null => void, - }; - - return sockfd; -}; - export fn stream_accept(l: *listener) (*io::stream | io::error) = { assert(l.accept == &stream_accept); let l = l: *stream_listener; let sn = rt::sockaddr {...}; const sz = size(rt::sockaddr): u32; - const fd = wrap(rt::accept(l.fd, &sn, &sz))?; + const fd = match (rt::accept(l.fd, &sn, &sz)) { + err: rt::errno => return errors::errno(err), + fd: int => fd, + }; static let namebuf: [32]u8 = [0...]; return os::fdopen(fd, fmt::bsprintf(namebuf, "<net connection {}>", fd), @@ -90,36 +40,3 @@ export fn stream_shutdown(l: *listener) void = { rt::close(l.fd)!; free(l); }; - -fn setsockopt(sockfd: int, option: int, value: bool) (void | rt::errno) = { - let val: int = if (value) 1 else 0; - rt::setsockopt(sockfd, rt::SOL_SOCKET, option, - &val: *void, size(int): u32)?; - return; -}; - -fn setfcntl(sockfd: int, flag: int) (void | rt::errno) = { - let flags = rt::fcntl(sockfd, rt::F_GETFL, 0)?; - rt::fcntl(sockfd, rt::F_SETFL, flags | flag)!; - return; -}; - -fn wrap(ie: (int | rt::errno)) (int | io::error) = { - match (ie) { - i: int => i, - err: rt::errno => errors::errno(err), - }; -}; - -fn sockasz(addr: rt::sockaddr) u32 = { - return switch (addr.in.sin_family) { - rt::AF_INET => size(rt::sockaddr_in): u32, - rt::AF_INET6 => size(rt::sockaddr_in6): u32, - rt::AF_UNIX => size(rt::sockaddr_un): u32, - * => size(rt::sockaddr): u32, - }; -}; - -export fn mksockfd(addr: rt::sockaddr) (int | io::error) = { - return wrap(rt::socket(addr.in.sin_family: int, rt::SOCK_STREAM, 0))?; -}; diff --git a/net/options.ha b/net/options.ha @@ -1,30 +0,0 @@ -// Enables keep-alive for a socket. -export type keepalive = void; - -// Configures the backlog size for a listener. If not specified, a sensible -// default (10) is used. -export type backlog = u32; - -// Enables port re-use for a TCP listener. -export type reuseport = void; - -// Enables address re-use for a TCP listener. -export type reuseaddr = void; - -// To have the system select an arbitrary unused port for [[listen]], set port to -// zero. To retrieve the assigned port, provide this as one of the options and -// the addressed u16 will be filled in with the port. -export type portassignment = *u16; - -// Options for [[connect]]. Not all options are supported by all socket types or -// platforms. -export type connect_option = keepalive; - -// Options for [[listen]]. Not all options are supported by all socket types or -// platforms. -export type listen_option = ( - keepalive | - reuseport | - reuseaddr | - backlog | - portassignment); diff --git a/net/tcp/+linux.ha b/net/tcp/+linux.ha @@ -1,3 +1,4 @@ +use errors; use io; use net::ip; use net; @@ -9,10 +10,28 @@ use rt; export fn connect( addr: ip::addr, port: u16, - options: net::connect_option... + options: connect_option... ) (*io::stream | io::error) = { const sockaddr = ip::to_native(addr, port); - const sockfd = net::connect_fd(sockaddr, options...)?; + const sockfd = match (rt::socket(match (addr) { + ip::addr4 => rt::AF_INET: int, + ip::addr6 => rt::AF_INET6: int, + }, rt::SOCK_STREAM, 0)) { + err: rt::errno => return errors::errno(err), + fd: int => fd, + }; + let flags = rt::fcntl(sockfd, rt::F_GETFL, 0)!; + rt::fcntl(sockfd, rt::F_SETFL, flags | rt::O_CLOEXEC)!; + + for (let i = 0z; i < len(options); i += 1) { + // The only option is keepalive right now + setsockopt(sockfd, rt::SO_KEEPALIVE, true)?; + }; + const sz = size(rt::sockaddr): u32; + match (rt::connect(sockfd, &sockaddr, sz)) { + err: rt::errno => return errors::errno(err), + int => void, + }; return os::fdopen(sockfd, ip::string(addr), io::mode::READ | io::mode::WRITE); }; @@ -21,10 +40,54 @@ export fn connect( export fn listen( addr: ip::addr, port: u16, - options: net::listen_option... + options: listen_option... ) (*net::listener | io::error) = { - let sockaddr = ip::to_native(addr, port); - let sockfd = net::listen_fd(sockaddr, options...)?; + const sockaddr = ip::to_native(addr, port); + const sockfd = match (rt::socket(match (addr) { + ip::addr4 => rt::AF_INET: int, + ip::addr6 => rt::AF_INET6: int, + }, rt::SOCK_STREAM, 0)) { + err: rt::errno => return errors::errno(err), + fd: int => fd, + }; + let flags = rt::fcntl(sockfd, rt::F_GETFL, 0)!; + rt::fcntl(sockfd, rt::F_SETFL, flags | rt::O_CLOEXEC)!; + + let bk: u32 = 10; + for (let i = 0z; i < len(options); i += 1) { + match (options[i]) { + reuseaddr => setsockopt(sockfd, rt::SO_REUSEADDR, true)?, + reuseport => setsockopt(sockfd, rt::SO_REUSEPORT, true)?, + keepalive => setsockopt(sockfd, rt::SO_KEEPALIVE, true)?, + b: backlog => bk = b, + p: portassignment => void, + }; + }; + + match (rt::bind(sockfd, &sockaddr, size(rt::sockaddr): u32)) { + err: rt::errno => return errors::errno(err), + int => void, + }; + match ((rt::listen(sockfd, bk))) { + err: rt::errno => return errors::errno(err), + int => void, + }; + + for (let i = 0z; i < len(options); i += 1) { + let portout = match (options[i]) { + p: portassignment => p, + * => continue, + }; + let sn = rt::sockaddr {...}; + let al = size(rt::sockaddr): u32; + match (rt::getsockname(sockfd, &sn, &al)) { + err: rt::errno => return errors::errno(err), + int => void, + }; + const addr = ip::from_native(sn); + *portout = addr.1; + }; + return alloc(net::stream_listener { l = net::listener { accept = &net::stream_accept, @@ -48,3 +111,16 @@ export fn peeraddr(stream: *io::stream) ((ip::addr, u16) | void) = { }; return ip::from_native(sn); }; + +fn setsockopt( + sockfd: int, + option: int, + value: bool, +) (void | errors::opaque) = { + let val: int = if (value) 1 else 0; + return match (rt::setsockopt(sockfd, rt::SOL_SOCKET, option, + &val: *void, size(int): u32)) { + err: rt::errno => errors::errno(err), + int => void, + }; +}; diff --git a/net/tcp/options.ha b/net/tcp/options.ha @@ -0,0 +1,28 @@ +// Enables keep-alive for a socket. +export type keepalive = void; + +// Configures the backlog size for a listener. If not specified, a sensible +// default (10) is used. +export type backlog = u32; + +// Enables port re-use for a TCP listener. +export type reuseport = void; + +// Enables address re-use for a TCP listener. +export type reuseaddr = void; + +// To have the system select an arbitrary unused port for [[listen]], set port to +// zero. To retrieve the assigned port, provide this as one of the options and +// the addressed u16 will be filled in with the port. +export type portassignment = *u16; + +// Options for [[connect]]. +export type connect_option = keepalive; + +// Options for [[listen]]. +export type listen_option = ( + keepalive | + reuseport | + reuseaddr | + backlog | + portassignment); diff --git a/net/udp/+linux.ha b/net/udp/+linux.ha @@ -0,0 +1,123 @@ +use errors; +use net; +use net::ip; +use rt; + +export type socket = int; + +// Creates a UDP socket and sets the default destination to the given address. +export fn connect( + dest: ip::addr, + port: u16, +) (socket | errors::opaque) = { + const sockfd = match (rt::socket(match (dest) { + ip::addr4 => rt::AF_INET: int, + ip::addr6 => rt::AF_INET6: int, + }, rt::SOCK_DGRAM, 0)) { + err: rt::errno => return errors::errno(err), + fd: int => fd, + }; + + const sockaddr = ip::to_native(dest, port); + const sz = size(rt::sockaddr): u32; + return match (rt::connect(sockfd, &sockaddr, sz)) { + int => sockfd, + err: rt::errno => errors::errno(err), + }; +}; + +// Creates a UDP socket bound to an interface. +export fn listen( + addr: ip::addr, + port: u16, + options: bind_option... +) (socket | errors::opaque) = { + const sockfd = match (rt::socket(match (addr) { + ip::addr4 => rt::AF_INET: int, + ip::addr6 => rt::AF_INET6: int, + }, rt::SOCK_DGRAM, 0)) { + err: rt::errno => return errors::errno(err), + fd: int => fd, + }; + let flags = rt::fcntl(sockfd, rt::F_GETFL, 0)!; + rt::fcntl(sockfd, rt::F_SETFL, flags | rt::O_CLOEXEC)!; + + const sockaddr = ip::to_native(addr, port); + const sz = size(rt::sockaddr): u32; + match (rt::bind(sockfd, &sockaddr, sz)) { + int => void, + err: rt::errno => return errors::errno(err), + }; + + for (let i = 0z; i < len(options); i += 1) { + // The only option is portassignment right now + let sn = rt::sockaddr {...}; + let al = size(rt::sockaddr): u32; + match (rt::getsockname(sockfd, &sn, &al)) { + err: rt::errno => return errors::errno(err), + int => void, + }; + const addr = ip::from_native(sn); + *options[i] = addr.1; + }; + + return sockfd; +}; + +// Closes a UDP socket. +export fn close(sock: socket) void = { + rt::close(sock): void; +}; + +// Sends a UDP packet to the destination previously specified by [[connect]]. +export fn send(sock: socket, buf: []u8) (size | errors::opaque) = { + return match (rt::send(sock, buf: *[*]u8, len(buf), 0)) { + sz: size => sz, + err: rt::errno => errors::errno(err), + }; +}; + +// Sends a UDP packet using this socket. +export fn sendto( + sock: socket, + buf: []u8, + dest: ip::addr, + port: u16, +) (size | errors::opaque) = { + const sockaddr = ip::to_native(dest, port); + const sz = size(rt::sockaddr): u32; + return match (rt::sendto(sock, buf: *[*]u8, len(buf), + 0, &sockaddr, sz)) { + sz: size => sz, + err: rt::errno => errors::errno(err), + }; +}; + +// Receives a UDP packet from a bound socket. +export fn recvfrom( + sock: socket, + buf: []u8, + src: nullable *ip::addr, + port: nullable *u16, +) (size | errors::opaque) = { + let addrsz = size(rt::sockaddr): u32; + const sockaddr = rt::sockaddr { ... }; + const sz = match (rt::recvfrom(sock, buf: *[*]u8, len(buf), + 0, &sockaddr, &addrsz)) { + sz: size => sz, + err: rt::errno => errors::errno(err), + }; + + assert(addrsz <= size(rt::sockaddr)); + const peer = ip::from_native(sockaddr); + match (src) { + null => void, + src: *ip::addr => *src = peer.0, + }; + match (port) { + null => void, + port: *u16 => *port = peer.1, + }; + + return sz; +}; diff --git a/net/udp/options.ha b/net/udp/options.ha @@ -0,0 +1,9 @@ +// To have the system select an arbitrary unused port for [[listen]], set port to +// zero. To retrieve the assigned port, provide this as one of the options and +// the addressed u16 will be filled in with the port. +export type portassignment = *u16; + +// Options available for [[bind]]. +export type bind_option = portassignment; + +// TODO: Add send/recv flags diff --git a/net/unix/+linux.ha b/net/unix/+linux.ha @@ -7,33 +7,62 @@ use rt; use strings; use types; -// Opens a UNIX socket connection to the path. Blocks until the -// connection is established. -export fn connect( - addr: addr, - options: net::connect_option... -) (*io::stream | io::error) = { +// Opens a UNIX socket connection to the path. Blocks until the connection is +// established. +export fn connect(addr: addr) (*io::stream | io::error) = { let sockaddr = match (to_native(addr)) { a: rt::sockaddr => a, invalid => return errors::unsupported, // path too long }; - const sockfd = net::connect_fd(sockaddr, options...)?; + const sockfd = match (rt::socket(rt::AF_UNIX: int, rt::SOCK_STREAM, 0)) { + err: rt::errno => return errors::errno(err), + fd: int => fd, + }; + let flags = rt::fcntl(sockfd, rt::F_GETFL, 0)!; + rt::fcntl(sockfd, rt::F_SETFL, flags | rt::O_CLOEXEC)!; + + const sz = size(rt::sockaddr): u32; + match (rt::connect(sockfd, &sockaddr, sz)) { + err: rt::errno => return errors::errno(err), + int => void, + }; static let buf: [rt::UNIX_PATH_MAX + 32]u8 = [0...]; return os::fdopen(sockfd, fmt::bsprintf(buf, "<unix connection {}>", addr), io::mode::READ | io::mode::WRITE); }; -// Binds a UNIX socket listener at the given path. +// Binds a UNIX socket listener to the given path. export fn listen( addr: addr, - options: net::listen_option... + options: listen_option... ) (*net::listener | io::error) = { let sockaddr = match (to_native(addr)) { a: rt::sockaddr => a, invalid => return errors::unsupported, // path too long }; - let sockfd = net::listen_fd(sockaddr, options...)?; + const sockfd = match (rt::socket(rt::AF_UNIX: int, rt::SOCK_STREAM, 0)) { + err: rt::errno => return errors::errno(err), + fd: int => fd, + }; + let flags = rt::fcntl(sockfd, rt::F_GETFL, 0)!; + rt::fcntl(sockfd, rt::F_SETFL, flags | rt::O_CLOEXEC)!; + + let bk: u32 = 10; + for (let i = 0z; i < len(options); i += 1) { + // Only option is backlog right now + bk = options[i]; + }; + + match (rt::bind(sockfd, &sockaddr, size(rt::sockaddr): u32)) { + err: rt::errno => return errors::errno(err), + int => void, + }; + match ((rt::listen(sockfd, bk))) { + err: rt::errno => return errors::errno(err), + int => void, + }; + return alloc(net::stream_listener { l = net::listener { accept = &net::stream_accept, diff --git a/net/unix/options.ha b/net/unix/options.ha @@ -0,0 +1,6 @@ +// Configures the backlog size for a listener. If not specified, a sensible +// default (10) is used. +export type backlog = u32; + +// Options for [[listen]]. +export type listen_option = backlog; diff --git a/rt/+linux/syscalls.ha b/rt/+linux/syscalls.ha @@ -496,11 +496,11 @@ export fn recvfrom(sockfd: int, buf: *void, len_: size, flags: int, }; export fn sendto(sockfd: int, buf: *void, len_: size, flags: int, - dest_addr: nullable *sockaddr, addrlen: nullable *u32 + dest_addr: nullable *sockaddr, addrlen: u32 ) (size | errno) = { return wrap_return(syscall6(SYS_sendto, sockfd: u64, buf: uintptr: u64, len_: u64, flags: u64, - dest_addr: uintptr: u64, addrlen: uintptr: u64))?: size; + dest_addr: uintptr: u64, addrlen: u64))?: size; }; export fn recv(sockfd: int, buf: *void, len_: size, flags: int) (size | errno) = { @@ -508,7 +508,7 @@ export fn recv(sockfd: int, buf: *void, len_: size, flags: int) (size | errno) = }; export fn send(sockfd: int, buf: *void, len_: size, flags: int) (size | errno) = { - return sendto(sockfd, buf, len_, flags, null, null); + return sendto(sockfd, buf, len_, flags, null, 0); }; export fn getsockopt(sockfd: int, level: int, optname: int, optval: nullable *void, optlen: nullable *u32) (int | errno) = { diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib @@ -545,7 +545,6 @@ net() { printf '# net\n' gen_srcs net \ '$(PLATFORM).ha' \ - options.ha \ listener.ha gen_ssa net io os strings net::ip errors rt fmt } @@ -571,15 +570,25 @@ net_ip() { net_tcp() { printf '# net::tcp\n' gen_srcs net::tcp \ - '$(PLATFORM).ha' + '$(PLATFORM).ha' \ + options.ha gen_ssa net::tcp io net net::ip os rt } +net_udp() { + printf '# net::udp\n' + gen_srcs net::udp \ + '$(PLATFORM).ha' \ + options.ha + gen_ssa net::udp net net::ip errors rt +} + net_unix() { printf '# net::unix\n' gen_srcs net::unix \ + '$(PLATFORM).ha' \ addr.ha \ - '$(PLATFORM).ha' + options.ha gen_ssa net::unix net errors os io strings types fmt } @@ -801,6 +810,7 @@ linux::vdso net net::ip net::tcp +net::udp net::unix math::random os diff --git a/stdlib.mk b/stdlib.mk @@ -253,6 +253,10 @@ hare_stdlib_deps+=$(stdlib_net_ip) stdlib_net_tcp=$(HARECACHE)/net/tcp/net_tcp.o hare_stdlib_deps+=$(stdlib_net_tcp) +# gen_lib net::udp +stdlib_net_udp=$(HARECACHE)/net/udp/net_udp.o +hare_stdlib_deps+=$(stdlib_net_udp) + # gen_lib net::unix stdlib_net_unix=$(HARECACHE)/net/unix/net_unix.o hare_stdlib_deps+=$(stdlib_net_unix) @@ -803,7 +807,6 @@ $(HARECACHE)/linux/vdso/linux_vdso.ssa: $(stdlib_linux_vdso_srcs) $(stdlib_rt) $ # net stdlib_net_srcs= \ $(STDLIB)/net/$(PLATFORM).ha \ - $(STDLIB)/net/options.ha \ $(STDLIB)/net/listener.ha $(HARECACHE)/net/net.ssa: $(stdlib_net_srcs) $(stdlib_rt) $(stdlib_io) $(stdlib_os) $(stdlib_strings) $(stdlib_net_ip) $(stdlib_errors) $(stdlib_rt) $(stdlib_fmt) @@ -826,7 +829,8 @@ $(HARECACHE)/net/ip/net_ip.ssa: $(stdlib_net_ip_srcs) $(stdlib_rt) $(stdlib_byte # net::tcp # net::tcp stdlib_net_tcp_srcs= \ - $(STDLIB)/net/tcp/$(PLATFORM).ha + $(STDLIB)/net/tcp/$(PLATFORM).ha \ + $(STDLIB)/net/tcp/options.ha $(HARECACHE)/net/tcp/net_tcp.ssa: $(stdlib_net_tcp_srcs) $(stdlib_rt) $(stdlib_io) $(stdlib_net) $(stdlib_net_ip) $(stdlib_os) $(stdlib_rt) @printf 'HAREC \t$@\n' @@ -834,11 +838,24 @@ $(HARECACHE)/net/tcp/net_tcp.ssa: $(stdlib_net_tcp_srcs) $(stdlib_rt) $(stdlib_i @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nnet::tcp \ -t$(HARECACHE)/net/tcp/net_tcp.td $(stdlib_net_tcp_srcs) +# net::udp +# net::udp +stdlib_net_udp_srcs= \ + $(STDLIB)/net/udp/$(PLATFORM).ha \ + $(STDLIB)/net/udp/options.ha + +$(HARECACHE)/net/udp/net_udp.ssa: $(stdlib_net_udp_srcs) $(stdlib_rt) $(stdlib_net) $(stdlib_net_ip) $(stdlib_errors) $(stdlib_rt) + @printf 'HAREC \t$@\n' + @mkdir -p $(HARECACHE)/net/udp + @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nnet::udp \ + -t$(HARECACHE)/net/udp/net_udp.td $(stdlib_net_udp_srcs) + # net::unix # net::unix stdlib_net_unix_srcs= \ + $(STDLIB)/net/unix/$(PLATFORM).ha \ $(STDLIB)/net/unix/addr.ha \ - $(STDLIB)/net/unix/$(PLATFORM).ha + $(STDLIB)/net/unix/options.ha $(HARECACHE)/net/unix/net_unix.ssa: $(stdlib_net_unix_srcs) $(stdlib_rt) $(stdlib_net) $(stdlib_errors) $(stdlib_os) $(stdlib_io) $(stdlib_strings) $(stdlib_types) $(stdlib_fmt) @printf 'HAREC \t$@\n' @@ -1318,6 +1335,10 @@ hare_testlib_deps+=$(testlib_net_ip) testlib_net_tcp=$(TESTCACHE)/net/tcp/net_tcp.o hare_testlib_deps+=$(testlib_net_tcp) +# gen_lib net::udp +testlib_net_udp=$(TESTCACHE)/net/udp/net_udp.o +hare_testlib_deps+=$(testlib_net_udp) + # gen_lib net::unix testlib_net_unix=$(TESTCACHE)/net/unix/net_unix.o hare_testlib_deps+=$(testlib_net_unix) @@ -1888,7 +1909,6 @@ $(TESTCACHE)/linux/vdso/linux_vdso.ssa: $(testlib_linux_vdso_srcs) $(testlib_rt) # net testlib_net_srcs= \ $(STDLIB)/net/$(PLATFORM).ha \ - $(STDLIB)/net/options.ha \ $(STDLIB)/net/listener.ha $(TESTCACHE)/net/net.ssa: $(testlib_net_srcs) $(testlib_rt) $(testlib_io) $(testlib_os) $(testlib_strings) $(testlib_net_ip) $(testlib_errors) $(testlib_rt) $(testlib_fmt) @@ -1912,7 +1932,8 @@ $(TESTCACHE)/net/ip/net_ip.ssa: $(testlib_net_ip_srcs) $(testlib_rt) $(testlib_b # net::tcp # net::tcp testlib_net_tcp_srcs= \ - $(STDLIB)/net/tcp/$(PLATFORM).ha + $(STDLIB)/net/tcp/$(PLATFORM).ha \ + $(STDLIB)/net/tcp/options.ha $(TESTCACHE)/net/tcp/net_tcp.ssa: $(testlib_net_tcp_srcs) $(testlib_rt) $(testlib_io) $(testlib_net) $(testlib_net_ip) $(testlib_os) $(testlib_rt) @printf 'HAREC \t$@\n' @@ -1920,11 +1941,24 @@ $(TESTCACHE)/net/tcp/net_tcp.ssa: $(testlib_net_tcp_srcs) $(testlib_rt) $(testli @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nnet::tcp \ -t$(TESTCACHE)/net/tcp/net_tcp.td $(testlib_net_tcp_srcs) +# net::udp +# net::udp +testlib_net_udp_srcs= \ + $(STDLIB)/net/udp/$(PLATFORM).ha \ + $(STDLIB)/net/udp/options.ha + +$(TESTCACHE)/net/udp/net_udp.ssa: $(testlib_net_udp_srcs) $(testlib_rt) $(testlib_net) $(testlib_net_ip) $(testlib_errors) $(testlib_rt) + @printf 'HAREC \t$@\n' + @mkdir -p $(TESTCACHE)/net/udp + @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nnet::udp \ + -t$(TESTCACHE)/net/udp/net_udp.td $(testlib_net_udp_srcs) + # net::unix # net::unix testlib_net_unix_srcs= \ + $(STDLIB)/net/unix/$(PLATFORM).ha \ $(STDLIB)/net/unix/addr.ha \ - $(STDLIB)/net/unix/$(PLATFORM).ha + $(STDLIB)/net/unix/options.ha $(TESTCACHE)/net/unix/net_unix.ssa: $(testlib_net_unix_srcs) $(testlib_rt) $(testlib_net) $(testlib_errors) $(testlib_os) $(testlib_io) $(testlib_strings) $(testlib_types) $(testlib_fmt) @printf 'HAREC \t$@\n'