From 4cf3bd63a22bd2f88c1f04cddac31aa19bc78a9d Mon Sep 17 00:00:00 2001 From: Chris Boesch Date: Wed, 15 Feb 2023 22:55:44 +0100 Subject: [PATCH 1/4] added the first C exercise --- build.zig | 14 +++++++ exercises/093_hello_c.zig | 69 +++++++++++++++++++++++++++++++ patches/patches/093_hello_c.patch | 4 ++ 3 files changed, 87 insertions(+) create mode 100644 exercises/093_hello_c.zig create mode 100644 patches/patches/093_hello_c.patch diff --git a/build.zig b/build.zig index 930fc56..32ee69c 100644 --- a/build.zig +++ b/build.zig @@ -31,6 +31,10 @@ const Exercise = struct { /// We need to keep track of this, so we compile without the self hosted compiler @"async": bool = false, + /// This exercise makes use of C functions + /// We need to keep track of this, so we compile with libc + C: bool = false, + /// Returns the name of the main file with .zig stripped. pub fn baseName(self: Exercise) []const u8 { assert(std.mem.endsWith(u8, self.main_file, ".zig")); @@ -461,6 +465,11 @@ const exercises = [_]Exercise{ // .output = "ABCDEF", // .@"async" = true, // }, + .{ + .main_file = "093_hello_c.zig", + .output = "Hello C from Zig! - C result ist 17 chars", + .C = true, + }, .{ .main_file = "999_the_end.zig", .output = "\nThis is the end for now!\nWe hope you had fun and were able to learn a lot, so visit us again when the next exercises are available.", @@ -725,6 +734,11 @@ const ZiglingStep = struct { // zig_args.append("-fstage1") catch unreachable; // } + // Enable C support for exercises that use C functions + if (self.exercise.C) { + zig_args.append("-lc") catch unreachable; + } + if (builder.color != .auto) { zig_args.append("--color") catch unreachable; zig_args.append(@tagName(builder.color)) catch unreachable; diff --git a/exercises/093_hello_c.zig b/exercises/093_hello_c.zig new file mode 100644 index 0000000..ba76a46 --- /dev/null +++ b/exercises/093_hello_c.zig @@ -0,0 +1,69 @@ +// +// When Andrew Kelley announced the idea of a new programming language +// - namely Zig - in his blog on February 8, 2016, he also immediately +// stated his ambitious goal: to replace the C language! +// +// In order to be able to achieve this goal at all, Zig should be +// as compatible as possible with its "predecessor". +// Only if it is possible to exchange individual modules in existing +// C programs without having to use complicated wrappers, +// the undertaking has a chance of success. +// +// So it is not surprising that calling C functions and vice versa +// is extremely "smooth". +// +// To call C functions in Zig, you only need to specify the library +// that contains said function. For this purpose there is a built-in +// function corresponding to the well-known @import(): +// +// @cImport() +// +// All required libraries can now be included in the usual Zig notation: +// +// const c = @cImport({ +// @cInclude("stdio.h"); +// @cInclude("..."); +// }); +// +// Now a function can be called via the (in this example) constant 'c': +// +// c.puts("Hello world!"); +// +// By the way, most C functions have return values in the form of an +// integer value. Errors can then be evaluated (return < 0) or other +// information can be obtained. For example, 'puts' returns the number +// of characters output. +// +// So that all this does not remain a dry theroy now, let's just start +// and call a C function out of Zig. +// +// our well-known "import" for Zig +const std = @import("std"); + +// new the import for C +const c = @cImport({ + + // we use "standard input/output" from C + @cInclude("stdio.h"); +}); + +pub fn main() void { + + // In order to output a text that can be evaluated by the + // Zig Builder, we need to write it to the Error output. + // In Zig we do this with "std.debug.print" and in C we can + // specify the file to write to, i.e. "standard error (stderr)". + // + // Ups, something is wrong... + const c_res = fprintf(c.stderr, "Hello C from Zig!"); + + // let's see what the result from C is: + std.debug.print(" - C result ist {d} chars\n", .{c_res}); +} +// +// Something must be considered when compiling with C functions. +// Namely that the Zig compiler knows that it should include +// corresponding libraries. For this purpose we call the compiler +// with the parameter "lc" for such a program, +// e.g. "zig run -lc hello_c.zig". +// diff --git a/patches/patches/093_hello_c.patch b/patches/patches/093_hello_c.patch new file mode 100644 index 0000000..15e2a44 --- /dev/null +++ b/patches/patches/093_hello_c.patch @@ -0,0 +1,4 @@ +58c58 +< const c_res = fprintf(c.stderr, "Hello C from Zig!"); +--- +> const c_res = c.fprintf(c.stderr, "Hello C from Zig!"); From 9693860bc0cbb49ac68ad23a8297be6eb1bc2add Mon Sep 17 00:00:00 2001 From: Chris Boesch Date: Thu, 16 Feb 2023 11:15:07 +0100 Subject: [PATCH 2/4] inserted a workaround for mac-os, see https://github.com/ziglang/zig/issues/14657#issuecomment-1432180967 --- exercises/093_hello_c.zig | 11 ++++++++++- patches/patches/093_hello_c.patch | 6 +++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/exercises/093_hello_c.zig b/exercises/093_hello_c.zig index ba76a46..d234165 100644 --- a/exercises/093_hello_c.zig +++ b/exercises/093_hello_c.zig @@ -49,13 +49,22 @@ const c = @cImport({ pub fn main() void { + // Due to a current limitation in the Zig compiler, + // we need a small workaround to make this exercise + // work on mac-os. + const builtin = @import("builtin"); + const stderr = switch (builtin.target.os.tag) { + .macos => 1, + else => c.stderr, + }; + // In order to output a text that can be evaluated by the // Zig Builder, we need to write it to the Error output. // In Zig we do this with "std.debug.print" and in C we can // specify the file to write to, i.e. "standard error (stderr)". // // Ups, something is wrong... - const c_res = fprintf(c.stderr, "Hello C from Zig!"); + const c_res = fprintf(stderr, "Hello C from Zig!"); // let's see what the result from C is: std.debug.print(" - C result ist {d} chars\n", .{c_res}); diff --git a/patches/patches/093_hello_c.patch b/patches/patches/093_hello_c.patch index 15e2a44..a0f62c2 100644 --- a/patches/patches/093_hello_c.patch +++ b/patches/patches/093_hello_c.patch @@ -1,4 +1,4 @@ -58c58 -< const c_res = fprintf(c.stderr, "Hello C from Zig!"); +63c63 +< const c_res = fprintf(stderr, "Hello C from Zig!"); --- -> const c_res = c.fprintf(c.stderr, "Hello C from Zig!"); +> const c_res = c.fprintf(stderr, "Hello C from Zig!"); From bb956254774ed022f29c70e98f7ca95c28f2c86f Mon Sep 17 00:00:00 2001 From: Chris Boesch Date: Thu, 16 Feb 2023 18:33:06 +0100 Subject: [PATCH 3/4] try 'write' that works on mac, but I didn't know if it works on windows --- exercises/093_hello_c.zig | 20 +++++--------------- patches/patches/093_hello_c.patch | 6 +++--- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/exercises/093_hello_c.zig b/exercises/093_hello_c.zig index d234165..1877e04 100644 --- a/exercises/093_hello_c.zig +++ b/exercises/093_hello_c.zig @@ -42,29 +42,19 @@ const std = @import("std"); // new the import for C const c = @cImport({ - - // we use "standard input/output" from C - @cInclude("stdio.h"); + @cInclude("unistd.h"); }); pub fn main() void { - // Due to a current limitation in the Zig compiler, - // we need a small workaround to make this exercise - // work on mac-os. - const builtin = @import("builtin"); - const stderr = switch (builtin.target.os.tag) { - .macos => 1, - else => c.stderr, - }; - // In order to output a text that can be evaluated by the // Zig Builder, we need to write it to the Error output. // In Zig we do this with "std.debug.print" and in C we can - // specify the file to write to, i.e. "standard error (stderr)". + // specify the file descriptor i.e. 2 for error console. // - // Ups, something is wrong... - const c_res = fprintf(stderr, "Hello C from Zig!"); + // In this case we use 'write' to output 17 chars, + // but something is missing... + const c_res = write(2, "Hello C from Zig!", 17); // let's see what the result from C is: std.debug.print(" - C result ist {d} chars\n", .{c_res}); diff --git a/patches/patches/093_hello_c.patch b/patches/patches/093_hello_c.patch index a0f62c2..60eeaf0 100644 --- a/patches/patches/093_hello_c.patch +++ b/patches/patches/093_hello_c.patch @@ -1,4 +1,4 @@ -63c63 -< const c_res = fprintf(stderr, "Hello C from Zig!"); +57c57 +< const c_res = write(2, "Hello C from Zig!", 17); --- -> const c_res = c.fprintf(stderr, "Hello C from Zig!"); +> const c_res = c.write(2, "Hello C from Zig!", 17); From dc187889c1a0cec8cb0e7de67df96ebc9e981995 Mon Sep 17 00:00:00 2001 From: Chris Boesch Date: Thu, 16 Feb 2023 19:28:10 +0100 Subject: [PATCH 4/4] some improvements in the description --- build.zig | 8 ++++---- exercises/093_hello_c.zig | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build.zig b/build.zig index c23e92e..1abe5c7 100644 --- a/build.zig +++ b/build.zig @@ -465,14 +465,14 @@ const exercises = [_]Exercise{ // .output = "ABCDEF", // .@"async" = true, // }, + .{ + .main_file = "092_interfaces.zig", + .output = "Daily Insect Report:\nAnt is alive.\nBee visited 17 flowers.\nGrasshopper hopped 32 meters.", + }, .{ .main_file = "093_hello_c.zig", .output = "Hello C from Zig! - C result ist 17 chars", .C = true, -}, -.{ - .main_file = "092_interfaces.zig", - .output = "Daily Insect Report:\nAnt is alive.\nBee visited 17 flowers.\nGrasshopper hopped 32 meters.", }, .{ .main_file = "999_the_end.zig", diff --git a/exercises/093_hello_c.zig b/exercises/093_hello_c.zig index 1877e04..b294f38 100644 --- a/exercises/093_hello_c.zig +++ b/exercises/093_hello_c.zig @@ -36,11 +36,11 @@ // // So that all this does not remain a dry theroy now, let's just start // and call a C function out of Zig. -// + // our well-known "import" for Zig const std = @import("std"); -// new the import for C +// and here the new the import for C const c = @cImport({ @cInclude("unistd.h"); }); @@ -50,9 +50,9 @@ pub fn main() void { // In order to output a text that can be evaluated by the // Zig Builder, we need to write it to the Error output. // In Zig we do this with "std.debug.print" and in C we can - // specify the file descriptor i.e. 2 for error console. + // specify a file descriptor i.e. 2 for error console. // - // In this case we use 'write' to output 17 chars, + // In this exercise we use 'write' to output 17 chars, // but something is missing... const c_res = write(2, "Hello C from Zig!", 17);