From 73d7ed45b207d7c2343e9e6241e5a3788fea2018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=22xq=22=20Quei=C3=9Fner?= Date: Wed, 3 Jan 2024 19:46:02 +0100 Subject: [PATCH 01/23] empty initial commit From 8a142c55ac6b4f770be614e66c02ed61fd2306fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=22xq=22=20Quei=C3=9Fner?= Date: Wed, 3 Jan 2024 19:49:10 +0100 Subject: [PATCH 02/23] Initial commit: Implements a handful of functions. --- modules/foundation-libc/.gitattributes | 1 + modules/foundation-libc/.gitignore | 2 + modules/foundation-libc/README.md | 10 ++++ modules/foundation-libc/build.zig | 26 ++++++++++ modules/foundation-libc/include/stdlib.h | 6 +++ modules/foundation-libc/include/string.h | 10 ++++ modules/foundation-libc/src/libc.zig | 6 +++ .../foundation-libc/src/modules/stdlib.zig | 17 +++++++ .../foundation-libc/src/modules/string.zig | 50 +++++++++++++++++++ 9 files changed, 128 insertions(+) create mode 100644 modules/foundation-libc/.gitattributes create mode 100644 modules/foundation-libc/.gitignore create mode 100644 modules/foundation-libc/README.md create mode 100644 modules/foundation-libc/build.zig create mode 100644 modules/foundation-libc/include/stdlib.h create mode 100644 modules/foundation-libc/include/string.h create mode 100644 modules/foundation-libc/src/libc.zig create mode 100644 modules/foundation-libc/src/modules/stdlib.zig create mode 100644 modules/foundation-libc/src/modules/string.zig diff --git a/modules/foundation-libc/.gitattributes b/modules/foundation-libc/.gitattributes new file mode 100644 index 000000000..0cb064aeb --- /dev/null +++ b/modules/foundation-libc/.gitattributes @@ -0,0 +1 @@ +*.zig text=auto eol=lf diff --git a/modules/foundation-libc/.gitignore b/modules/foundation-libc/.gitignore new file mode 100644 index 000000000..e73c965f8 --- /dev/null +++ b/modules/foundation-libc/.gitignore @@ -0,0 +1,2 @@ +zig-cache/ +zig-out/ diff --git a/modules/foundation-libc/README.md b/modules/foundation-libc/README.md new file mode 100644 index 000000000..59aa91912 --- /dev/null +++ b/modules/foundation-libc/README.md @@ -0,0 +1,10 @@ +# Foundation libc + +A C standard library that only implements a subset of functions that can be safely used without an operating system. + +This libc is primarily meant to be used with microcontrollers, hobbyist operating systems and so on. + +## Development + +Zig Version: 0.11 + diff --git a/modules/foundation-libc/build.zig b/modules/foundation-libc/build.zig new file mode 100644 index 000000000..ead00f7cb --- /dev/null +++ b/modules/foundation-libc/build.zig @@ -0,0 +1,26 @@ +const std = @import("std"); + +/// Creates a new instance of the libc target. +pub fn createLibrary(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile { + const libc = b.addStaticLibrary(.{ + .name = "foundation-libc", + .target = target, + .optimize = optimize, + .root_source_file = .{ .path = "src/libc.zig" }, + }); + + libc.addIncludePath(.{ .path = "include" }); + + return libc; +} + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const libc = createLibrary(b, target, optimize); + b.installArtifact(libc); + + b.addInstallHeaderFile("include/stdlib.h"); + b.addInstallHeaderFile("include/string.h"); +} diff --git a/modules/foundation-libc/include/stdlib.h b/modules/foundation-libc/include/stdlib.h new file mode 100644 index 000000000..9d6a800e3 --- /dev/null +++ b/modules/foundation-libc/include/stdlib.h @@ -0,0 +1,6 @@ +#ifndef _FOUNDATION_LIBC_STDLIB_H_ +#define _FOUNDATION_LIBC_STDLIB_H_ + +int atoi( const char *str ); + +#endif diff --git a/modules/foundation-libc/include/string.h b/modules/foundation-libc/include/string.h new file mode 100644 index 000000000..200436813 --- /dev/null +++ b/modules/foundation-libc/include/string.h @@ -0,0 +1,10 @@ +#ifndef _FOUNDATION_LIBC_STRING_H_ +#define _FOUNDATION_LIBC_STRING_H_ + +char *strchr( const char *str, int ch ); + +int strncmp( const char* lhs, const char* rhs, size_t count ); + +size_t strlen( const char *str ); + +#endif diff --git a/modules/foundation-libc/src/libc.zig b/modules/foundation-libc/src/libc.zig new file mode 100644 index 000000000..a4a129000 --- /dev/null +++ b/modules/foundation-libc/src/libc.zig @@ -0,0 +1,6 @@ +const std = @import("std"); + +comptime { + _ = @import("modules/stdlib.zig"); + _ = @import("modules/string.zig"); +} diff --git a/modules/foundation-libc/src/modules/stdlib.zig b/modules/foundation-libc/src/modules/stdlib.zig new file mode 100644 index 000000000..b33758007 --- /dev/null +++ b/modules/foundation-libc/src/modules/stdlib.zig @@ -0,0 +1,17 @@ +//! implementation of `stdlib.h` + +const std = @import("std"); + +/// https://en.cppreference.com/w/c/string/byte/atoi +export fn atoi(str: ?[*:0]const c_char) c_int { + const s = str orelse return 0; + + var i: usize = 0; + while (std.ascii.isWhitespace(@bitCast(s[i]))) { + i += 1; + } + + const slice = std.mem.sliceTo(s + i, 0); + + return std.fmt.parseInt(c_int, @ptrCast(slice), 10) catch return 0; +} diff --git a/modules/foundation-libc/src/modules/string.zig b/modules/foundation-libc/src/modules/string.zig new file mode 100644 index 000000000..edbe139e6 --- /dev/null +++ b/modules/foundation-libc/src/modules/string.zig @@ -0,0 +1,50 @@ +//! implementation of `string.h` + +const std = @import("std"); + +/// https://en.cppreference.com/w/c/string/byte/strchr +export fn strchr(str: ?[*:0]const c_char, ch: c_int) ?[*:0]c_char { + const s = str orelse return null; + + const searched: c_char = @bitCast(@as(u8, @truncate(@as(c_uint, @bitCast(ch))))); + + var i: usize = 0; + while (true) { + const actual = s[i]; + if (actual == searched) + return @constCast(s + i); + if (actual == 0) + return null; + i += 1; + } +} + +/// https://en.cppreference.com/w/c/string/byte/strlen +export fn strlen(str: ?[*:0]const c_char) usize { + const s = str orelse return 0; + return std.mem.len(s); +} + +/// https://en.cppreference.com/w/c/string/byte/strncmp +export fn strncmp(lhs: ?[*:0]const c_char, rhs: ?[*:0]const c_char, count: usize) c_int { + const lhs_s: [*:0]const u8 = @ptrCast(lhs orelse return if (rhs != null) 1 else 0); + const rhs_s: [*:0]const u8 = @ptrCast(rhs orelse return if (lhs != null) -1 else 0); + + var i: usize = 0; + while (i < count) { + const l = lhs_s[i]; + const r = rhs_s[i]; + + const d = @as(c_int, l) - @as(c_int, r); + if (d != 0) + return d; + + if (l == 0 and r == 0) + return 0; + std.debug.assert(l != 0); + std.debug.assert(r != 0); + + i += 1; + } + return 0; +} From 39198f22182f6bb39d4994d345ab27ad7134193f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=22xq=22=20Quei=C3=9Fner?= Date: Wed, 3 Jan 2024 20:03:59 +0100 Subject: [PATCH 03/23] Some more hints --- modules/foundation-libc/README.md | 24 ++++++++++++++++++++++++ modules/foundation-libc/build.zig | 5 +++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/modules/foundation-libc/README.md b/modules/foundation-libc/README.md index 59aa91912..d7cd7d8a7 100644 --- a/modules/foundation-libc/README.md +++ b/modules/foundation-libc/README.md @@ -1,10 +1,34 @@ # Foundation libc A C standard library that only implements a subset of functions that can be safely used without an operating system. +This is called a [freestanding environment](https://en.cppreference.com/w/cpp/freestanding). This libc is primarily meant to be used with microcontrollers, hobbyist operating systems and so on. +## Support + +The first goal is to reach full C11 *freestanding* support. + +- No support for locales +- No allocator (ship your own!) +- No support for functions that require an operating system of sorts in the background. + ## Development Zig Version: 0.11 + +Run +```sh-session +user@microzig ~/foundation-libc $ zig build +user@microzig ~/foundation-libc $ +``` + +to compile the libc and generate a lib file in `zig-out/lib` as well as the headers in `zig-out/include`. + + +## Links + +- [C11 Standard](https://www.iso.org/standard/57853.html) +- [C11 Standard Draft](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1548.pdf) + diff --git a/modules/foundation-libc/build.zig b/modules/foundation-libc/build.zig index ead00f7cb..27d452cd9 100644 --- a/modules/foundation-libc/build.zig +++ b/modules/foundation-libc/build.zig @@ -21,6 +21,7 @@ pub fn build(b: *std.Build) void { const libc = createLibrary(b, target, optimize); b.installArtifact(libc); - b.addInstallHeaderFile("include/stdlib.h"); - b.addInstallHeaderFile("include/string.h"); + // Add all headers here: + b.getInstallStep().dependOn(&b.addInstallHeaderFile("include/stdlib.h", "stdlib.h").step); + b.getInstallStep().dependOn(&b.addInstallHeaderFile("include/string.h", "string.h").step); } From c306b8ce34638338d2d9d8d75ceaa9c92d8275df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=22xq=22=20Quei=C3=9Fner?= Date: Wed, 3 Jan 2024 20:28:26 +0100 Subject: [PATCH 04/23] Adds docs about scope and stubs out headers. --- modules/foundation-libc/README.md | 42 +++++++++++++++++++ modules/foundation-libc/include/ctype.h | 4 ++ modules/foundation-libc/include/errno.h | 4 ++ modules/foundation-libc/include/inttypes.h | 4 ++ modules/foundation-libc/include/math.h | 4 ++ modules/foundation-libc/include/setjmp.h | 4 ++ modules/foundation-libc/include/stdnoreturn.h | 4 ++ modules/foundation-libc/include/tgmath.h | 4 ++ modules/foundation-libc/include/uchar.h | 4 ++ 9 files changed, 74 insertions(+) create mode 100644 modules/foundation-libc/include/ctype.h create mode 100644 modules/foundation-libc/include/errno.h create mode 100644 modules/foundation-libc/include/inttypes.h create mode 100644 modules/foundation-libc/include/math.h create mode 100644 modules/foundation-libc/include/setjmp.h create mode 100644 modules/foundation-libc/include/stdnoreturn.h create mode 100644 modules/foundation-libc/include/tgmath.h create mode 100644 modules/foundation-libc/include/uchar.h diff --git a/modules/foundation-libc/README.md b/modules/foundation-libc/README.md index d7cd7d8a7..dc51db073 100644 --- a/modules/foundation-libc/README.md +++ b/modules/foundation-libc/README.md @@ -12,6 +12,7 @@ The first goal is to reach full C11 *freestanding* support. - No support for locales - No allocator (ship your own!) - No support for functions that require an operating system of sorts in the background. +- No support for `wchar_t` and `wchar.h` as it isn't portable between compilers. ## Development @@ -31,4 +32,45 @@ to compile the libc and generate a lib file in `zig-out/lib` as well as the head - [C11 Standard](https://www.iso.org/standard/57853.html) - [C11 Standard Draft](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1548.pdf) +- [ziglibc](https://github.com/marler8997/ziglibc) +- [libc-test](https://wiki.musl-libc.org/libc-test.html) by musl +- [cppreference on freestanding](https://en.cppreference.com/w/cpp/freestanding) +- [IBM libc functions](https://www.ibm.com/docs/en/i/7.5?topic=extensions-standard-c-library-functions-table-by-name) (function to header map) +## Status + + +- โŒ `assert.h` Conditionally compiled macro that compares its argument to zero +- โŒ `complex.h` (since C99) Complex number arithmetic +- โณ `ctype.h` Functions to determine the type contained in character data +- โณ `errno.h` Macros reporting error conditions +- ๐Ÿ”ฎ `fenv.h` (since C99) Floating-point environment +- ๐Ÿ”€ `float.h` Limits of floating-point types +- โณ `inttypes.h` (since C99) Format conversion of integer types +- ๐Ÿ”€ `iso646.h` (since C95) Alternative operator spellings +- ๐Ÿ”€ `limits.h` Ranges of integer types +- โŒ `locale.h` Localization utilities +- โณ `math.h` Common mathematics functions +- โณ `setjmp.h` Nonlocal jumps +- โŒ `signal.h` Signal handling +- ๐Ÿ”€ `stdalign.h` (since C11) alignas and alignof convenience macros +- ๐Ÿ”€ `stdarg.h` Variable arguments +- ๐Ÿ”ฎ `stdatomic.h` (since C11) Atomic operations +- ๐Ÿ”ฎ `stdbit.h` (since C23) Macros to work with the byte and bit representations of types +- ๐Ÿ”€ `stdbool.h` (since C99) Macros for boolean type +- ๐Ÿ”ฎ `stdckdint.h` (since C23) macros for performing checked integer arithmetic +- ๐Ÿ”€ `stddef.h` Common macro definitions +- ๐Ÿ”€ `stdint.h` (since C99) Fixed-width integer types +- โŒ `stdio.h` Input/output +- โณ `stdlib.h` General utilities: memory management, program utilities, string conversions, random numbers, algorithms +- โณ `stdnoreturn.h` (since C11) noreturn convenience macro +- โณ `string.h` String handling +- โณ `tgmath.h` (since C99) Type-generic math (macros wrapping math.h and complex.h) +- โŒ `threads.h` (since C11) Thread library +- โŒ `time.h` Time/date utilities +- โณ `uchar.h` (since C11) UTF-16 and UTF-32 character utilities +- โŒ `wchar.h` (since C95) Extended multibyte and wide character utilities +- โŒ `wctype.h` (since C95) Functions to determine the type contained in wide character data + + +โณ (not started), ๐Ÿ›  (work in progress), โš ๏ธ (partial support), โœ… (full support), โŒ (no support), ๐Ÿ”ฎ (potential future support), ๐Ÿ”€ (implemented by compiler) diff --git a/modules/foundation-libc/include/ctype.h b/modules/foundation-libc/include/ctype.h new file mode 100644 index 000000000..0ec9a2c99 --- /dev/null +++ b/modules/foundation-libc/include/ctype.h @@ -0,0 +1,4 @@ +#ifndef _FOUNDATION_LIBC_CTYPE_H_ +#define _FOUNDATION_LIBC_CTYPE_H_ + +#endif diff --git a/modules/foundation-libc/include/errno.h b/modules/foundation-libc/include/errno.h new file mode 100644 index 000000000..be0bbe1c0 --- /dev/null +++ b/modules/foundation-libc/include/errno.h @@ -0,0 +1,4 @@ +#ifndef _FOUNDATION_LIBC_ERRNO_H_ +#define _FOUNDATION_LIBC_ERRNO_H_ + +#endif diff --git a/modules/foundation-libc/include/inttypes.h b/modules/foundation-libc/include/inttypes.h new file mode 100644 index 000000000..2c0697a6d --- /dev/null +++ b/modules/foundation-libc/include/inttypes.h @@ -0,0 +1,4 @@ +#ifndef _FOUNDATION_LIBC_INTTYPES_H_ +#define _FOUNDATION_LIBC_INTTYPES_H_ + +#endif diff --git a/modules/foundation-libc/include/math.h b/modules/foundation-libc/include/math.h new file mode 100644 index 000000000..6f3fdad00 --- /dev/null +++ b/modules/foundation-libc/include/math.h @@ -0,0 +1,4 @@ +#ifndef _FOUNDATION_LIBC_MATH_H_ +#define _FOUNDATION_LIBC_MATH_H_ + +#endif diff --git a/modules/foundation-libc/include/setjmp.h b/modules/foundation-libc/include/setjmp.h new file mode 100644 index 000000000..abe7d0a91 --- /dev/null +++ b/modules/foundation-libc/include/setjmp.h @@ -0,0 +1,4 @@ +#ifndef _FOUNDATION_LIBC_SETJMP_H_ +#define _FOUNDATION_LIBC_SETJMP_H_ + +#endif diff --git a/modules/foundation-libc/include/stdnoreturn.h b/modules/foundation-libc/include/stdnoreturn.h new file mode 100644 index 000000000..0aa491d4f --- /dev/null +++ b/modules/foundation-libc/include/stdnoreturn.h @@ -0,0 +1,4 @@ +#ifndef _FOUNDATION_LIBC_STDNORETURN_H_ +#define _FOUNDATION_LIBC_STDNORETURN_H_ + +#endif diff --git a/modules/foundation-libc/include/tgmath.h b/modules/foundation-libc/include/tgmath.h new file mode 100644 index 000000000..a6a0c603a --- /dev/null +++ b/modules/foundation-libc/include/tgmath.h @@ -0,0 +1,4 @@ +#ifndef _FOUNDATION_LIBC_TGMATH_H_ +#define _FOUNDATION_LIBC_TGMATH_H_ + +#endif diff --git a/modules/foundation-libc/include/uchar.h b/modules/foundation-libc/include/uchar.h new file mode 100644 index 000000000..c0cf95031 --- /dev/null +++ b/modules/foundation-libc/include/uchar.h @@ -0,0 +1,4 @@ +#ifndef _FOUNDATION_LIBC_UCHAR_H_ +#define _FOUNDATION_LIBC_UCHAR_H_ + +#endif From 676d85eee1fee02ca67dbf07c677c114a27535e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=22xq=22=20Quei=C3=9Fner?= Date: Wed, 3 Jan 2024 20:35:26 +0100 Subject: [PATCH 05/23] Stubs out zig module files, installs all headers. --- modules/foundation-libc/README.md | 9 +++++-- modules/foundation-libc/build.zig | 24 +++++++++++++++---- modules/foundation-libc/src/libc.zig | 5 ++++ modules/foundation-libc/src/modules/ctype.zig | 3 +++ modules/foundation-libc/src/modules/errno.zig | 4 ++++ modules/foundation-libc/src/modules/math.zig | 3 +++ .../foundation-libc/src/modules/setjmp.zig | 3 +++ modules/foundation-libc/src/modules/uchar.zig | 3 +++ 8 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 modules/foundation-libc/src/modules/ctype.zig create mode 100644 modules/foundation-libc/src/modules/errno.zig create mode 100644 modules/foundation-libc/src/modules/math.zig create mode 100644 modules/foundation-libc/src/modules/setjmp.zig create mode 100644 modules/foundation-libc/src/modules/uchar.zig diff --git a/modules/foundation-libc/README.md b/modules/foundation-libc/README.md index dc51db073..e96d6b45f 100644 --- a/modules/foundation-libc/README.md +++ b/modules/foundation-libc/README.md @@ -27,6 +27,11 @@ user@microzig ~/foundation-libc $ to compile the libc and generate a lib file in `zig-out/lib` as well as the headers in `zig-out/include`. +## Contribution + +Start by grabbing a header marked with โณ or ๐Ÿ›  and implement the functions from that header. See if others already have a PR open for those functions so you don't do work twice! + +Leverage functions from Zig `std` if possible as they are already well tested and should work. ## Links @@ -62,9 +67,9 @@ to compile the libc and generate a lib file in `zig-out/lib` as well as the head - ๐Ÿ”€ `stddef.h` Common macro definitions - ๐Ÿ”€ `stdint.h` (since C99) Fixed-width integer types - โŒ `stdio.h` Input/output -- โณ `stdlib.h` General utilities: memory management, program utilities, string conversions, random numbers, algorithms +- ๐Ÿ›  `stdlib.h` General utilities: memory management, program utilities, string conversions, random numbers, algorithms - โณ `stdnoreturn.h` (since C11) noreturn convenience macro -- โณ `string.h` String handling +- ๐Ÿ›  `string.h` String handling - โณ `tgmath.h` (since C99) Type-generic math (macros wrapping math.h and complex.h) - โŒ `threads.h` (since C11) Thread library - โŒ `time.h` Time/date utilities diff --git a/modules/foundation-libc/build.zig b/modules/foundation-libc/build.zig index 27d452cd9..421c2dfd9 100644 --- a/modules/foundation-libc/build.zig +++ b/modules/foundation-libc/build.zig @@ -1,9 +1,22 @@ const std = @import("std"); +const header_files = [_][]const u8{ + "ctype.h", + "errno.h", + "inttypes.h", + "math.h", + "setjmp.h", + "stdlib.h", + "stdnoreturn.h", + "string.h", + "tgmath.h", + "uchar.h", +}; + /// Creates a new instance of the libc target. pub fn createLibrary(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile { const libc = b.addStaticLibrary(.{ - .name = "foundation-libc", + .name = "foundation", .target = target, .optimize = optimize, .root_source_file = .{ .path = "src/libc.zig" }, @@ -21,7 +34,10 @@ pub fn build(b: *std.Build) void { const libc = createLibrary(b, target, optimize); b.installArtifact(libc); - // Add all headers here: - b.getInstallStep().dependOn(&b.addInstallHeaderFile("include/stdlib.h", "stdlib.h").step); - b.getInstallStep().dependOn(&b.addInstallHeaderFile("include/string.h", "string.h").step); + for (header_files) |header_name| { + b.getInstallStep().dependOn(&b.addInstallHeaderFile( + b.fmt("include/{s}", .{header_name}), + header_name, + ).step); + } } diff --git a/modules/foundation-libc/src/libc.zig b/modules/foundation-libc/src/libc.zig index a4a129000..5dbdd5b77 100644 --- a/modules/foundation-libc/src/libc.zig +++ b/modules/foundation-libc/src/libc.zig @@ -1,6 +1,11 @@ const std = @import("std"); comptime { + _ = @import("modules/ctype.zig"); + _ = @import("modules/errno.zig"); + _ = @import("modules/math.zig"); + _ = @import("modules/setjmp.zig"); _ = @import("modules/stdlib.zig"); _ = @import("modules/string.zig"); + _ = @import("modules/uchar.zig"); } diff --git a/modules/foundation-libc/src/modules/ctype.zig b/modules/foundation-libc/src/modules/ctype.zig new file mode 100644 index 000000000..d88fdd3d8 --- /dev/null +++ b/modules/foundation-libc/src/modules/ctype.zig @@ -0,0 +1,3 @@ +//! implementation of `ctype.h` + +const std = @import("std"); diff --git a/modules/foundation-libc/src/modules/errno.zig b/modules/foundation-libc/src/modules/errno.zig new file mode 100644 index 000000000..8f46470ad --- /dev/null +++ b/modules/foundation-libc/src/modules/errno.zig @@ -0,0 +1,4 @@ +//! implementation of `errno.h` + +const std = @import("std"); + diff --git a/modules/foundation-libc/src/modules/math.zig b/modules/foundation-libc/src/modules/math.zig new file mode 100644 index 000000000..1a3da7693 --- /dev/null +++ b/modules/foundation-libc/src/modules/math.zig @@ -0,0 +1,3 @@ +//! implementation of `math.h` + +const std = @import("std"); diff --git a/modules/foundation-libc/src/modules/setjmp.zig b/modules/foundation-libc/src/modules/setjmp.zig new file mode 100644 index 000000000..6e0b0ea38 --- /dev/null +++ b/modules/foundation-libc/src/modules/setjmp.zig @@ -0,0 +1,3 @@ +//! implementation of `setjmp.h` + +const std = @import("std"); diff --git a/modules/foundation-libc/src/modules/uchar.zig b/modules/foundation-libc/src/modules/uchar.zig new file mode 100644 index 000000000..7b4706d64 --- /dev/null +++ b/modules/foundation-libc/src/modules/uchar.zig @@ -0,0 +1,3 @@ +//! implementation of `uchar.h` + +const std = @import("std"); From 45bbc75386dab5715358aef4c7087a7b41c915fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=22xq=22=20Quei=C3=9Fner?= Date: Wed, 3 Jan 2024 20:37:13 +0100 Subject: [PATCH 06/23] Even more readme. --- modules/foundation-libc/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/foundation-libc/README.md b/modules/foundation-libc/README.md index e96d6b45f..6981cafb3 100644 --- a/modules/foundation-libc/README.md +++ b/modules/foundation-libc/README.md @@ -33,6 +33,8 @@ Start by grabbing a header marked with โณ or ๐Ÿ›  and implement the functions f Leverage functions from Zig `std` if possible as they are already well tested and should work. +Which functions belong into which header can be figured out by taking a look at the *C11 Standard Draft* document or the *IBM libc functions* list. [cppreference.com](https://en.cppreference.com/w/c) usually has the better docs though, so best check out both. + ## Links - [C11 Standard](https://www.iso.org/standard/57853.html) @@ -41,6 +43,7 @@ Leverage functions from Zig `std` if possible as they are already well tested an - [libc-test](https://wiki.musl-libc.org/libc-test.html) by musl - [cppreference on freestanding](https://en.cppreference.com/w/cpp/freestanding) - [IBM libc functions](https://www.ibm.com/docs/en/i/7.5?topic=extensions-standard-c-library-functions-table-by-name) (function to header map) +- [cppreference](https://en.cppreference.com/w/c) ## Status From c8263a3a682de33a86393bce2f0b612af70620b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=22xq=22=20Quei=C3=9Fner?= Date: Wed, 3 Jan 2024 20:37:37 +0100 Subject: [PATCH 07/23] Adds MIT licence. --- modules/foundation-libc/LICENCE | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 modules/foundation-libc/LICENCE diff --git a/modules/foundation-libc/LICENCE b/modules/foundation-libc/LICENCE new file mode 100644 index 000000000..95d3d5961 --- /dev/null +++ b/modules/foundation-libc/LICENCE @@ -0,0 +1,19 @@ +Copyright (c) 2024 Felix "xq" QueiรŸner + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. From 5eb5fa50265d5223bb33e1b0571452afed898d3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=22xq=22=20Quei=C3=9Fner?= Date: Fri, 5 Jan 2024 09:17:39 +0100 Subject: [PATCH 08/23] Completes the string.h header. --- modules/foundation-libc/build.zig | 10 +++++++ modules/foundation-libc/include/string.h | 26 ++++++++++++++++--- .../foundation-libc/src/modules/string.zig | 19 ++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/modules/foundation-libc/build.zig b/modules/foundation-libc/build.zig index 421c2dfd9..06ff762ed 100644 --- a/modules/foundation-libc/build.zig +++ b/modules/foundation-libc/build.zig @@ -1,5 +1,9 @@ const std = @import("std"); +pub const include_path = std.Build.LazyPath{ + .cwd_relative = sdk_root ++ "/include", +}; + const header_files = [_][]const u8{ "ctype.h", "errno.h", @@ -41,3 +45,9 @@ pub fn build(b: *std.Build) void { ).step); } } + +const sdk_root = computeSdkRoot(); + +fn computeSdkRoot() []const u8 { + return std.fs.path.dirname(@src().path).?; +} diff --git a/modules/foundation-libc/include/string.h b/modules/foundation-libc/include/string.h index 200436813..b9f0741f6 100644 --- a/modules/foundation-libc/include/string.h +++ b/modules/foundation-libc/include/string.h @@ -1,10 +1,30 @@ #ifndef _FOUNDATION_LIBC_STRING_H_ #define _FOUNDATION_LIBC_STRING_H_ -char *strchr( const char *str, int ch ); - -int strncmp( const char* lhs, const char* rhs, size_t count ); +#include +void * memchr(const void *s, int c, size_t n); +int memcmp(const void *s1, const void *s2, size_t n); +void * memcpy(void * restrict s1, const void * restrict s2, size_t n); +void * memmove(void *s1, const void *s2, size_t n); +void * memset(void *s, int c, size_t n); +char * strcat(char * restrict s1, const char * restrict s2); +char * strchr( const char *str, int ch ); +int strcmp(const char *s1, const char *s2); +char * strcpy(char * restrict s1, const char * restrict s2); +size_t strcspn(const char *s1, const char *s2); +char * strerror(int errnum); size_t strlen( const char *str ); +char * strncat(char * restrict s1, const char * restrict s2, size_t n); +int strncmp( const char* lhs, const char* rhs, size_t count ); +char * strncpy(char * restrict s1, const char * restrict s2, size_t n); +char * strpbrk(const char *s1, const char *s2); +char * strrchr(const char *s, int c); +size_t strspn(const char *s1, const char *s2); +char * strstr(const char *s1, const char *s2); +char * strtok(char * restrict s1, const char * restrict s2); + +// locale dependent: size_t strxfrm(char * restrict s1, const char * restrict s2, size_t n); +// locale dependent: int strcoll(const char *s1, const char *s2); #endif diff --git a/modules/foundation-libc/src/modules/string.zig b/modules/foundation-libc/src/modules/string.zig index edbe139e6..d17ce7f09 100644 --- a/modules/foundation-libc/src/modules/string.zig +++ b/modules/foundation-libc/src/modules/string.zig @@ -2,6 +2,25 @@ const std = @import("std"); +// Implemented in compiler_rt: +// * memcpy +// * memset + +// TODO: memchr +// TODO: memcmp +// TODO: memmove +// TODO: strcat +// TODO: strcmp +// TODO: strcpy +// TODO: strcspn +// TODO: strerror +// TODO: strncat +// TODO: strncpy +// TODO: strpbrk +// TODO: strrchr +// TODO: strspn +// TODO: strstr + /// https://en.cppreference.com/w/c/string/byte/strchr export fn strchr(str: ?[*:0]const c_char, ch: c_int) ?[*:0]c_char { const s = str orelse return null; From 5cf78776331c465c17a62bacd909105a4c86e284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=22xq=22=20Quei=C3=9Fner?= Date: Fri, 5 Jan 2024 09:37:43 +0100 Subject: [PATCH 09/23] Adds a nix flake for tooling, clang-format file and formats string.h, stdlib.h --- modules/foundation-libc/.clang-format | 17 +++ modules/foundation-libc/README.md | 9 ++ modules/foundation-libc/flake.lock | 146 +++++++++++++++++++++++ modules/foundation-libc/flake.nix | 62 ++++++++++ modules/foundation-libc/include/string.h | 42 +++---- 5 files changed, 255 insertions(+), 21 deletions(-) create mode 100644 modules/foundation-libc/.clang-format create mode 100644 modules/foundation-libc/flake.lock create mode 100644 modules/foundation-libc/flake.nix diff --git a/modules/foundation-libc/.clang-format b/modules/foundation-libc/.clang-format new file mode 100644 index 000000000..fc5590bc9 --- /dev/null +++ b/modules/foundation-libc/.clang-format @@ -0,0 +1,17 @@ +--- +Language: Cpp +BasedOnStyle: LLVM +IndentWidth: 4 +TabWidth: 4 +UseTab: Never + +QualifierAlignment: Right # use east const for better readability +PointerAlignment: Middle # do not tack pointer to type nor value + +# align function names and keep them apart at least one line +AlignConsecutiveDeclarations: AcrossEmptyLines + +SortIncludes: CaseSensitive + +--- + diff --git a/modules/foundation-libc/README.md b/modules/foundation-libc/README.md index 6981cafb3..3610f69ff 100644 --- a/modules/foundation-libc/README.md +++ b/modules/foundation-libc/README.md @@ -35,6 +35,15 @@ Leverage functions from Zig `std` if possible as they are already well tested an Which functions belong into which header can be figured out by taking a look at the *C11 Standard Draft* document or the *IBM libc functions* list. [cppreference.com](https://en.cppreference.com/w/c) usually has the better docs though, so best check out both. +### Style Guides + +- The header files are ment to be as minimal as possible + - Do not use comments documenting the functions, they are well documented everywhere else. + - Only insert empty lines between functions if necessarity for clarity + - Keep function names sorted alphabetically +- Try not to use macros at all +- Use `clang-format` with the provided style file. + ## Links - [C11 Standard](https://www.iso.org/standard/57853.html) diff --git a/modules/foundation-libc/flake.lock b/modules/foundation-libc/flake.lock new file mode 100644 index 000000000..5a71654bc --- /dev/null +++ b/modules/foundation-libc/flake.lock @@ -0,0 +1,146 @@ +{ + "nodes": { + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "locked": { + "lastModified": 1659877975, + "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1704420045, + "narHash": "sha256-C36QmoJd5tdQ5R9MC1jM7fBkZW9zBUqbUCsgwS6j4QU=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "c1be43e8e837b8dbee2b3665a007e761680f0c3d", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "release-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1702350026, + "narHash": "sha256-A+GNZFZdfl4JdDphYKBJ5Ef1HOiFsP18vQe9mqjmUis=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "9463103069725474698139ab10f17a9d125da859", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "zig": "zig" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "zig": { + "inputs": { + "flake-compat": "flake-compat_2", + "flake-utils": "flake-utils_2", + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1704414187, + "narHash": "sha256-augAzcGHaeuWknIsYSua1oJoCE0Mh12BRm3i4ZONcpc=", + "owner": "mitchellh", + "repo": "zig-overlay", + "rev": "aeb1f52d1ca19d836f0ef4a7f3f25deec661f75f", + "type": "github" + }, + "original": { + "owner": "mitchellh", + "repo": "zig-overlay", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/modules/foundation-libc/flake.nix b/modules/foundation-libc/flake.nix new file mode 100644 index 000000000..c93025a82 --- /dev/null +++ b/modules/foundation-libc/flake.nix @@ -0,0 +1,62 @@ +{ + description = "foundation libc development environment"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/release-23.11"; + flake-utils.url = "github:numtide/flake-utils"; + + # required for latest zig + zig.url = "github:mitchellh/zig-overlay"; + + # Used for shell.nix + flake-compat = { + url = github:edolstra/flake-compat; + flake = false; + }; + }; + + outputs = { + self, + nixpkgs, + flake-utils, + ... + } @ inputs: let + overlays = [ + # Other overlays + (final: prev: { + zigpkgs = inputs.zig.packages.${prev.system}; + }) + ]; + + # Our supported systems are the same supported systems as the Zig binaries + systems = builtins.attrNames inputs.zig.packages; + in + flake-utils.lib.eachSystem systems ( + system: let + pkgs = import nixpkgs {inherit overlays system;}; + in rec { + devShells.default = pkgs.mkShell { + nativeBuildInputs = [ + pkgs.zigpkgs."0.11.0" + pkgs.llvmPackages_16.clang-unwrapped + ]; + + buildInputs = [ + # we need a version of bash capable of being interactive + # as opposed to a bash just used for building this flake + # in non-interactive mode + pkgs.bashInteractive + ]; + + shellHook = '' + # once we set SHELL to point to the interactive bash, neovim will + # launch the correct $SHELL in its :terminal + export SHELL=${pkgs.bashInteractive}/bin/bash + ''; + }; + + # For compatibility with older versions of the `nix` binary + devShell = self.devShells.${system}.default; + } + ); +} diff --git a/modules/foundation-libc/include/string.h b/modules/foundation-libc/include/string.h index b9f0741f6..0392b02de 100644 --- a/modules/foundation-libc/include/string.h +++ b/modules/foundation-libc/include/string.h @@ -3,28 +3,28 @@ #include -void * memchr(const void *s, int c, size_t n); -int memcmp(const void *s1, const void *s2, size_t n); -void * memcpy(void * restrict s1, const void * restrict s2, size_t n); -void * memmove(void *s1, const void *s2, size_t n); -void * memset(void *s, int c, size_t n); -char * strcat(char * restrict s1, const char * restrict s2); -char * strchr( const char *str, int ch ); -int strcmp(const char *s1, const char *s2); -char * strcpy(char * restrict s1, const char * restrict s2); -size_t strcspn(const char *s1, const char *s2); +void * memchr(void const * s, int c, size_t n); +int memcmp(void const * s1, void const * s2, size_t n); +void * memcpy(void * restrict s1, void const * restrict s2, size_t n); +void * memmove(void * s1, void const * s2, size_t n); +void * memset(void * s, int c, size_t n); +char * strcat(char * restrict s1, char const * restrict s2); +char * strchr(char const * str, int ch); +int strcmp(char const * s1, char const * s2); +char * strcpy(char * restrict s1, char const * restrict s2); +size_t strcspn(char const * s1, char const * s2); char * strerror(int errnum); -size_t strlen( const char *str ); -char * strncat(char * restrict s1, const char * restrict s2, size_t n); -int strncmp( const char* lhs, const char* rhs, size_t count ); -char * strncpy(char * restrict s1, const char * restrict s2, size_t n); -char * strpbrk(const char *s1, const char *s2); -char * strrchr(const char *s, int c); -size_t strspn(const char *s1, const char *s2); -char * strstr(const char *s1, const char *s2); -char * strtok(char * restrict s1, const char * restrict s2); +size_t strlen(char const * str); +char * strncat(char * restrict s1, char const * restrict s2, size_t n); +int strncmp(char const * lhs, char const * rhs, size_t count); +char * strncpy(char * restrict s1, char const * restrict s2, size_t n); +char * strpbrk(char const * s1, char const * s2); +char * strrchr(char const * s, int c); +size_t strspn(char const * s1, char const * s2); +char * strstr(char const * s1, char const * s2); +char * strtok(char * restrict s1, char const * restrict s2); -// locale dependent: size_t strxfrm(char * restrict s1, const char * restrict s2, size_t n); -// locale dependent: int strcoll(const char *s1, const char *s2); +// locale dependent: size_t strxfrm(char * restrict s1, const char * restrict +// s2, size_t n); locale dependent: int strcoll(const char *s1, const char *s2); #endif From 8c3244c8a0fafdc234a4b2c69efb601d967c53c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=22xq=22=20Quei=C3=9Fner?= Date: Fri, 5 Jan 2024 10:19:32 +0100 Subject: [PATCH 10/23] Adds header validator, implements some header contents. --- modules/foundation-libc/README.md | 68 +++--- modules/foundation-libc/build.zig | 60 +++-- modules/foundation-libc/include/ctype.h | 17 ++ modules/foundation-libc/include/errno.h | 8 + modules/foundation-libc/include/math.h | 217 ++++++++++++++++++ modules/foundation-libc/include/setjmp.h | 6 + modules/foundation-libc/include/stdlib.h | 2 +- modules/foundation-libc/src/modules/ctype.zig | 15 ++ modules/foundation-libc/src/modules/errno.zig | 5 + .../tests/syntactic-validation.c | 12 + 10 files changed, 363 insertions(+), 47 deletions(-) create mode 100644 modules/foundation-libc/tests/syntactic-validation.c diff --git a/modules/foundation-libc/README.md b/modules/foundation-libc/README.md index 3610f69ff..51ae4236e 100644 --- a/modules/foundation-libc/README.md +++ b/modules/foundation-libc/README.md @@ -44,6 +44,7 @@ Which functions belong into which header can be figured out by taking a look at - Try not to use macros at all - Use `clang-format` with the provided style file. + ## Links - [C11 Standard](https://www.iso.org/standard/57853.html) @@ -53,41 +54,44 @@ Which functions belong into which header can be figured out by taking a look at - [cppreference on freestanding](https://en.cppreference.com/w/cpp/freestanding) - [IBM libc functions](https://www.ibm.com/docs/en/i/7.5?topic=extensions-standard-c-library-functions-table-by-name) (function to header map) - [cppreference](https://en.cppreference.com/w/c) +- [clang-format style options](https://releases.llvm.org/16.0.0/tools/clang/docs/ClangFormatStyleOptions.html) ## Status +โณ (not started), ๐Ÿ›  (work in progress), โš ๏ธ (partial support), โœ… (full support), โŒ (no support), ๐Ÿ”ฎ (potential future support), ๐Ÿ”€ (implemented by compiler) -- โŒ `assert.h` Conditionally compiled macro that compares its argument to zero -- โŒ `complex.h` (since C99) Complex number arithmetic -- โณ `ctype.h` Functions to determine the type contained in character data -- โณ `errno.h` Macros reporting error conditions -- ๐Ÿ”ฎ `fenv.h` (since C99) Floating-point environment -- ๐Ÿ”€ `float.h` Limits of floating-point types -- โณ `inttypes.h` (since C99) Format conversion of integer types -- ๐Ÿ”€ `iso646.h` (since C95) Alternative operator spellings -- ๐Ÿ”€ `limits.h` Ranges of integer types -- โŒ `locale.h` Localization utilities -- โณ `math.h` Common mathematics functions -- โณ `setjmp.h` Nonlocal jumps -- โŒ `signal.h` Signal handling -- ๐Ÿ”€ `stdalign.h` (since C11) alignas and alignof convenience macros -- ๐Ÿ”€ `stdarg.h` Variable arguments -- ๐Ÿ”ฎ `stdatomic.h` (since C11) Atomic operations -- ๐Ÿ”ฎ `stdbit.h` (since C23) Macros to work with the byte and bit representations of types -- ๐Ÿ”€ `stdbool.h` (since C99) Macros for boolean type -- ๐Ÿ”ฎ `stdckdint.h` (since C23) macros for performing checked integer arithmetic -- ๐Ÿ”€ `stddef.h` Common macro definitions -- ๐Ÿ”€ `stdint.h` (since C99) Fixed-width integer types -- โŒ `stdio.h` Input/output -- ๐Ÿ›  `stdlib.h` General utilities: memory management, program utilities, string conversions, random numbers, algorithms -- โณ `stdnoreturn.h` (since C11) noreturn convenience macro -- ๐Ÿ›  `string.h` String handling -- โณ `tgmath.h` (since C99) Type-generic math (macros wrapping math.h and complex.h) -- โŒ `threads.h` (since C11) Thread library -- โŒ `time.h` Time/date utilities -- โณ `uchar.h` (since C11) UTF-16 and UTF-32 character utilities -- โŒ `wchar.h` (since C95) Extended multibyte and wide character utilities -- โŒ `wctype.h` (since C95) Functions to determine the type contained in wide character data +| Header File | Header Status | Implementation Status | Description | +| --------------- | ------------- | --------------------- | ------------------------------------------------------------------------------------------------------- | +| `assert.h` | โŒ | | Conditionally compiled macro that compares its argument to zero | +| `complex.h` | โŒ | | (since C99) Complex number arithmetic | +| `ctype.h` | โœ… | โณ | Functions to determine the type contained in character data | +| `errno.h` | โœ… | โœ… | Macros reporting error conditions | +| `fenv.h` | ๐Ÿ”ฎ | | (since C99) Floating-point environment | +| `float.h` | ๐Ÿ”€ | | Limits of floating-point types | +| `inttypes.h` | โณ | โณ | (since C99) Format conversion of integer types | +| `iso646.h` | ๐Ÿ”€ | | (since C95) Alternative operator spellings | +| `limits.h` | ๐Ÿ”€ | | Ranges of integer types | +| `locale.h` | โŒ | | Localization utilities | +| `math.h` | ๐Ÿ›  | โณ | Common mathematics functions | +| `setjmp.h` | ๐Ÿ›  | โณ | Nonlocal jumps | +| `signal.h` | โŒ | | Signal handling | +| `stdalign.h` | ๐Ÿ”€ | | (since C11) alignas and alignof convenience macros | +| `stdarg.h` | ๐Ÿ”€ | | Variable arguments | +| `stdatomic.h` | ๐Ÿ”ฎ | | (since C11) Atomic operations | +| `stdbit.h` | ๐Ÿ”ฎ | | (since C23) Macros to work with the byte and bit representations of types | +| `stdbool.h` | ๐Ÿ”€ | | (since C99) Macros for boolean type | +| `stdckdint.h` | ๐Ÿ”ฎ | | (since C23) macros for performing checked integer arithmetic | +| `stddef.h` | ๐Ÿ”€ | | Common macro definitions | +| `stdint.h` | ๐Ÿ”€ | | (since C99) Fixed-width integer types | +| `stdio.h` | โŒ | | Input/output | +| `stdlib.h` | ๐Ÿ›  | ๐Ÿ›  | General utilities: memory management, program utilities, string conversions, random numbers, algorithms | +| `stdnoreturn.h` | โณ | | (since C11) noreturn convenience macro | +| `string.h` | โœ… | ๐Ÿ›  | String handling | +| `tgmath.h` | โณ | โณ | (since C99) Type-generic math (macros wrapping math.h and complex.h) | +| `threads.h` | โŒ | | (since C11) Thread library | +| `time.h` | โŒ | | Time/date utilities | +| `uchar.h` | โณ | โณ | (since C11) UTF-16 and UTF-32 character utilities | +| `wchar.h` | โŒ | | (since C95) Extended multibyte and wide character utilities | +| `wctype.h` | โŒ | | (since C95) Functions to determine the type contained in wide character data | -โณ (not started), ๐Ÿ›  (work in progress), โš ๏ธ (partial support), โœ… (full support), โŒ (no support), ๐Ÿ”ฎ (potential future support), ๐Ÿ”€ (implemented by compiler) diff --git a/modules/foundation-libc/build.zig b/modules/foundation-libc/build.zig index 06ff762ed..f6887eb07 100644 --- a/modules/foundation-libc/build.zig +++ b/modules/foundation-libc/build.zig @@ -4,19 +4,6 @@ pub const include_path = std.Build.LazyPath{ .cwd_relative = sdk_root ++ "/include", }; -const header_files = [_][]const u8{ - "ctype.h", - "errno.h", - "inttypes.h", - "math.h", - "setjmp.h", - "stdlib.h", - "stdnoreturn.h", - "string.h", - "tgmath.h", - "uchar.h", -}; - /// Creates a new instance of the libc target. pub fn createLibrary(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile { const libc = b.addStaticLibrary(.{ @@ -44,10 +31,55 @@ pub fn build(b: *std.Build) void { header_name, ).step); } + + // test suite: + { + // Check if the syntax of all of our header files is valid: + const syntax_validator = b.addStaticLibrary(.{ + .name = "syntax-validator", + .target = .{}, // just build for the host + .optimize = .Debug, + }); + syntax_validator.addCSourceFile(.{ + .file = .{ .path = "tests/syntactic-validation.c" }, + .flags = &common_c_flags, + }); + syntax_validator.addIncludePath(include_path); + _ = syntax_validator.getEmittedBin(); + + // Just compile, do not install: + b.getInstallStep().dependOn(&syntax_validator.step); + } } +const header_files = [_][]const u8{ + "ctype.h", + "errno.h", + "inttypes.h", + "math.h", + "setjmp.h", + "stdlib.h", + "stdnoreturn.h", + "string.h", + "tgmath.h", + "uchar.h", +}; + +const common_c_flags = [_][]const u8{ + "-std=c11", // target C standard + // + "-Werror", // we do not allow warnings + "-Wall", + "-Wextra", + "-Wmost", + "-Weverything", // this might be dropped later as we just want to find all potential warnings and enable/disable them as required. + // + "-Wno-reserved-macro-identifier", // we actually want to implement those! + "-Wno-reserved-identifier", // we actually want to implement those! +}; + const sdk_root = computeSdkRoot(); fn computeSdkRoot() []const u8 { - return std.fs.path.dirname(@src().path).?; + return std.fs.path.dirname(@src().file).?; } diff --git a/modules/foundation-libc/include/ctype.h b/modules/foundation-libc/include/ctype.h index 0ec9a2c99..feeed0dc4 100644 --- a/modules/foundation-libc/include/ctype.h +++ b/modules/foundation-libc/include/ctype.h @@ -1,4 +1,21 @@ #ifndef _FOUNDATION_LIBC_CTYPE_H_ #define _FOUNDATION_LIBC_CTYPE_H_ +// TODO: #define EOF (-1) + +int isalnum(int c); +int isalpha(int c); +int isblank(int c); +int iscntrl(int c); +int isdigit(int c); +int isgraph(int c); +int islower(int c); +int isprint(int c); +int ispunct(int c); +int isspace(int c); +int isupper(int c); +int isxdigit(int c); +int tolower(int c); +int toupper(int c); + #endif diff --git a/modules/foundation-libc/include/errno.h b/modules/foundation-libc/include/errno.h index be0bbe1c0..f00e1f1f2 100644 --- a/modules/foundation-libc/include/errno.h +++ b/modules/foundation-libc/include/errno.h @@ -1,4 +1,12 @@ #ifndef _FOUNDATION_LIBC_ERRNO_H_ #define _FOUNDATION_LIBC_ERRNO_H_ +#define EDOM 1 +#define EILSEQ 2 +#define ERANGE 3 + +#define errno (*__get_errno()) + +int * __get_errno(void); + #endif diff --git a/modules/foundation-libc/include/math.h b/modules/foundation-libc/include/math.h index 6f3fdad00..833cbf69a 100644 --- a/modules/foundation-libc/include/math.h +++ b/modules/foundation-libc/include/math.h @@ -1,4 +1,221 @@ #ifndef _FOUNDATION_LIBC_MATH_H_ #define _FOUNDATION_LIBC_MATH_H_ +typedef float float_t; +typedef double double_t; + +#define FLT_EVAL_METHOD 0 + +#define HUGE_VAL +#define HUGE_VALF +#define HUGE_VALL + +#define INFINITY +#define NAN + +#define FP_INFINITE +#define FP_NAN +#define FP_NORMAL +#define FP_SUBNORMAL +#define FP_ZERO + +#define FP_FAST_FMA + +#define FP_FAST_FMAF +#define FP_FAST_FMAL + +#define FP_ILOGB0 +#define FP_ILOGBNAN + +#define MATH_ERRNO 1 +#define MATH_ERREXCEPT 2 + +#define math_errhandling + +#define fpclassify(x) +#define isfinite(x) +#define isinf(x) +#define isnan(x) +#define isnormal(x) +#define signbit(x) + +double acos(double x); +float acosf(float x); +long double acosl(long double x); +double asin(double x); +float asinf(float x); +long double asinl(long double x); +double atan(double x); +float atanf(float x); +long double atanl(long double x); +double atan2(double y, double x); +float atan2f(float y, float x); +long double atan2l(long double y, long double x); +double cos(double x); +float cosf(float x); +long double cosl(long double x); +double sin(double x); +float sinf(float x); +long double sinl(long double x); +double tan(double x); +float tanf(float x); +long double tanl(long double x); +double acosh(double x); +float acoshf(float x); +long double acoshl(long double x); +double asinh(double x); +float asinhf(float x); +long double asinhl(long double x); +double atanh(double x); +float atanhf(float x); +long double atanhl(long double x); +double cosh(double x); +float coshf(float x); +long double coshl(long double x); +double sinh(double x); +float sinhf(float x); +long double sinhl(long double x); +double tanh(double x); +float tanhf(float x); +long double tanhl(long double x); +double exp(double x); +float expf(float x); +long double expl(long double x); +double exp2(double x); +float exp2f(float x); +long double exp2l(long double x); +double expm1(double x); +float expm1f(float x); +long double expm1l(long double x); +double frexp(double value, int * exp); +float frexpf(float value, int * exp); +long double frexpl(long double value, int * exp); +int ilogb(double x); +int ilogbf(float x); +int ilogbl(long double x); +double ldexp(double x, int exp); +float ldexpf(float x, int exp); +long double ldexpl(long double x, int exp); +double log(double x); +float logf(float x); +long double logl(long double x); +double log10(double x); +float log10f(float x); +long double log10l(long double x); +double log1p(double x); +float log1pf(float x); +long double log1pl(long double x); +double log2(double x); +float log2f(float x); +long double log2l(long double x); +double logb(double x); +float logbf(float x); +long double logbl(long double x); +double modf(double value, double * iptr); +float modff(float value, float * iptr); +long double modfl(long double value, long double * iptr); +double scalbn(double x, int n); +float scalbnf(float x, int n); +long double scalbnl(long double x, int n); +double scalbln(double x, long int n); +float scalblnf(float x, long int n); +long double scalblnl(long double x, long int n); +double cbrt(double x); +float cbrtf(float x); +long double cbrtl(long double x); +double fabs(double x); +float fabsf(float x); +long double fabsl(long double x); +double hypot(double x, double y); +float hypotf(float x, float y); +long double hypotl(long double x, long double y); +double pow(double x, double y); +float powf(float x, float y); +long double powl(long double x, long double y); +double sqrt(double x); +float sqrtf(float x); +long double sqrtl(long double x); +double erf(double x); +float erff(float x); +long double erfl(long double x); +double erfc(double x); +float erfcf(float x); +long double erfcl(long double x); +double lgamma(double x); +float lgammaf(float x); +long double lgammal(long double x); +double tgamma(double x); +float tgammaf(float x); +long double tgammal(long double x); +double ceil(double x); +float ceilf(float x); +long double ceill(long double x); +double floor(double x); +float floorf(float x); +long double floorl(long double x); +double nearbyint(double x); +float nearbyintf(float x); +long double nearbyintl(long double x); +double rint(double x); +float rintf(float x); +long double rintl(long double x); +long int lrint(double x); +long int lrintf(float x); +long int lrintl(long double x); +long long int llrint(double x); +long long int llrintf(float x); +long long int llrintl(long double x); +double round(double x); +float roundf(float x); +long double roundl(long double x); +long int lround(double x); +long int lroundf(float x); +long int lroundl(long double x); +long long int llround(double x); +long long int llroundf(float x); +long long int llroundl(long double x); +double trunc(double x); +float truncf(float x); +long double truncl(long double x); +double fmod(double x, double y); +float fmodf(float x, float y); +long double fmodl(long double x, long double y); +double remainder(double x, double y); +float remainderf(float x, float y); +long double remainderl(long double x, long double y); +double remquo(double x, double y, int * quo); +float remquof(float x, float y, int * quo); +long double remquol(long double x, long double y, int * quo); +double copysign(double x, double y); +float copysignf(float x, float y); +long double copysignl(long double x, long double y); +double nan(char const * tagp); +float nanf(char const * tagp); +long double nanl(char const * tagp); +double nextafter(double x, double y); +float nextafterf(float x, float y); +long double nextafterl(long double x, long double y); +double nexttoward(double x, long double y); +float nexttowardf(float x, long double y); +long double nexttowardl(long double x, long double y); +double fdim(double x, double y); +float fdimf(float x, float y); +long double fdiml(long double x, long double y); +double fmax(double x, double y); +float fmaxf(float x, float y); +long double fmaxl(long double x, long double y); +double fmin(double x, double y); +float fminf(float x, float y); +long double fminl(long double x, long double y); +double fma(double x, double y, double z); +float fmaf(float x, float y, float z); +long double fmal(long double x, long double y, long double z); + +#define isgreater(x, y) +#define isgreaterequal(x, y) +#define isless(x, y) +#define islessequal(x, y) +#define islessgreater(x, y) +#define isunordered(x, y) + #endif diff --git a/modules/foundation-libc/include/setjmp.h b/modules/foundation-libc/include/setjmp.h index abe7d0a91..7481adc71 100644 --- a/modules/foundation-libc/include/setjmp.h +++ b/modules/foundation-libc/include/setjmp.h @@ -1,4 +1,10 @@ #ifndef _FOUNDATION_LIBC_SETJMP_H_ #define _FOUNDATION_LIBC_SETJMP_H_ +typedef unsigned int jmp_buf[1]; + +#define setjmp(env) + +_Noreturn void longjmp(jmp_buf env, int val); + #endif diff --git a/modules/foundation-libc/include/stdlib.h b/modules/foundation-libc/include/stdlib.h index 9d6a800e3..a3fb8ee33 100644 --- a/modules/foundation-libc/include/stdlib.h +++ b/modules/foundation-libc/include/stdlib.h @@ -1,6 +1,6 @@ #ifndef _FOUNDATION_LIBC_STDLIB_H_ #define _FOUNDATION_LIBC_STDLIB_H_ -int atoi( const char *str ); +int atoi(char const * str); #endif diff --git a/modules/foundation-libc/src/modules/ctype.zig b/modules/foundation-libc/src/modules/ctype.zig index d88fdd3d8..2f3bee66e 100644 --- a/modules/foundation-libc/src/modules/ctype.zig +++ b/modules/foundation-libc/src/modules/ctype.zig @@ -1,3 +1,18 @@ //! implementation of `ctype.h` const std = @import("std"); + +// TODO: isalnum +// TODO: isalpha +// TODO: isblank +// TODO: iscntrl +// TODO: isdigit +// TODO: isgraph +// TODO: islower +// TODO: isprint +// TODO: ispunct +// TODO: isspace +// TODO: isupper +// TODO: isxdigit +// TODO: tolower +// TODO: toupper diff --git a/modules/foundation-libc/src/modules/errno.zig b/modules/foundation-libc/src/modules/errno.zig index 8f46470ad..751ddafd3 100644 --- a/modules/foundation-libc/src/modules/errno.zig +++ b/modules/foundation-libc/src/modules/errno.zig @@ -2,3 +2,8 @@ const std = @import("std"); +threadlocal var errno: c_int = 0; + +export fn __get_errno() *c_int { + return &errno; +} diff --git a/modules/foundation-libc/tests/syntactic-validation.c b/modules/foundation-libc/tests/syntactic-validation.c new file mode 100644 index 000000000..6f730d6b6 --- /dev/null +++ b/modules/foundation-libc/tests/syntactic-validation.c @@ -0,0 +1,12 @@ +// files must be included as non-system includes to trigger warnings: + +#include "ctype.h" +#include "errno.h" +#include "inttypes.h" +#include "math.h" +#include "setjmp.h" +#include "stdlib.h" +#include "stdnoreturn.h" +#include "string.h" +#include "tgmath.h" +#include "uchar.h" From c42c1a9630211830acdc590630582646303e6d56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=22xq=22=20Quei=C3=9Fner?= Date: Fri, 5 Jan 2024 11:09:31 +0100 Subject: [PATCH 11/23] Even more work on the headers, adds more validation code --- modules/foundation-libc/.clang-format | 15 +++- modules/foundation-libc/README.md | 6 +- modules/foundation-libc/build.zig | 70 ++++++++++++++----- modules/foundation-libc/flake.nix | 5 +- modules/foundation-libc/include/stdnoreturn.h | 4 -- modules/foundation-libc/include/uchar.h | 14 ++++ .../tests/syntactic-validation.c | 14 +++- 7 files changed, 100 insertions(+), 28 deletions(-) delete mode 100644 modules/foundation-libc/include/stdnoreturn.h diff --git a/modules/foundation-libc/.clang-format b/modules/foundation-libc/.clang-format index fc5590bc9..8951bebb2 100644 --- a/modules/foundation-libc/.clang-format +++ b/modules/foundation-libc/.clang-format @@ -1,17 +1,26 @@ --- Language: Cpp BasedOnStyle: LLVM + +# basic indentation is 4 spaces, indented by spaces: IndentWidth: 4 TabWidth: 4 UseTab: Never -QualifierAlignment: Right # use east const for better readability -PointerAlignment: Middle # do not tack pointer to type nor value +# use east const for better readability: +QualifierAlignment: Right + +# do not tack pointer to type nor value: +PointerAlignment: Middle # align function names and keep them apart at least one line AlignConsecutiveDeclarations: AcrossEmptyLines -SortIncludes: CaseSensitive +# we sort includes to prevent merge conflicts: +SortIncludes: CaseSensitive + +# disable column limit: +ColumnLimit: 0 --- diff --git a/modules/foundation-libc/README.md b/modules/foundation-libc/README.md index 51ae4236e..1573309a6 100644 --- a/modules/foundation-libc/README.md +++ b/modules/foundation-libc/README.md @@ -13,6 +13,7 @@ The first goal is to reach full C11 *freestanding* support. - No allocator (ship your own!) - No support for functions that require an operating system of sorts in the background. - No support for `wchar_t` and `wchar.h` as it isn't portable between compilers. +- Multi-byte character strings are implemented as UTF-8. ## Development @@ -52,6 +53,7 @@ Which functions belong into which header can be figured out by taking a look at - [ziglibc](https://github.com/marler8997/ziglibc) - [libc-test](https://wiki.musl-libc.org/libc-test.html) by musl - [cppreference on freestanding](https://en.cppreference.com/w/cpp/freestanding) +- [GCC on freestanding](https://gcc.gnu.org/onlinedocs/gcc/Standards.html#C-Language) - [IBM libc functions](https://www.ibm.com/docs/en/i/7.5?topic=extensions-standard-c-library-functions-table-by-name) (function to header map) - [cppreference](https://en.cppreference.com/w/c) - [clang-format style options](https://releases.llvm.org/16.0.0/tools/clang/docs/ClangFormatStyleOptions.html) @@ -85,12 +87,12 @@ Which functions belong into which header can be figured out by taking a look at | `stdint.h` | ๐Ÿ”€ | | (since C99) Fixed-width integer types | | `stdio.h` | โŒ | | Input/output | | `stdlib.h` | ๐Ÿ›  | ๐Ÿ›  | General utilities: memory management, program utilities, string conversions, random numbers, algorithms | -| `stdnoreturn.h` | โณ | | (since C11) noreturn convenience macro | +| `stdnoreturn.h` | ๐Ÿ”€ | | (since C11) noreturn convenience macro | | `string.h` | โœ… | ๐Ÿ›  | String handling | | `tgmath.h` | โณ | โณ | (since C99) Type-generic math (macros wrapping math.h and complex.h) | | `threads.h` | โŒ | | (since C11) Thread library | | `time.h` | โŒ | | Time/date utilities | -| `uchar.h` | โณ | โณ | (since C11) UTF-16 and UTF-32 character utilities | +| `uchar.h` | ๐Ÿ›  | โณ | (since C11) UTF-16 and UTF-32 character utilities | | `wchar.h` | โŒ | | (since C95) Extended multibyte and wide character utilities | | `wctype.h` | โŒ | | (since C95) Functions to determine the type contained in wide character data | diff --git a/modules/foundation-libc/build.zig b/modules/foundation-libc/build.zig index f6887eb07..2a5340cfe 100644 --- a/modules/foundation-libc/build.zig +++ b/modules/foundation-libc/build.zig @@ -22,6 +22,10 @@ pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); + // check if the host has a gcc or clang available: + const maybe_gcc = b.findProgram(&.{"gcc"}, &.{}) catch null; + const maybe_clang = b.findProgram(&.{"clang"}, &.{}) catch null; + const libc = createLibrary(b, target, optimize); b.installArtifact(libc); @@ -34,21 +38,56 @@ pub fn build(b: *std.Build) void { // test suite: { - // Check if the syntax of all of our header files is valid: - const syntax_validator = b.addStaticLibrary(.{ - .name = "syntax-validator", - .target = .{}, // just build for the host - .optimize = .Debug, - }); - syntax_validator.addCSourceFile(.{ - .file = .{ .path = "tests/syntactic-validation.c" }, - .flags = &common_c_flags, - }); - syntax_validator.addIncludePath(include_path); - _ = syntax_validator.getEmittedBin(); - - // Just compile, do not install: - b.getInstallStep().dependOn(&syntax_validator.step); + const syntax_validator_source: std.Build.LazyPath = .{ .path = "tests/syntactic-validation.c" }; + + // use the shipped C compiler to validate our code: + { + // Check if the syntax of all of our header files is valid: + const syntax_validator = b.addStaticLibrary(.{ + .name = "syntax-validator", + .target = .{}, // just build for the host + .optimize = .Debug, + }); + syntax_validator.addCSourceFile(.{ + .file = syntax_validator_source, + .flags = &common_c_flags, + }); + syntax_validator.addIncludePath(include_path); + _ = syntax_validator.getEmittedBin(); + + // Just compile, do not install: + b.getInstallStep().dependOn(&syntax_validator.step); + } + + // use the host C compilers to validate our code: + for ([_]?[]const u8{ maybe_gcc, maybe_clang }) |maybe_cc| { + const cc = maybe_cc orelse continue; + + const ext_compiler = b.addSystemCommand(&.{cc}); + + // just compile every time, we don't have dir caching + // so changes on the headers wouldn't re-trigger this + ext_compiler.has_side_effects = true; + + ext_compiler.addPrefixedDirectoryArg("-I", include_path); + + ext_compiler.addArg("-c"); // compile only + ext_compiler.addArg("-O0"); // no optimization for fast compiles + ext_compiler.addArg("-ffreestanding"); // we require freestanding environment + ext_compiler.addArg("-nostdlib"); // do not try to link anything useful + + // turn on warnings + ext_compiler.addArg("-Werror"); + ext_compiler.addArg("-Wall"); + ext_compiler.addArg("-Wextra"); + + ext_compiler.addFileArg(syntax_validator_source); + + ext_compiler.addArg("-o"); + ext_compiler.addArg(b.pathJoin(&.{ b.makeTempPath(), "dummy" })); // we don't really care where this ends up + + b.getInstallStep().dependOn(&ext_compiler.step); + } } } @@ -59,7 +98,6 @@ const header_files = [_][]const u8{ "math.h", "setjmp.h", "stdlib.h", - "stdnoreturn.h", "string.h", "tgmath.h", "uchar.h", diff --git a/modules/foundation-libc/flake.nix b/modules/foundation-libc/flake.nix index c93025a82..9a4bfaccd 100644 --- a/modules/foundation-libc/flake.nix +++ b/modules/foundation-libc/flake.nix @@ -38,7 +38,7 @@ devShells.default = pkgs.mkShell { nativeBuildInputs = [ pkgs.zigpkgs."0.11.0" - pkgs.llvmPackages_16.clang-unwrapped + pkgs.llvmPackages_17.clangUseLLVM ]; buildInputs = [ @@ -48,6 +48,9 @@ pkgs.bashInteractive ]; + # see https://github.com/NixOS/nixpkgs/issues/18995 + hardeningDisable = ["all"]; + shellHook = '' # once we set SHELL to point to the interactive bash, neovim will # launch the correct $SHELL in its :terminal diff --git a/modules/foundation-libc/include/stdnoreturn.h b/modules/foundation-libc/include/stdnoreturn.h deleted file mode 100644 index 0aa491d4f..000000000 --- a/modules/foundation-libc/include/stdnoreturn.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef _FOUNDATION_LIBC_STDNORETURN_H_ -#define _FOUNDATION_LIBC_STDNORETURN_H_ - -#endif diff --git a/modules/foundation-libc/include/uchar.h b/modules/foundation-libc/include/uchar.h index c0cf95031..634470ca1 100644 --- a/modules/foundation-libc/include/uchar.h +++ b/modules/foundation-libc/include/uchar.h @@ -1,4 +1,18 @@ #ifndef _FOUNDATION_LIBC_UCHAR_H_ #define _FOUNDATION_LIBC_UCHAR_H_ +#include + +typedef struct { + int dummy; // TODO: fill in internal multi-byte conversion state +} mbstate_t; + +typedef uint_least16_t char16_t; +typedef uint_least32_t char32_t; + +size_t mbrtoc16(char16_t * restrict pc16, char const * restrict s, size_t n, mbstate_t * restrict ps); +size_t c16rtomb(char * restrict s, char16_t c16, mbstate_t * restrict ps); +size_t mbrtoc32(char32_t * restrict pc32, char const * restrict s, size_t n, mbstate_t * restrict ps); +size_t c32rtomb(char * restrict s, char32_t c32, mbstate_t * restrict ps); + #endif diff --git a/modules/foundation-libc/tests/syntactic-validation.c b/modules/foundation-libc/tests/syntactic-validation.c index 6f730d6b6..e0092506b 100644 --- a/modules/foundation-libc/tests/syntactic-validation.c +++ b/modules/foundation-libc/tests/syntactic-validation.c @@ -1,12 +1,22 @@ -// files must be included as non-system includes to trigger warnings: +// our own files must be included as non-system includes to trigger warnings: #include "ctype.h" #include "errno.h" #include "inttypes.h" #include "math.h" #include "setjmp.h" #include "stdlib.h" -#include "stdnoreturn.h" #include "string.h" #include "tgmath.h" #include "uchar.h" + +// those files should be present still, but are compiler provided: +#include +#include +#include +#include +#include +#include +#include +#include +#include From 0f10b1f68a5943528b1b69a41458b6d48db670c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=22xq=22=20Quei=C3=9Fner?= Date: Fri, 5 Jan 2024 11:17:41 +0100 Subject: [PATCH 12/23] Fixes errno to work on single-threaded targets, adds CI --- .../.github/workflows/build.yml | 28 +++++++++++++++++++ modules/foundation-libc/build.zig | 3 ++ modules/foundation-libc/src/modules/errno.zig | 12 ++++++-- 3 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 modules/foundation-libc/.github/workflows/build.yml diff --git a/modules/foundation-libc/.github/workflows/build.yml b/modules/foundation-libc/.github/workflows/build.yml new file mode 100644 index 000000000..f48db1c19 --- /dev/null +++ b/modules/foundation-libc/.github/workflows/build.yml @@ -0,0 +1,28 @@ +name: Continuous Integration + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build: + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + single_thread: [true, false] + + runs-on: ${{ matrix.os }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Zig + uses: goto-bus-stop/setup-zig@v2 + with: + version: 0.11.0 + + - name: Generate and validate packages + run: | + zig build -Dsingle-threaded=${{ matrix.single_thread }} diff --git a/modules/foundation-libc/build.zig b/modules/foundation-libc/build.zig index 2a5340cfe..926565c64 100644 --- a/modules/foundation-libc/build.zig +++ b/modules/foundation-libc/build.zig @@ -22,11 +22,14 @@ pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); + const single_threaded = b.option(bool, "single-threaded", "Create a single-threaded libc implementation (default: false)") orelse false; + // check if the host has a gcc or clang available: const maybe_gcc = b.findProgram(&.{"gcc"}, &.{}) catch null; const maybe_clang = b.findProgram(&.{"clang"}, &.{}) catch null; const libc = createLibrary(b, target, optimize); + libc.single_threaded = single_threaded; b.installArtifact(libc); for (header_files) |header_name| { diff --git a/modules/foundation-libc/src/modules/errno.zig b/modules/foundation-libc/src/modules/errno.zig index 751ddafd3..a1f4c1ace 100644 --- a/modules/foundation-libc/src/modules/errno.zig +++ b/modules/foundation-libc/src/modules/errno.zig @@ -1,9 +1,17 @@ //! implementation of `errno.h` const std = @import("std"); +const builtin = @import("builtin"); -threadlocal var errno: c_int = 0; +const storage = if (builtin.single_threaded) + struct { + var errno: c_int = 0; // regular variable on single-threaded systems + } +else + struct { + threadlocal var errno: c_int = 0; // thread-local variable on multi-threaded systems + }; export fn __get_errno() *c_int { - return &errno; + return &storage.errno; } From 511e4d78569acbd779a336f1f453cc00f6196e1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=22xq=22=20Quei=C3=9Fner?= Date: Fri, 5 Jan 2024 12:25:00 +0100 Subject: [PATCH 13/23] Adds CI badge to README.md --- modules/foundation-libc/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/foundation-libc/README.md b/modules/foundation-libc/README.md index 1573309a6..2aecec261 100644 --- a/modules/foundation-libc/README.md +++ b/modules/foundation-libc/README.md @@ -1,5 +1,7 @@ # Foundation libc +[![Continuous Integration](https://github.com/ZigEmbeddedGroup/foundation-libc/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/ZigEmbeddedGroup/foundation-libc/actions/workflows/build.yml) + A C standard library that only implements a subset of functions that can be safely used without an operating system. This is called a [freestanding environment](https://en.cppreference.com/w/cpp/freestanding). @@ -19,7 +21,6 @@ The first goal is to reach full C11 *freestanding* support. Zig Version: 0.11 - Run ```sh-session user@microzig ~/foundation-libc $ zig build From 23f92c02b9819630f9558f0cd4b26320cf62277f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=22xq=22=20Quei=C3=9Fner?= Date: Sun, 21 Jan 2024 11:11:17 +0100 Subject: [PATCH 14/23] Implements ctype.h functions, adds support for panics, adds more code validation. --- modules/foundation-libc/README.md | 12 +- modules/foundation-libc/build.zig | 132 ++++++++++++++++- modules/foundation-libc/include/ctype.h | 2 +- .../foundation-libc/include/foundation/libc.h | 25 ++++ modules/foundation-libc/src/libc.zig | 74 ++++++++++ modules/foundation-libc/src/modules/ctype.zig | 137 ++++++++++++++++-- .../tests/syntactic-validation.c | 1 + 7 files changed, 365 insertions(+), 18 deletions(-) create mode 100644 modules/foundation-libc/include/foundation/libc.h diff --git a/modules/foundation-libc/README.md b/modules/foundation-libc/README.md index 2aecec261..cec3d39d3 100644 --- a/modules/foundation-libc/README.md +++ b/modules/foundation-libc/README.md @@ -17,6 +17,16 @@ The first goal is to reach full C11 *freestanding* support. - No support for `wchar_t` and `wchar.h` as it isn't portable between compilers. - Multi-byte character strings are implemented as UTF-8. +## Customization + +Foundation libc doesn't really support much customization/configuration except for the hard required options. + +There is [`foundation/libc.h`](include/foundation/libc.h) which documents the behaviour of all required configurations. + +Right now, the following configurations exist: + +- `foundation_libc_panic_handler`, which allows users to catch detectable undefined behaviour. + ## Development Zig Version: 0.11 @@ -67,7 +77,7 @@ Which functions belong into which header can be figured out by taking a look at | --------------- | ------------- | --------------------- | ------------------------------------------------------------------------------------------------------- | | `assert.h` | โŒ | | Conditionally compiled macro that compares its argument to zero | | `complex.h` | โŒ | | (since C99) Complex number arithmetic | -| `ctype.h` | โœ… | โณ | Functions to determine the type contained in character data | +| `ctype.h` | โœ… | โœ… | Functions to determine the type contained in character data | | `errno.h` | โœ… | โœ… | Macros reporting error conditions | | `fenv.h` | ๐Ÿ”ฎ | | (since C99) Floating-point environment | | `float.h` | ๐Ÿ”€ | | Limits of floating-point types | diff --git a/modules/foundation-libc/build.zig b/modules/foundation-libc/build.zig index 926565c64..7a4f7bdd8 100644 --- a/modules/foundation-libc/build.zig +++ b/modules/foundation-libc/build.zig @@ -19,11 +19,18 @@ pub fn createLibrary(b: *std.Build, target: std.zig.CrossTarget, optimize: std.b } pub fn build(b: *std.Build) void { + const validation_step = b.step("validate", "Runs the test suite and validates everything. Automatically triggered in Debug builds."); + const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); const single_threaded = b.option(bool, "single-threaded", "Create a single-threaded libc implementation (default: false)") orelse false; + // Run validation in debug builds for convenience: + if (optimize == .Debug) { + b.getInstallStep().dependOn(validation_step); + } + // check if the host has a gcc or clang available: const maybe_gcc = b.findProgram(&.{"gcc"}, &.{}) catch null; const maybe_clang = b.findProgram(&.{"clang"}, &.{}) catch null; @@ -41,6 +48,22 @@ pub fn build(b: *std.Build) void { // test suite: { + // Compile for huge amount of targets to detect breakage early on: + for ([_]bool{ false, true }) |validation_single_threaded| { + for (std.enums.values(std.builtin.OptimizeMode)) |validation_optimize| { + for (validation_target_list) |validation_target| { + + // skip everything that cannot support multithreading on freestanding: + if (!validation_single_threaded and !target_can_multithread(validation_target)) + continue; + + const vlc = createLibrary(b, validation_target, validation_optimize); + vlc.single_threaded = validation_single_threaded; + validation_step.dependOn(&vlc.step); + } + } + } + const syntax_validator_source: std.Build.LazyPath = .{ .path = "tests/syntactic-validation.c" }; // use the shipped C compiler to validate our code: @@ -59,7 +82,7 @@ pub fn build(b: *std.Build) void { _ = syntax_validator.getEmittedBin(); // Just compile, do not install: - b.getInstallStep().dependOn(&syntax_validator.step); + validation_step.dependOn(&syntax_validator.step); } // use the host C compilers to validate our code: @@ -89,7 +112,7 @@ pub fn build(b: *std.Build) void { ext_compiler.addArg("-o"); ext_compiler.addArg(b.pathJoin(&.{ b.makeTempPath(), "dummy" })); // we don't really care where this ends up - b.getInstallStep().dependOn(&ext_compiler.step); + validation_step.dependOn(&ext_compiler.step); } } } @@ -104,6 +127,7 @@ const header_files = [_][]const u8{ "string.h", "tgmath.h", "uchar.h", + "foundation/libc.h", }; const common_c_flags = [_][]const u8{ @@ -119,6 +143,110 @@ const common_c_flags = [_][]const u8{ "-Wno-reserved-identifier", // we actually want to implement those! }; +fn target_can_multithread(target: std.zig.CrossTarget) bool { + return switch (target.getCpuArch()) { + .wasm32, + .wasm64, + .msp430, + => false, + + else => true, + }; +} + +const validation_target_list = [_]std.zig.CrossTarget{ + .{}, // regular host platform + .{ .os_tag = .freestanding }, // host platform, but no OS + + // Check several common cpu targets: + + // arm: + .{ .cpu_arch = .arm, .os_tag = .freestanding }, + .{ .cpu_arch = .armeb, .os_tag = .freestanding }, + .{ .cpu_arch = .thumb, .os_tag = .freestanding }, + .{ .cpu_arch = .thumbeb, .os_tag = .freestanding }, + .{ .cpu_arch = .aarch64, .os_tag = .freestanding }, + // .{ .cpu_arch = .aarch64_32, .os_tag = .freestanding }, // error: unknown target triple 'aarch64_32-unknown-unknown-eabi', please use -triple or -arch + .{ .cpu_arch = .aarch64_be, .os_tag = .freestanding }, + + // risc-v: + .{ .cpu_arch = .riscv32, .os_tag = .freestanding }, + .{ .cpu_arch = .riscv64, .os_tag = .freestanding }, + + // intel: + .{ .cpu_arch = .x86_64, .os_tag = .freestanding }, + .{ .cpu_arch = .x86, .os_tag = .freestanding }, + + // mips: + .{ .cpu_arch = .mips, .os_tag = .freestanding }, + .{ .cpu_arch = .mips64, .os_tag = .freestanding }, + .{ .cpu_arch = .mips64el, .os_tag = .freestanding }, + .{ .cpu_arch = .mipsel, .os_tag = .freestanding }, + + // sparc: + .{ .cpu_arch = .sparc, .os_tag = .freestanding }, + .{ .cpu_arch = .sparc64, .os_tag = .freestanding }, + .{ .cpu_arch = .sparcel, .os_tag = .freestanding }, + + // power: + .{ .cpu_arch = .powerpc, .os_tag = .freestanding }, + .{ .cpu_arch = .powerpc64, .os_tag = .freestanding }, + .{ .cpu_arch = .powerpc64le, .os_tag = .freestanding }, + .{ .cpu_arch = .powerpcle, .os_tag = .freestanding }, + + // web assembly: + .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, + .{ .cpu_arch = .wasm64, .os_tag = .freestanding }, + + // nice to have, but broken: + .{ .cpu_arch = .avr, .os_tag = .freestanding }, + // .{ .cpu_arch = .msp430, .os_tag = .freestanding }, // error: unknown target CPU 'generic' + // .{ .cpu_arch = .m68k, .os_tag = .freestanding }, + // .{ .cpu_arch = .xtensa, .os_tag = .freestanding }, + + // Not evaluated if reasonable to check: + // arc + // csky + // hexagon + // hsail + // hsail64 + // kalimba + // lanai + // le32 + // le64 + // loongarch32 + // loongarch64 + // r600 + // s390x + // shave + // spu_2 + // tce + // tcele + // ve + // xcore + + // will never be supported due to their properties: + // spir + // spir64 + // spirv32 + // spirv64 + + // bpfeb + // bpfel + + // renderscript32 + // renderscript64 + + // amdgcn + // amdil + // amdil64 + + // nvptx + // nvptx64 + + // dxil +}; + const sdk_root = computeSdkRoot(); fn computeSdkRoot() []const u8 { diff --git a/modules/foundation-libc/include/ctype.h b/modules/foundation-libc/include/ctype.h index feeed0dc4..c4e7caacb 100644 --- a/modules/foundation-libc/include/ctype.h +++ b/modules/foundation-libc/include/ctype.h @@ -1,7 +1,7 @@ #ifndef _FOUNDATION_LIBC_CTYPE_H_ #define _FOUNDATION_LIBC_CTYPE_H_ -// TODO: #define EOF (-1) +#define EOF (-1) int isalnum(int c); int isalpha(int c); diff --git a/modules/foundation-libc/include/foundation/libc.h b/modules/foundation-libc/include/foundation/libc.h new file mode 100644 index 000000000..7b245c800 --- /dev/null +++ b/modules/foundation-libc/include/foundation/libc.h @@ -0,0 +1,25 @@ +#ifndef _FOUNDATION_LIBC_INTERNALS_H_ +#define _FOUNDATION_LIBC_INTERNALS_H_ + +#include + +/// +/// Panic handler for undefined, but catchable behaviour in safe modes. +/// +/// This will be invoked when Zig detects undefined behaviour at runtime, +/// or when foundation libc can recognize illegal arguments. +/// +/// The function receives a non-terminated pointer to the panic message +/// with `msg_len` bytes of UTF-8 encoded payload. +/// +/// It has a weak default implementation shipped, so just implement this +/// function to plug in your own custom behaviour. +/// The default implementation is done by invoking a `trap` instruction to +/// emit an illegal instruction or otherwise crash the program execution. +/// +/// NOTE: This function must never return, because otherwise, the undefined +/// behaviour will be actually undefined! +/// +void foundation_libc_panic_handler(char const * msg_ptr, size_t msg_len); + +#endif diff --git a/modules/foundation-libc/src/libc.zig b/modules/foundation-libc/src/libc.zig index 5dbdd5b77..447516862 100644 --- a/modules/foundation-libc/src/libc.zig +++ b/modules/foundation-libc/src/libc.zig @@ -1,6 +1,38 @@ const std = @import("std"); +const builtin = @import("builtin"); + +pub const h = @cImport({ + @cInclude("ctype.h"); + @cInclude("errno.h"); + @cInclude("inttypes.h"); + @cInclude("math.h"); + @cInclude("setjmp.h"); + @cInclude("stdlib.h"); + @cInclude("string.h"); + @cInclude("tgmath.h"); + @cInclude("uchar.h"); + @cInclude("foundation/libc.h"); +}); comptime { + // Some assertions over the target platform: + std.debug.assert(@bitSizeOf(c_char) == 8); + + // Ensure hierarchy: + std.debug.assert(@bitSizeOf(c_short) >= @bitSizeOf(c_char)); + std.debug.assert(@bitSizeOf(c_int) >= @bitSizeOf(c_short)); + std.debug.assert(@bitSizeOf(c_long) >= @bitSizeOf(c_int)); + std.debug.assert(@bitSizeOf(c_longlong) >= @bitSizeOf(c_long)); + + // Ensure same-sized signed and unsigned + std.debug.assert(@bitSizeOf(c_ushort) == @bitSizeOf(c_short)); + std.debug.assert(@bitSizeOf(c_uint) == @bitSizeOf(c_int)); + std.debug.assert(@bitSizeOf(c_ulong) == @bitSizeOf(c_long)); + std.debug.assert(@bitSizeOf(c_ulonglong) == @bitSizeOf(c_longlong)); +} + +comptime { + // Drag in all implementations, so they are compiled: _ = @import("modules/ctype.zig"); _ = @import("modules/errno.zig"); _ = @import("modules/math.zig"); @@ -9,3 +41,45 @@ comptime { _ = @import("modules/string.zig"); _ = @import("modules/uchar.zig"); } + +/// Invokes safety-checked undefined behaviour, use this to implement +/// UB checks in the libc itself. +pub fn undefined_behaviour(comptime string: []const u8) noreturn { + switch (builtin.mode) { + // In debug mode, trigger a breakpoint so it's easier to detect the situation + // of the undefined behaviour: + .Debug => { + @breakpoint(); + @panic("UNDEFINED BEHAVIOUR: " ++ string); + }, + + // Safe modes have nice messages with + .ReleaseSafe => @panic("UNDEFINED BEHAVIOUR DETECTED: " ++ string), + + .ReleaseSmall => @panic("UB"), + + .ReleaseFast => unreachable, + } +} + +/// Zig panic handler, forwards panics to `foundation_libc_panic_handler`. +pub fn panic(msg: []const u8, maybe_error_return_trace: ?*std.builtin.StackTrace, maybe_return_address: ?usize) noreturn { + _ = maybe_error_return_trace; + _ = maybe_return_address; + h.foundation_libc_panic_handler(msg.ptr, msg.len); + unreachable; +} + +/// default implementation for `foundation_libc_panic_handler`. +fn fallback_panic_handler(msg_ptr: [*]const u8, msg_len: usize) callconv(.C) noreturn { + _ = msg_ptr; + _ = msg_len; + @trap(); +} +comptime { + @export(fallback_panic_handler, std.builtin.ExportOptions{ + .name = "foundation_libc_panic_handler", + .linkage = .Weak, + .visibility = .default, + }); +} diff --git a/modules/foundation-libc/src/modules/ctype.zig b/modules/foundation-libc/src/modules/ctype.zig index 2f3bee66e..1f6133113 100644 --- a/modules/foundation-libc/src/modules/ctype.zig +++ b/modules/foundation-libc/src/modules/ctype.zig @@ -1,18 +1,127 @@ //! implementation of `ctype.h` +//! +//! The header declares several functions useful for classifying and mapping +//! characters. In all cases the argument is an int, the value of which shall be +//! representable as an unsigned char or shall equal the value of the macro EOF. If the +//! argument has any other value, the behavior is undefined. +//! const std = @import("std"); +const libc = @import("../libc.zig"); -// TODO: isalnum -// TODO: isalpha -// TODO: isblank -// TODO: iscntrl -// TODO: isdigit -// TODO: isgraph -// TODO: islower -// TODO: isprint -// TODO: ispunct -// TODO: isspace -// TODO: isupper -// TODO: isxdigit -// TODO: tolower -// TODO: toupper +// Use an alias to std.ascii to allow potential future replacement +// of the locale implementation: +const locale = std.ascii; + +const EOF = libc.h.EOF; + +/// Convert input to u8, undefined behaviour +fn conv(c: c_int) ?u8 { + if (c == EOF) + return null; + return std.math.cast(u8, c) orelse libc.undefined_behaviour("passed a value that is not unsigned char nor EOF to a ctype function"); +} + +/// The isalnum function tests for any character for which isalpha or isdigit is true. +export fn isalnum(c: c_int) c_int { + const u = conv(c) orelse return EOF; + return @intFromBool(locale.isAlphanumeric(u)); +} + +/// The isalpha function tests for any character for which isupper or islower is true, +/// or any character that is one of a locale-specific set of alphabetic characters for which +/// none of iscntrl, isdigit, ispunct, or isspace is true.200) In the "C" locale, +/// isalpha returns true only for the characters for which isupper or islower is true. +export fn isalpha(c: c_int) c_int { + const u = conv(c) orelse return EOF; + return @intFromBool(locale.isAlphabetic(u)); +} + +/// The isblank function tests for any character that is a standard blank character or is one +/// of a locale-specific set of characters for which isspace is true and that is used to +/// separate words within a line of text. The standard blank characters are the following: +/// space (' '), and horizontal tab ('\t'). In the "C" locale, isblank returns true only +/// for the standard blank characters. +export fn isblank(c: c_int) c_int { + const u = conv(c) orelse return EOF; + return @intFromBool(u == ' ' or u == '\t'); +} + +/// The iscntrl function tests for any control character. +export fn iscntrl(c: c_int) c_int { + const u = conv(c) orelse return EOF; + return @intFromBool(locale.isControl(u)); +} + +/// The isdigit function tests for any decimal-digit character (as defined in 5.2.1). +export fn isdigit(c: c_int) c_int { + const u = conv(c) orelse return EOF; + return @intFromBool(locale.isDigit(u)); +} + +/// The isgraph function tests for any printing character except space (' '). +export fn isgraph(c: c_int) c_int { + const u = conv(c) orelse return EOF; + return @intFromBool(locale.isPrint(u) and (c != ' ')); +} + +/// The islower function tests for any character that is a lowercase letter or is one of a +/// locale-specific set of characters for which none of iscntrl, isdigit, ispunct, or +/// isspace is true. In the "C" locale, islower returns true only for the lowercase +/// letters (as defined in 5.2.1). +export fn islower(c: c_int) c_int { + const u = conv(c) orelse return EOF; + return @intFromBool(locale.isLower(u)); +} + +/// The isprint function tests for any printing character including space (' '). +export fn isprint(c: c_int) c_int { + const u = conv(c) orelse return EOF; + return @intFromBool(locale.isPrint(u)); +} + +/// The ispunct function tests for any printing character that is one of a locale-specific set +/// of punctuation characters for which neither isspace nor isalnum is true. In the "C" +/// locale, ispunct returns true for every printing character for which neither isspace +/// nor isalnum is true. +export fn ispunct(c: c_int) c_int { + const u = conv(c) orelse return EOF; + return @intFromBool(!locale.isWhitespace(u) and !locale.isAlphanumeric(u)); +} + +/// The isspace function tests for any character that is a standard white-space character or +/// is one of a locale-specific set of characters for which isalnum is false. The standard +/// white-space characters are the following: space (' '), form feed ('\f'), new-line +/// ('\n'), carriage return ('\r'), horizontal tab ('\t'), and vertical tab ('\v'). In the +/// "C" locale, isspace returns true only for the standard white-space characters. +export fn isspace(c: c_int) c_int { + const u = conv(c) orelse return EOF; + return @intFromBool(locale.isWhitespace(u)); +} + +/// The isupper function tests for any character that is an uppercase letter or is one of a +/// locale-specific set of characters for which none of iscntrl, isdigit, ispunct, or +/// isspace is true. In the "C" locale, isupper returns true only for the uppercase +/// letters (as defined in 5.2.1). +export fn isupper(c: c_int) c_int { + const u = conv(c) orelse return EOF; + return @intFromBool(locale.isUpper(u)); +} + +/// The isxdigit function tests for any hexadecimal-digit character (as defined in 6.4.4.1). +export fn isxdigit(c: c_int) c_int { + const u = conv(c) orelse return EOF; + return @intFromBool(locale.isHex(u)); +} + +/// The tolower function converts an uppercase letter to a corresponding lowercase letter. +export fn tolower(c: c_int) c_int { + const u = conv(c) orelse return EOF; + return locale.toLower(u); +} + +/// The toupper function converts a lowercase letter to a corresponding uppercase letter. +export fn toupper(c: c_int) c_int { + const u = conv(c) orelse return EOF; + return locale.toUpper(u); +} diff --git a/modules/foundation-libc/tests/syntactic-validation.c b/modules/foundation-libc/tests/syntactic-validation.c index e0092506b..c31050e26 100644 --- a/modules/foundation-libc/tests/syntactic-validation.c +++ b/modules/foundation-libc/tests/syntactic-validation.c @@ -2,6 +2,7 @@ // our own files must be included as non-system includes to trigger warnings: #include "ctype.h" #include "errno.h" +#include "foundation/libc.h" #include "inttypes.h" #include "math.h" #include "setjmp.h" From 04dabf0b7059df7378f47eda09f2cb7055f7b90c Mon Sep 17 00:00:00 2001 From: Matt Knight Date: Sun, 21 Jan 2024 12:45:21 -0800 Subject: [PATCH 15/23] Add assert.h, fix installation of headers (#1) * initial implementation of assert * fix use of return type * fix formatting * install header * change install headers * small fix * small fix --- modules/foundation-libc/build.zig | 13 ++++++------- modules/foundation-libc/include/assert.h | 16 ++++++++++++++++ modules/foundation-libc/src/libc.zig | 3 +++ modules/foundation-libc/src/modules/assert.zig | 14 ++++++++++++++ 4 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 modules/foundation-libc/include/assert.h create mode 100644 modules/foundation-libc/src/modules/assert.zig diff --git a/modules/foundation-libc/build.zig b/modules/foundation-libc/build.zig index 7a4f7bdd8..b1df2a968 100644 --- a/modules/foundation-libc/build.zig +++ b/modules/foundation-libc/build.zig @@ -14,6 +14,11 @@ pub fn createLibrary(b: *std.Build, target: std.zig.CrossTarget, optimize: std.b }); libc.addIncludePath(.{ .path = "include" }); + for (header_files) |header_name| + libc.installHeader( + b.fmt("include/{s}", .{header_name}), + header_name, + ); return libc; } @@ -39,13 +44,6 @@ pub fn build(b: *std.Build) void { libc.single_threaded = single_threaded; b.installArtifact(libc); - for (header_files) |header_name| { - b.getInstallStep().dependOn(&b.addInstallHeaderFile( - b.fmt("include/{s}", .{header_name}), - header_name, - ).step); - } - // test suite: { // Compile for huge amount of targets to detect breakage early on: @@ -118,6 +116,7 @@ pub fn build(b: *std.Build) void { } const header_files = [_][]const u8{ + "assert.h", "ctype.h", "errno.h", "inttypes.h", diff --git a/modules/foundation-libc/include/assert.h b/modules/foundation-libc/include/assert.h new file mode 100644 index 000000000..81cbd9e9a --- /dev/null +++ b/modules/foundation-libc/include/assert.h @@ -0,0 +1,16 @@ +#ifndef _FOUNDATION_LIBC_ASSERT_H_ +#define _FOUNDATION_LIBC_ASSERT_H_ + +#ifndef NDEBUG +#define assert(expr) +#else +extern void __assert(char const * assertion, char const * file, unsigned line) __attribute__((__noreturn__)); + +#define assert(expr) \ + ((expr) \ + ? void(0) \ + : __assert(#expr, __FILE__, __LINE__)) + +#endif + +#endif diff --git a/modules/foundation-libc/src/libc.zig b/modules/foundation-libc/src/libc.zig index 447516862..fc5c90249 100644 --- a/modules/foundation-libc/src/libc.zig +++ b/modules/foundation-libc/src/libc.zig @@ -2,6 +2,7 @@ const std = @import("std"); const builtin = @import("builtin"); pub const h = @cImport({ + @cInclude("assert.h"); @cInclude("ctype.h"); @cInclude("errno.h"); @cInclude("inttypes.h"); @@ -11,6 +12,7 @@ pub const h = @cImport({ @cInclude("string.h"); @cInclude("tgmath.h"); @cInclude("uchar.h"); + @cInclude("foundation/libc.h"); }); @@ -33,6 +35,7 @@ comptime { comptime { // Drag in all implementations, so they are compiled: + _ = @import("modules/assert.zig"); _ = @import("modules/ctype.zig"); _ = @import("modules/errno.zig"); _ = @import("modules/math.zig"); diff --git a/modules/foundation-libc/src/modules/assert.zig b/modules/foundation-libc/src/modules/assert.zig new file mode 100644 index 000000000..fc181de73 --- /dev/null +++ b/modules/foundation-libc/src/modules/assert.zig @@ -0,0 +1,14 @@ +const std = @import("std"); + +export fn __assert( + assertion: ?[*:0]const u8, + file: ?[*:0]const u8, + line: c_uint, +) noreturn { + var buf: [256]u8 = undefined; + const str = std.fmt.bufPrint(&buf, "assertion failed: '{?s}' in file {?s} line {}", .{ assertion, file, line }) catch { + @panic("assertion failed"); + }; + + @panic(str); +} From c463e287f66ae910afb33d560de8df2cf1f5c835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=22xq=22=20Quei=C3=9Fner?= Date: Tue, 23 Jan 2024 09:14:01 +0100 Subject: [PATCH 16/23] Adds checks for validating assert, improves assertion behaviour based on code size optimization, adds configuration for assertion macro behaviour --- modules/foundation-libc/README.md | 17 +++++++- modules/foundation-libc/build.zig | 26 +++++++++++ modules/foundation-libc/include/assert.h | 43 +++++++++++++++---- .../include/foundation/builtins.h | 37 ++++++++++++++++ .../foundation-libc/src/modules/assert.zig | 20 ++++++--- .../foundation-libc/tests/assert-validator.c | 25 +++++++++++ .../tests/syntactic-validation.c | 1 + 7 files changed, 153 insertions(+), 16 deletions(-) create mode 100644 modules/foundation-libc/include/foundation/builtins.h create mode 100644 modules/foundation-libc/tests/assert-validator.c diff --git a/modules/foundation-libc/README.md b/modules/foundation-libc/README.md index cec3d39d3..a6262abca 100644 --- a/modules/foundation-libc/README.md +++ b/modules/foundation-libc/README.md @@ -27,6 +27,21 @@ Right now, the following configurations exist: - `foundation_libc_panic_handler`, which allows users to catch detectable undefined behaviour. +You can also configure the libc by chosing the build mode: + +- `Debug`: Implements additional safety checks and adds breakpoints in panics. +- `ReleaseSafe`: Keeps the safety checks, but removes breakpoints. +- `ReleaseSmall`: Still keeps a certain amount of safety, but drops long internal strings to reduce code and ram size. +- `ReleaseFast`: Gotta go fast. Drops all safety and assumes all code behaves well. + +There are also certain "usage" configurations that can be chosen to affect behaviour when *using* the headers. Those are implemented as C macros/defines: + +- `FOUNDATION_LIBC_ASSERT` is a global macro that defines how `assert()` should behave: + - `FOUNDATION_LIBC_ASSERT_DEFAULT=0`: Behaves like a regular assert that can print file name, assertion message and line. + - `FOUNDATION_LIBC_ASSERT_NOFILE=1`: Drops the filename from the assertion to reduce code size. + - `FOUNDATION_LIBC_ASSERT_NOMSG=2`: Additionally drops the assertion message from the assertion to reduce code size. + - `FOUNDATION_LIBC_ASSERT_EXPECTED=3`: Replaces `assert(โ€ฆ)` with a construct that tells the compiler the assertion is always met. Makes code very fast. Assertions aren't checked. + ## Development Zig Version: 0.11 @@ -75,7 +90,7 @@ Which functions belong into which header can be figured out by taking a look at | Header File | Header Status | Implementation Status | Description | | --------------- | ------------- | --------------------- | ------------------------------------------------------------------------------------------------------- | -| `assert.h` | โŒ | | Conditionally compiled macro that compares its argument to zero | +| `assert.h` | โœ… | โœ… | Conditionally compiled macro that compares its argument to zero | | `complex.h` | โŒ | | (since C99) Complex number arithmetic | | `ctype.h` | โœ… | โœ… | Functions to determine the type contained in character data | | `errno.h` | โœ… | โœ… | Macros reporting error conditions | diff --git a/modules/foundation-libc/build.zig b/modules/foundation-libc/build.zig index b1df2a968..f73f2855e 100644 --- a/modules/foundation-libc/build.zig +++ b/modules/foundation-libc/build.zig @@ -112,6 +112,32 @@ pub fn build(b: *std.Build) void { validation_step.dependOn(&ext_compiler.step); } + + // Validate all modes of assertion: + for ([_][]const u8{ + "FOUNDATION_LIBC_ASSERT_DEFAULT", + "FOUNDATION_LIBC_ASSERT_NOFILE", + "FOUNDATION_LIBC_ASSERT_NOMSG", + "FOUNDATION_LIBC_ASSERT_EXPECTED", + }) |assert_mode| { + // Check if the syntax of all of our header files is valid: + const assert_validator = b.addStaticLibrary(.{ + .name = "assert-validator", + .target = .{}, // just build for the host + .optimize = .Debug, + }); + assert_validator.addCSourceFile(.{ + .file = .{ .path = "tests/assert-validator.c" }, + .flags = &common_c_flags, + }); + assert_validator.addIncludePath(include_path); + _ = assert_validator.getEmittedBin(); + + assert_validator.defineCMacro("FOUNDATION_LIBC_ASSERT", assert_mode); + + // Just compile, do not install: + validation_step.dependOn(&assert_validator.step); + } } } diff --git a/modules/foundation-libc/include/assert.h b/modules/foundation-libc/include/assert.h index 81cbd9e9a..16c497948 100644 --- a/modules/foundation-libc/include/assert.h +++ b/modules/foundation-libc/include/assert.h @@ -1,16 +1,43 @@ #ifndef _FOUNDATION_LIBC_ASSERT_H_ #define _FOUNDATION_LIBC_ASSERT_H_ -#ifndef NDEBUG -#define assert(expr) -#else -extern void __assert(char const * assertion, char const * file, unsigned line) __attribute__((__noreturn__)); +#include "foundation/builtins.h" + +extern FOUNDATION_LIBC_NORETURN void foundation_libc_assert(char const * assertion, char const * file, unsigned line); + +#if FOUNDATION_LIBC_ASSERT == FOUNDATION_LIBC_ASSERT_DEFAULT + +#define assert(expr) \ + do { \ + if ((expr) == 0) { \ + foundation_libc_assert(#expr, __FILE__, __LINE__); \ + } \ + } while (0) + +#elif FOUNDATION_LIBC_ASSERT == FOUNDATION_LIBC_ASSERT_NOFILE -#define assert(expr) \ - ((expr) \ - ? void(0) \ - : __assert(#expr, __FILE__, __LINE__)) +#define assert(expr) \ + do { \ + if ((expr) == 0) { \ + foundation_libc_assert(#expr, "", __LINE__); \ + } \ + } while (0) +#elif FOUNDATION_LIBC_ASSERT == FOUNDATION_LIBC_ASSERT_NOMSG + +#define assert(expr) \ + do { \ + if ((expr) == 0) { \ + foundation_libc_assert("", "", __LINE__); \ + } \ + } while (0) + +#elif FOUNDATION_LIBC_ASSERT == FOUNDATION_LIBC_ASSERT_EXPECTED + +#define assert(expr) FOUNDATION_LIBC_EXPECT(expr) + +#else +#error "bad definition of FOUNDATION_LIBC_ASSERT_DEFAULT!" #endif #endif diff --git a/modules/foundation-libc/include/foundation/builtins.h b/modules/foundation-libc/include/foundation/builtins.h new file mode 100644 index 000000000..e95803e8a --- /dev/null +++ b/modules/foundation-libc/include/foundation/builtins.h @@ -0,0 +1,37 @@ +#ifndef _FOUNDATION_LIBC_BUILTINS_H_ +#define _FOUNDATION_LIBC_BUILTINS_H_ + +#include + +#define FOUNDATION_LIBC_ASSERT_DEFAULT 0 +#define FOUNDATION_LIBC_ASSERT_NOFILE 1 +#define FOUNDATION_LIBC_ASSERT_NOMSG 2 +#define FOUNDATION_LIBC_ASSERT_EXPECTED 3 + +#ifndef FOUNDATION_LIBC_ASSERT +#define FOUNDATION_LIBC_ASSERT FOUNDATION_LIBC_ASSERT_DEFAULT +#endif + +#if defined(__clang__) + +#define FOUNDATION_LIBC_NORETURN __attribute__((__noreturn__)) +#define FOUNDATION_LIBC_EXPECT(expr) (__builtin_expect(!(expr), 0)) + +#elif defined(__GNUC__) || defined(__GNUG__) + +#define FOUNDATION_LIBC_NORETURN __attribute__((__noreturn__)) +#define FOUNDATION_LIBC_EXPECT(expr) (__builtin_expect(!(expr), 0)) + +#elif defined(_MSC_VER) + +#define FOUNDATION_LIBC_NORETURN __declspec(noreturn) +#define FOUNDATION_LIBC_EXPECT(expr) (__assume((expr))) + +#else + +#define FOUNDATION_LIBC_NORETURN +#define FOUNDATION_LIBC_EXPECT(expr) + +#endif + +#endif diff --git a/modules/foundation-libc/src/modules/assert.zig b/modules/foundation-libc/src/modules/assert.zig index fc181de73..436760ae0 100644 --- a/modules/foundation-libc/src/modules/assert.zig +++ b/modules/foundation-libc/src/modules/assert.zig @@ -1,14 +1,20 @@ const std = @import("std"); +const builtin = @import("builtin"); -export fn __assert( +export fn foundation_libc_assert( assertion: ?[*:0]const u8, file: ?[*:0]const u8, line: c_uint, ) noreturn { - var buf: [256]u8 = undefined; - const str = std.fmt.bufPrint(&buf, "assertion failed: '{?s}' in file {?s} line {}", .{ assertion, file, line }) catch { - @panic("assertion failed"); - }; - - @panic(str); + switch (builtin.mode) { + .Debug, .ReleaseSafe => { + var buf: [256]u8 = undefined; + const str = std.fmt.bufPrint(&buf, "assertion failed: '{?s}' in file {?s} line {}", .{ assertion, file, line }) catch { + @panic("assertion failed"); + }; + @panic(str); + }, + .ReleaseSmall => @panic("assertion failed"), + .ReleaseFast => unreachable, + } } diff --git a/modules/foundation-libc/tests/assert-validator.c b/modules/foundation-libc/tests/assert-validator.c new file mode 100644 index 000000000..b75ca552c --- /dev/null +++ b/modules/foundation-libc/tests/assert-validator.c @@ -0,0 +1,25 @@ +#include + +#ifndef FOUNDATION_LIBC_ASSERT +#error "FOUNDATION_LIBC_ASSERT wasn't implicitly or explicitly defined by assert.h" +#endif + +#pragma GCC diagnostic ignored "-Wunused-function" +#pragma GCC diagnostic ignored "-Wmissing-prototypes" +// + +void assert_dynamic(int ok) { + assert(ok); +} + +void assert_ok(void) { + assert(1); +} + +// suppress noreturn diagnostic for missing "noreturn" +#pragma GCC diagnostic ignored "-Wmissing-noreturn" +// + +void assert_bad(void) { + assert(0); +} diff --git a/modules/foundation-libc/tests/syntactic-validation.c b/modules/foundation-libc/tests/syntactic-validation.c index c31050e26..375c48916 100644 --- a/modules/foundation-libc/tests/syntactic-validation.c +++ b/modules/foundation-libc/tests/syntactic-validation.c @@ -1,5 +1,6 @@ // our own files must be included as non-system includes to trigger warnings: +#include "assert.h" #include "ctype.h" #include "errno.h" #include "foundation/libc.h" From f86d53e8fff2c06bb83baccfbad11314bd5f47e3 Mon Sep 17 00:00:00 2001 From: Matt Knight Date: Tue, 21 May 2024 15:38:56 -0700 Subject: [PATCH 17/23] update to zig 0.12.0 (#2) * update to zig 0.12.0 * include build.zig.zon * fix typo * fix build invocation * update to 0.12.0 * don't test avr * fix path * fix build * link foundation * no gcc for now, doesn't work with mingw? --- .../.github/workflows/build.yml | 6 +- modules/foundation-libc/build.zig | 263 ++---------------- modules/foundation-libc/build.zig.zon | 11 + modules/foundation-libc/src/libc.zig | 2 +- modules/foundation-libc/test/build.zig | 222 +++++++++++++++ modules/foundation-libc/test/build.zig.zon | 12 + .../{tests => test/src}/assert-validator.c | 0 .../src}/syntactic-validation.c | 0 8 files changed, 265 insertions(+), 251 deletions(-) create mode 100644 modules/foundation-libc/build.zig.zon create mode 100644 modules/foundation-libc/test/build.zig create mode 100644 modules/foundation-libc/test/build.zig.zon rename modules/foundation-libc/{tests => test/src}/assert-validator.c (100%) rename modules/foundation-libc/{tests => test/src}/syntactic-validation.c (100%) diff --git a/modules/foundation-libc/.github/workflows/build.yml b/modules/foundation-libc/.github/workflows/build.yml index f48db1c19..14908696c 100644 --- a/modules/foundation-libc/.github/workflows/build.yml +++ b/modules/foundation-libc/.github/workflows/build.yml @@ -11,7 +11,6 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] - single_thread: [true, false] runs-on: ${{ matrix.os }} steps: @@ -21,8 +20,9 @@ jobs: - name: Setup Zig uses: goto-bus-stop/setup-zig@v2 with: - version: 0.11.0 + version: 0.12.0 - name: Generate and validate packages + working-directory: test run: | - zig build -Dsingle-threaded=${{ matrix.single_thread }} + zig build validate diff --git a/modules/foundation-libc/build.zig b/modules/foundation-libc/build.zig index f73f2855e..aabe36ff5 100644 --- a/modules/foundation-libc/build.zig +++ b/modules/foundation-libc/build.zig @@ -1,144 +1,36 @@ const std = @import("std"); -pub const include_path = std.Build.LazyPath{ - .cwd_relative = sdk_root ++ "/include", -}; - -/// Creates a new instance of the libc target. -pub fn createLibrary(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile { - const libc = b.addStaticLibrary(.{ - .name = "foundation", - .target = target, - .optimize = optimize, - .root_source_file = .{ .path = "src/libc.zig" }, - }); - - libc.addIncludePath(.{ .path = "include" }); - for (header_files) |header_name| - libc.installHeader( - b.fmt("include/{s}", .{header_name}), - header_name, - ); - - return libc; -} - pub fn build(b: *std.Build) void { const validation_step = b.step("validate", "Runs the test suite and validates everything. Automatically triggered in Debug builds."); const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); - const single_threaded = b.option(bool, "single-threaded", "Create a single-threaded libc implementation (default: false)") orelse false; + const single_threaded = b.option(bool, "single_threaded", "Create a single-threaded libc implementation (default: false)") orelse false; // Run validation in debug builds for convenience: if (optimize == .Debug) { b.getInstallStep().dependOn(validation_step); } - // check if the host has a gcc or clang available: - const maybe_gcc = b.findProgram(&.{"gcc"}, &.{}) catch null; - const maybe_clang = b.findProgram(&.{"clang"}, &.{}) catch null; - - const libc = createLibrary(b, target, optimize); - libc.single_threaded = single_threaded; - b.installArtifact(libc); - - // test suite: - { - // Compile for huge amount of targets to detect breakage early on: - for ([_]bool{ false, true }) |validation_single_threaded| { - for (std.enums.values(std.builtin.OptimizeMode)) |validation_optimize| { - for (validation_target_list) |validation_target| { - - // skip everything that cannot support multithreading on freestanding: - if (!validation_single_threaded and !target_can_multithread(validation_target)) - continue; - - const vlc = createLibrary(b, validation_target, validation_optimize); - vlc.single_threaded = validation_single_threaded; - validation_step.dependOn(&vlc.step); - } - } - } - - const syntax_validator_source: std.Build.LazyPath = .{ .path = "tests/syntactic-validation.c" }; - - // use the shipped C compiler to validate our code: - { - // Check if the syntax of all of our header files is valid: - const syntax_validator = b.addStaticLibrary(.{ - .name = "syntax-validator", - .target = .{}, // just build for the host - .optimize = .Debug, - }); - syntax_validator.addCSourceFile(.{ - .file = syntax_validator_source, - .flags = &common_c_flags, - }); - syntax_validator.addIncludePath(include_path); - _ = syntax_validator.getEmittedBin(); - - // Just compile, do not install: - validation_step.dependOn(&syntax_validator.step); - } - - // use the host C compilers to validate our code: - for ([_]?[]const u8{ maybe_gcc, maybe_clang }) |maybe_cc| { - const cc = maybe_cc orelse continue; - - const ext_compiler = b.addSystemCommand(&.{cc}); - - // just compile every time, we don't have dir caching - // so changes on the headers wouldn't re-trigger this - ext_compiler.has_side_effects = true; - - ext_compiler.addPrefixedDirectoryArg("-I", include_path); - - ext_compiler.addArg("-c"); // compile only - ext_compiler.addArg("-O0"); // no optimization for fast compiles - ext_compiler.addArg("-ffreestanding"); // we require freestanding environment - ext_compiler.addArg("-nostdlib"); // do not try to link anything useful - - // turn on warnings - ext_compiler.addArg("-Werror"); - ext_compiler.addArg("-Wall"); - ext_compiler.addArg("-Wextra"); - - ext_compiler.addFileArg(syntax_validator_source); - - ext_compiler.addArg("-o"); - ext_compiler.addArg(b.pathJoin(&.{ b.makeTempPath(), "dummy" })); // we don't really care where this ends up - - validation_step.dependOn(&ext_compiler.step); - } + const libc = b.addStaticLibrary(.{ + .name = "foundation", + .target = target, + .optimize = optimize, + .root_source_file = b.path("src/libc.zig"), + .single_threaded = single_threaded, + }); - // Validate all modes of assertion: - for ([_][]const u8{ - "FOUNDATION_LIBC_ASSERT_DEFAULT", - "FOUNDATION_LIBC_ASSERT_NOFILE", - "FOUNDATION_LIBC_ASSERT_NOMSG", - "FOUNDATION_LIBC_ASSERT_EXPECTED", - }) |assert_mode| { - // Check if the syntax of all of our header files is valid: - const assert_validator = b.addStaticLibrary(.{ - .name = "assert-validator", - .target = .{}, // just build for the host - .optimize = .Debug, - }); - assert_validator.addCSourceFile(.{ - .file = .{ .path = "tests/assert-validator.c" }, - .flags = &common_c_flags, - }); - assert_validator.addIncludePath(include_path); - _ = assert_validator.getEmittedBin(); + libc.addIncludePath(b.path("include")); + for (header_files) |header_name| + libc.installHeader( + b.path(b.fmt("include/{s}", .{header_name})), + header_name, + ); - assert_validator.defineCMacro("FOUNDATION_LIBC_ASSERT", assert_mode); + libc.installHeadersDirectory(b.path("include/foundation"), "foundation", .{}); - // Just compile, do not install: - validation_step.dependOn(&assert_validator.step); - } - } + b.installArtifact(libc); } const header_files = [_][]const u8{ @@ -154,126 +46,3 @@ const header_files = [_][]const u8{ "uchar.h", "foundation/libc.h", }; - -const common_c_flags = [_][]const u8{ - "-std=c11", // target C standard - // - "-Werror", // we do not allow warnings - "-Wall", - "-Wextra", - "-Wmost", - "-Weverything", // this might be dropped later as we just want to find all potential warnings and enable/disable them as required. - // - "-Wno-reserved-macro-identifier", // we actually want to implement those! - "-Wno-reserved-identifier", // we actually want to implement those! -}; - -fn target_can_multithread(target: std.zig.CrossTarget) bool { - return switch (target.getCpuArch()) { - .wasm32, - .wasm64, - .msp430, - => false, - - else => true, - }; -} - -const validation_target_list = [_]std.zig.CrossTarget{ - .{}, // regular host platform - .{ .os_tag = .freestanding }, // host platform, but no OS - - // Check several common cpu targets: - - // arm: - .{ .cpu_arch = .arm, .os_tag = .freestanding }, - .{ .cpu_arch = .armeb, .os_tag = .freestanding }, - .{ .cpu_arch = .thumb, .os_tag = .freestanding }, - .{ .cpu_arch = .thumbeb, .os_tag = .freestanding }, - .{ .cpu_arch = .aarch64, .os_tag = .freestanding }, - // .{ .cpu_arch = .aarch64_32, .os_tag = .freestanding }, // error: unknown target triple 'aarch64_32-unknown-unknown-eabi', please use -triple or -arch - .{ .cpu_arch = .aarch64_be, .os_tag = .freestanding }, - - // risc-v: - .{ .cpu_arch = .riscv32, .os_tag = .freestanding }, - .{ .cpu_arch = .riscv64, .os_tag = .freestanding }, - - // intel: - .{ .cpu_arch = .x86_64, .os_tag = .freestanding }, - .{ .cpu_arch = .x86, .os_tag = .freestanding }, - - // mips: - .{ .cpu_arch = .mips, .os_tag = .freestanding }, - .{ .cpu_arch = .mips64, .os_tag = .freestanding }, - .{ .cpu_arch = .mips64el, .os_tag = .freestanding }, - .{ .cpu_arch = .mipsel, .os_tag = .freestanding }, - - // sparc: - .{ .cpu_arch = .sparc, .os_tag = .freestanding }, - .{ .cpu_arch = .sparc64, .os_tag = .freestanding }, - .{ .cpu_arch = .sparcel, .os_tag = .freestanding }, - - // power: - .{ .cpu_arch = .powerpc, .os_tag = .freestanding }, - .{ .cpu_arch = .powerpc64, .os_tag = .freestanding }, - .{ .cpu_arch = .powerpc64le, .os_tag = .freestanding }, - .{ .cpu_arch = .powerpcle, .os_tag = .freestanding }, - - // web assembly: - .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, - .{ .cpu_arch = .wasm64, .os_tag = .freestanding }, - - // nice to have, but broken: - .{ .cpu_arch = .avr, .os_tag = .freestanding }, - // .{ .cpu_arch = .msp430, .os_tag = .freestanding }, // error: unknown target CPU 'generic' - // .{ .cpu_arch = .m68k, .os_tag = .freestanding }, - // .{ .cpu_arch = .xtensa, .os_tag = .freestanding }, - - // Not evaluated if reasonable to check: - // arc - // csky - // hexagon - // hsail - // hsail64 - // kalimba - // lanai - // le32 - // le64 - // loongarch32 - // loongarch64 - // r600 - // s390x - // shave - // spu_2 - // tce - // tcele - // ve - // xcore - - // will never be supported due to their properties: - // spir - // spir64 - // spirv32 - // spirv64 - - // bpfeb - // bpfel - - // renderscript32 - // renderscript64 - - // amdgcn - // amdil - // amdil64 - - // nvptx - // nvptx64 - - // dxil -}; - -const sdk_root = computeSdkRoot(); - -fn computeSdkRoot() []const u8 { - return std.fs.path.dirname(@src().file).?; -} diff --git a/modules/foundation-libc/build.zig.zon b/modules/foundation-libc/build.zig.zon new file mode 100644 index 000000000..db7ac7a1b --- /dev/null +++ b/modules/foundation-libc/build.zig.zon @@ -0,0 +1,11 @@ +.{ + .name = "foundation-libc", + .version = "0.0.0", + .paths = .{ + "LICENSE", + "README.md", + "build.zig", + "build.zig.zon", + "include", + }, +} diff --git a/modules/foundation-libc/src/libc.zig b/modules/foundation-libc/src/libc.zig index fc5c90249..1133ff679 100644 --- a/modules/foundation-libc/src/libc.zig +++ b/modules/foundation-libc/src/libc.zig @@ -82,7 +82,7 @@ fn fallback_panic_handler(msg_ptr: [*]const u8, msg_len: usize) callconv(.C) nor comptime { @export(fallback_panic_handler, std.builtin.ExportOptions{ .name = "foundation_libc_panic_handler", - .linkage = .Weak, + .linkage = .weak, .visibility = .default, }); } diff --git a/modules/foundation-libc/test/build.zig b/modules/foundation-libc/test/build.zig new file mode 100644 index 000000000..d6c282c90 --- /dev/null +++ b/modules/foundation-libc/test/build.zig @@ -0,0 +1,222 @@ +const std = @import("std"); +const Build = std.Build; + +pub fn build(b: *Build) void { + const validation_step = b.step("validate", "Runs the test suite and validates everything. Automatically triggered in Debug builds."); + + //const maybe_gcc = b.findProgram(&.{"gcc"}, &.{}) catch null; + const maybe_clang = b.findProgram(&.{"clang"}, &.{}) catch null; + + // Compile for huge amount of targets to detect breakage early on: + for ([_]bool{ false, true }) |validation_single_threaded| { + for (std.enums.values(std.builtin.OptimizeMode)) |validation_optimize| { + for (validation_target_list) |validation_target_query| { + const validation_target = b.resolveTargetQuery(validation_target_query); + + // skip everything that cannot support multithreading on freestanding: + if (!validation_single_threaded and !target_can_multithread(validation_target)) + continue; + + const dep = b.dependency("foundation_libc", .{ + .target = validation_target, + .optimize = validation_optimize, + .single_threaded = validation_single_threaded, + }); + + const foundation = dep.artifact("foundation"); + const syntax_validator_source = b.path("src/syntactic-validation.c"); + + { + // Check if the syntax of all of our header files is valid: + const syntax_validator = b.addStaticLibrary(.{ + .name = "syntax-validator", + .target = b.host, + .optimize = .Debug, + }); + syntax_validator.addCSourceFile(.{ + .file = syntax_validator_source, + .flags = &common_c_flags, + }); + syntax_validator.linkLibrary(foundation); + _ = syntax_validator.getEmittedBin(); + + // Just compile, do not install: + validation_step.dependOn(&syntax_validator.step); + } + + // use the host C compilers to validate our code: + for ([_]?[]const u8{maybe_clang}) |maybe_cc| { + const cc = maybe_cc orelse continue; + + const ext_compiler = b.addSystemCommand(&.{cc}); + + // just compile every time, we don't have dir caching + // so changes on the headers wouldn't re-trigger this + ext_compiler.has_side_effects = true; + + ext_compiler.addPrefixedDirectoryArg("-I", b.path("../include")); + + ext_compiler.addArg("-c"); // compile only + ext_compiler.addArg("-O0"); // no optimization for fast compiles + ext_compiler.addArg("-ffreestanding"); // we require freestanding environment + ext_compiler.addArg("-nostdlib"); // do not try to link anything useful + + // turn on warnings + ext_compiler.addArg("-Werror"); + ext_compiler.addArg("-Wall"); + ext_compiler.addArg("-Wextra"); + + ext_compiler.addFileArg(syntax_validator_source); + + ext_compiler.addArg("-o"); + ext_compiler.addArg(b.pathJoin(&.{ b.makeTempPath(), "dummy" })); // we don't really care where this ends up + + validation_step.dependOn(&ext_compiler.step); + } + + // Validate all modes of assertion: + for ([_][]const u8{ + "FOUNDATION_LIBC_ASSERT_DEFAULT", + "FOUNDATION_LIBC_ASSERT_NOFILE", + "FOUNDATION_LIBC_ASSERT_NOMSG", + "FOUNDATION_LIBC_ASSERT_EXPECTED", + }) |assert_mode| { + // Check if the syntax of all of our header files is valid: + const assert_validator = b.addStaticLibrary(.{ + .name = "assert-validator", + .target = b.host, + .optimize = .Debug, + }); + assert_validator.addCSourceFile(.{ + .file = b.path("src/assert-validator.c"), + .flags = &common_c_flags, + }); + assert_validator.linkLibrary(foundation); + _ = assert_validator.getEmittedBin(); + + assert_validator.defineCMacro("FOUNDATION_LIBC_ASSERT", assert_mode); + + // Just compile, do not install: + validation_step.dependOn(&assert_validator.step); + } + } + } + } +} + +const common_c_flags = [_][]const u8{ + "-std=c11", // target C standard + // + "-Werror", // we do not allow warnings + "-Wall", + "-Wextra", + "-Wmost", + "-Weverything", // this might be dropped later as we just want to find all potential warnings and enable/disable them as required. + // + "-Wno-reserved-macro-identifier", // we actually want to implement those! + "-Wno-reserved-identifier", // we actually want to implement those! +}; + +fn target_can_multithread(target: Build.ResolvedTarget) bool { + return switch (target.result.cpu.arch) { + .wasm32, + .wasm64, + .msp430, + => false, + + else => true, + }; +} + +const validation_target_list = [_]std.zig.CrossTarget{ + .{}, // regular host platform + .{ .os_tag = .freestanding }, // host platform, but no OS + + // Check several common cpu targets: + + // arm: + .{ .cpu_arch = .arm, .os_tag = .freestanding }, + .{ .cpu_arch = .armeb, .os_tag = .freestanding }, + .{ .cpu_arch = .thumb, .os_tag = .freestanding }, + .{ .cpu_arch = .thumbeb, .os_tag = .freestanding }, + .{ .cpu_arch = .aarch64, .os_tag = .freestanding }, + // .{ .cpu_arch = .aarch64_32, .os_tag = .freestanding }, // error: unknown target triple 'aarch64_32-unknown-unknown-eabi', please use -triple or -arch + .{ .cpu_arch = .aarch64_be, .os_tag = .freestanding }, + + // risc-v: + .{ .cpu_arch = .riscv32, .os_tag = .freestanding }, + .{ .cpu_arch = .riscv64, .os_tag = .freestanding }, + + // intel: + .{ .cpu_arch = .x86_64, .os_tag = .freestanding }, + .{ .cpu_arch = .x86, .os_tag = .freestanding }, + + // mips: + .{ .cpu_arch = .mips, .os_tag = .freestanding }, + .{ .cpu_arch = .mips64, .os_tag = .freestanding }, + .{ .cpu_arch = .mips64el, .os_tag = .freestanding }, + .{ .cpu_arch = .mipsel, .os_tag = .freestanding }, + + // sparc: + .{ .cpu_arch = .sparc, .os_tag = .freestanding }, + .{ .cpu_arch = .sparc64, .os_tag = .freestanding }, + .{ .cpu_arch = .sparcel, .os_tag = .freestanding }, + + // power: + .{ .cpu_arch = .powerpc, .os_tag = .freestanding }, + .{ .cpu_arch = .powerpc64, .os_tag = .freestanding }, + .{ .cpu_arch = .powerpc64le, .os_tag = .freestanding }, + .{ .cpu_arch = .powerpcle, .os_tag = .freestanding }, + + // web assembly: + .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, + .{ .cpu_arch = .wasm64, .os_tag = .freestanding }, + + // nice to have, but broken: + //.{ .cpu_arch = .avr, .os_tag = .freestanding }, + // .{ .cpu_arch = .msp430, .os_tag = .freestanding }, // error: unknown target CPU 'generic' + // .{ .cpu_arch = .m68k, .os_tag = .freestanding }, + // .{ .cpu_arch = .xtensa, .os_tag = .freestanding }, + + // Not evaluated if reasonable to check: + // arc + // csky + // hexagon + // hsail + // hsail64 + // kalimba + // lanai + // le32 + // le64 + // loongarch32 + // loongarch64 + // r600 + // s390x + // shave + // spu_2 + // tce + // tcele + // ve + // xcore + + // will never be supported due to their properties: + // spir + // spir64 + // spirv32 + // spirv64 + + // bpfeb + // bpfel + + // renderscript32 + // renderscript64 + + // amdgcn + // amdil + // amdil64 + + // nvptx + // nvptx64 + + // dxil +}; diff --git a/modules/foundation-libc/test/build.zig.zon b/modules/foundation-libc/test/build.zig.zon new file mode 100644 index 000000000..267e6d734 --- /dev/null +++ b/modules/foundation-libc/test/build.zig.zon @@ -0,0 +1,12 @@ +.{ + .name = "foundation-libc/test", + .version = "0.0.0", + .dependencies = .{ + .foundation_libc = .{ .path = ".." }, + }, + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + }, +} diff --git a/modules/foundation-libc/tests/assert-validator.c b/modules/foundation-libc/test/src/assert-validator.c similarity index 100% rename from modules/foundation-libc/tests/assert-validator.c rename to modules/foundation-libc/test/src/assert-validator.c diff --git a/modules/foundation-libc/tests/syntactic-validation.c b/modules/foundation-libc/test/src/syntactic-validation.c similarity index 100% rename from modules/foundation-libc/tests/syntactic-validation.c rename to modules/foundation-libc/test/src/syntactic-validation.c From ed0913c5ead746164692f1e6c89ec719d7dc4ae0 Mon Sep 17 00:00:00 2001 From: Matt Knight Date: Tue, 21 May 2024 16:00:17 -0700 Subject: [PATCH 18/23] update zon (#3) --- modules/foundation-libc/build.zig.zon | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/foundation-libc/build.zig.zon b/modules/foundation-libc/build.zig.zon index db7ac7a1b..3ad4c39a1 100644 --- a/modules/foundation-libc/build.zig.zon +++ b/modules/foundation-libc/build.zig.zon @@ -7,5 +7,6 @@ "build.zig", "build.zig.zon", "include", + "src", }, } From f6966f60e30a604a4b39b6784284bf4ca022e1e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=22xq=22=20Quei=C3=9Fner?= Date: Sun, 16 Jun 2024 11:40:38 +0200 Subject: [PATCH 19/23] Updates nix flake and CI --- .../.github/workflows/build.yml | 4 +- modules/foundation-libc/.gitignore | 2 +- modules/foundation-libc/flake.lock | 56 ++++++++++++------- modules/foundation-libc/flake.nix | 4 +- 4 files changed, 42 insertions(+), 24 deletions(-) diff --git a/modules/foundation-libc/.github/workflows/build.yml b/modules/foundation-libc/.github/workflows/build.yml index 14908696c..2395a232c 100644 --- a/modules/foundation-libc/.github/workflows/build.yml +++ b/modules/foundation-libc/.github/workflows/build.yml @@ -18,9 +18,9 @@ jobs: uses: actions/checkout@v4 - name: Setup Zig - uses: goto-bus-stop/setup-zig@v2 + uses: mlugg/setup-zig@v1 with: - version: 0.12.0 + version: 0.13.0 - name: Generate and validate packages working-directory: test diff --git a/modules/foundation-libc/.gitignore b/modules/foundation-libc/.gitignore index e73c965f8..3389c86c9 100644 --- a/modules/foundation-libc/.gitignore +++ b/modules/foundation-libc/.gitignore @@ -1,2 +1,2 @@ -zig-cache/ +.zig-cache/ zig-out/ diff --git a/modules/foundation-libc/flake.lock b/modules/foundation-libc/flake.lock index 5a71654bc..1e42e19ba 100644 --- a/modules/foundation-libc/flake.lock +++ b/modules/foundation-libc/flake.lock @@ -19,11 +19,11 @@ "flake-compat_2": { "flake": false, "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", "owner": "edolstra", "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "type": "github" }, "original": { @@ -37,11 +37,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1701680307, - "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "type": "github" }, "original": { @@ -51,12 +51,15 @@ } }, "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, "locked": { - "lastModified": 1659877975, - "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "lastModified": 1705309234, + "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", "owner": "numtide", "repo": "flake-utils", - "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", "type": "github" }, "original": { @@ -67,11 +70,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1704420045, - "narHash": "sha256-C36QmoJd5tdQ5R9MC1jM7fBkZW9zBUqbUCsgwS6j4QU=", + "lastModified": 1718478900, + "narHash": "sha256-v43N1gZLcGkhg3PdcrKUNIZ1L0FBzB2JqhIYEyKAHEs=", "owner": "nixos", "repo": "nixpkgs", - "rev": "c1be43e8e837b8dbee2b3665a007e761680f0c3d", + "rev": "c884223af91820615a6146af1ae1fea25c107005", "type": "github" }, "original": { @@ -83,16 +86,16 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1702350026, - "narHash": "sha256-A+GNZFZdfl4JdDphYKBJ5Ef1HOiFsP18vQe9mqjmUis=", + "lastModified": 1708161998, + "narHash": "sha256-6KnemmUorCvlcAvGziFosAVkrlWZGIc6UNT9GUYr0jQ=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "9463103069725474698139ab10f17a9d125da859", + "rev": "84d981bae8b5e783b3b548de505b22880559515f", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-23.05", + "ref": "nixos-23.11", "repo": "nixpkgs", "type": "github" } @@ -120,6 +123,21 @@ "type": "github" } }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, "zig": { "inputs": { "flake-compat": "flake-compat_2", @@ -127,11 +145,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1704414187, - "narHash": "sha256-augAzcGHaeuWknIsYSua1oJoCE0Mh12BRm3i4ZONcpc=", + "lastModified": 1718324667, + "narHash": "sha256-AZGskEGjvUmeb+fgBv4lxtCUtXmYBI+ABOlV+og9X14=", "owner": "mitchellh", "repo": "zig-overlay", - "rev": "aeb1f52d1ca19d836f0ef4a7f3f25deec661f75f", + "rev": "b2c14e5f842af6b2bf03e634f73fd84f6956d4ba", "type": "github" }, "original": { diff --git a/modules/foundation-libc/flake.nix b/modules/foundation-libc/flake.nix index 9a4bfaccd..4759faac2 100644 --- a/modules/foundation-libc/flake.nix +++ b/modules/foundation-libc/flake.nix @@ -37,8 +37,8 @@ in rec { devShells.default = pkgs.mkShell { nativeBuildInputs = [ - pkgs.zigpkgs."0.11.0" - pkgs.llvmPackages_17.clangUseLLVM + pkgs.zigpkgs."0.13.0" + pkgs.llvmPackages.clangUseLLVM ]; buildInputs = [ From e50c36e0f1bdcfcb6948195bbdcb7248e4fdc0c5 Mon Sep 17 00:00:00 2001 From: elpekenin Date: Sun, 24 Nov 2024 22:22:06 +0100 Subject: [PATCH 20/23] tiny fix for 0.14 --- modules/foundation-libc/src/libc.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/foundation-libc/src/libc.zig b/modules/foundation-libc/src/libc.zig index 1133ff679..62e9fe766 100644 --- a/modules/foundation-libc/src/libc.zig +++ b/modules/foundation-libc/src/libc.zig @@ -80,7 +80,8 @@ fn fallback_panic_handler(msg_ptr: [*]const u8, msg_len: usize) callconv(.C) nor @trap(); } comptime { - @export(fallback_panic_handler, std.builtin.ExportOptions{ + const ptr = &fallback_panic_handler; + @export(ptr, std.builtin.ExportOptions{ .name = "foundation_libc_panic_handler", .linkage = .weak, .visibility = .default, From d76f9496ce69f8295c3a97a9cc4790a069ff6d59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Fri, 10 Jan 2025 23:30:55 +0100 Subject: [PATCH 21/23] Implement static assert --- modules/foundation-libc/include/assert.h | 2 ++ modules/foundation-libc/include/foundation/builtins.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/modules/foundation-libc/include/assert.h b/modules/foundation-libc/include/assert.h index 16c497948..624363e73 100644 --- a/modules/foundation-libc/include/assert.h +++ b/modules/foundation-libc/include/assert.h @@ -40,4 +40,6 @@ extern FOUNDATION_LIBC_NORETURN void foundation_libc_assert(char const * asserti #error "bad definition of FOUNDATION_LIBC_ASSERT_DEFAULT!" #endif +#define static_assert FOUNDATION_LIBC_STATIC_ASSERT + #endif diff --git a/modules/foundation-libc/include/foundation/builtins.h b/modules/foundation-libc/include/foundation/builtins.h index e95803e8a..a5b57b961 100644 --- a/modules/foundation-libc/include/foundation/builtins.h +++ b/modules/foundation-libc/include/foundation/builtins.h @@ -12,6 +12,8 @@ #define FOUNDATION_LIBC_ASSERT FOUNDATION_LIBC_ASSERT_DEFAULT #endif +#define FOUNDATION_LIBC_STATIC_ASSERT _Static_assert + #if defined(__clang__) #define FOUNDATION_LIBC_NORETURN __attribute__((__noreturn__)) From 78dc4480cb75abcc786bdca79f3f468ef94690a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Fri, 10 Jan 2025 23:38:39 +0100 Subject: [PATCH 22/23] Exclude from C++ builds --- modules/foundation-libc/include/assert.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/foundation-libc/include/assert.h b/modules/foundation-libc/include/assert.h index 624363e73..b7eaf75a0 100644 --- a/modules/foundation-libc/include/assert.h +++ b/modules/foundation-libc/include/assert.h @@ -40,6 +40,8 @@ extern FOUNDATION_LIBC_NORETURN void foundation_libc_assert(char const * asserti #error "bad definition of FOUNDATION_LIBC_ASSERT_DEFAULT!" #endif +#if !defined(__cplusplus) #define static_assert FOUNDATION_LIBC_STATIC_ASSERT +#endif #endif From 3b17a4875702e6612dc4d42f76c1fa64770a9325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Sat, 11 Jan 2025 10:21:03 +0100 Subject: [PATCH 23/23] Fix build error, add test --- modules/foundation-libc/include/assert.h | 1 + modules/foundation-libc/test/src/assert-validator.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/modules/foundation-libc/include/assert.h b/modules/foundation-libc/include/assert.h index b7eaf75a0..74ab2cde8 100644 --- a/modules/foundation-libc/include/assert.h +++ b/modules/foundation-libc/include/assert.h @@ -41,6 +41,7 @@ extern FOUNDATION_LIBC_NORETURN void foundation_libc_assert(char const * asserti #endif #if !defined(__cplusplus) +#undef static_assert #define static_assert FOUNDATION_LIBC_STATIC_ASSERT #endif diff --git a/modules/foundation-libc/test/src/assert-validator.c b/modules/foundation-libc/test/src/assert-validator.c index b75ca552c..db1f4925a 100644 --- a/modules/foundation-libc/test/src/assert-validator.c +++ b/modules/foundation-libc/test/src/assert-validator.c @@ -23,3 +23,7 @@ void assert_ok(void) { void assert_bad(void) { assert(0); } + +void assert_static(void) { + static_assert(sizeof(int) == sizeof(int), "int size check"); +}