I have a main function in a C file in another folder and some platform dependent code. I am using zig file too in another folder. I want main function from the C file to be called. I removed src/main.zig and src/root.zig and created a dummy process.zig and added this as root_source_file and added main.c using exe.root_module.addCSourceFile(). But it is complaining no main function inside process.zig. What to do?
#Calling main function from C file?
1 messages · Page 1 of 1 (latest)
define an exported function in the zig file, and expose that to the C code for it to call it
and don't use addExecutable
use addLibrary
you link it to the main C executable with linkLibrary
basically, you're doin FFI
Can you please give an example here is my build.zig
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "zig_metal",
.root_module = b.createModule(.{
.root_source_file = b.path("src/renderer.zig"),
.target = target,
.optimize = optimize,
.link_libc = true,
}),
});
exe.root_module.addCSourceFile(.{
.file = b.path("macos-plat/main.m"),
.language = .objective_c,
.flags = &.{
"-fobjc-arc",
},
});
exe.root_module.addCSourceFile(.{
.file = b.path("metal-backend/metal_api.m"),
.language = .objective_c,
.flags = &.{
"-fobjc-arc",
},
});
exe.root_module.addCSourceFile(.{
.file = b.path("macos-plat/platform.m"),
.language = .objective_c,
.flags = &.{
"-fobjc-arc",
},
});
exe.root_module.linkSystemLibrary("objc", .{});
exe.root_module.linkFramework("Foundation", .{});
exe.root_module.linkFramework("Metal", .{});
exe.root_module.linkFramework("AppKit", .{});
exe.root_module.linkFramework("MetalKit", .{});
b.installArtifact(exe);
const run_step = b.step("run", "Run the app");
const run_cmd = b.addRunArtifact(exe);
run_step.dependOn(&run_cmd.step);
run_cmd.step.dependOn(b.getInstallStep());
}
@gloomy glen Can it be run? If I added addLibrary
will have to give me a bit
@gloomy glen hey
@granite loom I can't speak for mac, but on windows and linux in your exe's module you can add a
b.addCSourceFile(s)
that contain your int main() and there isn't a conflicting main in your zig files it should just work.
Additionally (at least what we do for windows and linux) we have to add this to our main.zig file:
pub const _start = void;
pub const WinMainCRTStartup = void;
Another caveat though, we are using zig 0.14, I did test this against zig 0.14.1
@rain elm I dont want main.zig file itself. I only want zig code and I will call it from c file there. Because eventloop is implemented there in C. So I want to call main with zig files working.
Another caveat. You can't do this for C++ main files. You have to have a C file
I said main but really just the root_file you pass to your zig module
Folder structure
src/
main.c <- int main()
exe_root.zig <- just the root zig file
exe_root.zig
pub const _start = void;
pub const WinMainCRTStartup = void;
//... plus your imports, no need for an entry function though
Build.zig, truncated to show the changes
pub fn build(b: *std.Build) void {
...
const exe_module = b.createModule(.{
.root_source_file = b.path("src/exe_root.zig"),
...
});
exe_module.addCSourceFile(.{
.file = b.path("src/main.c"),
.language = .c
});
const exe = b.addExecutable(.{
.name = "zig_metal",
.root_module = exe_module
...
});
As an example
But essentially you're giving the root source file for the module, in the root file you're declaring that _start = void, and adding the C source file with your main function
Ah wait, okay
So more specifically
In your src/renderer.zig you need to add:
pub const _start = void;
pub const WinMainCRTStartup = void;
And you need to change your "macos-plat/main.m" to a C file, since this behavior only works with C files and no other types
funny name change. one second, I'm re-booking a flight
as soon as I'm done with it I'll attend ye
even zig support needs customer support /j
I think I got it. Just gotta have him rewrite his main file to C
this is not the optimal way to do it
Which I don't think should be too crazy, he could also just create a stub main file, then call his obj-c main from there
I mean, very well. Listen to him then
I have done that. But it creates a dependency on zig from c. I created ApplicationMain and injecting the code from main into that and using extern in main.zig and calling that function but its creating a dependency loop
okay
Did you add
pub const _start = void;
pub const WinMainCRTStartup = void;
To your renderer.zig?
i am using macos
Nah it was working in whatever way i have mentioned here @rain elm . But i dont like it coz c is depending on zig and zig is depending on c its creating two way dependency i think its not optimal
one sec, I will write up the necessary parts
thnx
Ah okay, disregard what I've said then. I have something similar in the project I'm working on but we do it so we can have our main file written in C and then we call into zig from it. And then our C/C++ dependencies are inter-mixed but broken out into static libs
just to be clear, what does your main.c look like?
or how is it intended to look like
okay
#include "platform.h"
#include <AppKit/AppKit.h>
#include <stdlib.h>
int main(int argc, char **argv) {
NSApplication *application = [NSApplication sharedApplication];
AppDelegate *delegate = [AppDelegate new];
[application setDelegate:delegate];
[application run];
return EXIT_SUCCESS;
}
main.m actually
the basics of what I would envision would be something like:
int zigMain(int argc, char const *const *argv);
int main(int argc, char **argv) {
return zigMain(argc, argv);
}
and then you'd have a zig file with:
export fn zigMain(argc: c_int, argv: [*:null]const ?[*:0]const u8) c_int {
realMain(argv[0..@intCast(argc)]) catch |err| {
if (@errorReturnTrace()) |ert| {
const ert_fmt: std.debug.FormatStackTrace = .{
.stack_trace = ert,
};
std.debug.print("{s}:{f}", .{ @errorName(err), ert });
}
return 1;
};
return 0;
}
fn realMain(argv: [:null]const ?[*:0]const u8) !void {
// your code
}
Can't I just use main.m as my starting instead of some zig file? by saying not to use zig main
notice, this isn't an actual entry point
the actual entry point is the C file
the zig file is just being called by C
How is this different from what I said?
yeah @rain elm
in build.zig, you would have something like:
const zig_main = b.addLibrary(.{
.name = "zig_main",
.linkage = .static,
.root_module = b.createModule(.{ .root_source_file = b.path("src/main.zig") }),
// everything else
});
const c_main = b.addExecutable(.{
.root_module = c_main_module,
});
c_main.linkLibrary(zig_main);
there's no need to have any stubs to ignore start.zig with a static library
@gloomy glen
Maybe I need to double check but the reason I had to make stubs is that start.zig throws an error is because it expects a main function
https://www.mitchellhanberg.com/compiling-c-with-zig/
This blog does this
but i think the version is different
that's probably because you're using addExecutable, ie build-exe, which requires a main zig entrypoint
using a library has no such requirement
But that's what you do, with your c_main_module (or at least is implied)
notice that they aren't specifying a root source file for the executable
ah good point
I muddled concepts
yeah. but 0.15.x you must give a root_module for executable
indeed, you don't specify the root source file
you do just addCSourceFile
mb
and yeah
one sec
okay
const c_mod = b.createModule(.{
.root_source_file = null, // don't set this
// other options
});
const c_exe = b.addExecutable(.{
.root_source_file = c_mod,
});
and then c_exe.linkLibrary(zig_main)
okay
Ah it does get weird for him though. It's been a minute since I've done mac dev but I imagine the c_main_module in this case is going to have to be more C code than just the main function. Since iirc AppDelegate::setDelegate would have to call/point into a obj-c function that further imports more zig functions to get fully setup
yes, probably, but fundamentally it boils down to C code depending on a C library that exports one or more functions
np
Word
Car you elaborate?
Can*
also, @rain elm apologies if I sounded dismissive, I'm reading it back in retrospect, and I realise I was blunt. I also see our answers were fairly similar, with mine being more of a fine-tuning of your advice. T'wasn't my intention, I just had my attention divided
Do you mean objc will call more C functions inside? If so do u have core libraries which I can use directly instead of these?
Uhhh, maybe not in a meaningful way but I thought NSApplication requires/expects you to fill out/create a derived NSApplication class that you would necessarily not be able to do in zig
I imagine you can't do that in zig in the usual way
though it might just have some good defaults
You're all good man, our's is kinda weird, for windows we do a custom C entry point but for other platforms we use zig. But it's basically the same setup, so we have a C main, that calls into a local zig main, then our application is loaded via a module rather than library
I have often ended up giving slightly skewed advice based on previous experiences colored by niche requirements
Ditto, we do it wierd because we do weird IAT stuff
that's why #1019652020308824145 is useful, we can debate among peers until the correct answer is found
But if it works the way I think it works you have to make a obj-c (specifically obj-c) class that fundamentally cannot be done in zig
well, "fundamentally", you're able to make (compiler-dependent) C++-compatible layout structs that represent classes in zig
Well like you have to override methods for example
at the root if it all, it all boils down to a bunch of symbols that are expected to have some layout or another
it's just a question of how practical it is
usually, just writing the obj-c code is going to be the simplest, sanest, forward-compatible option available
so I agree it's the right choice
but not "fundamentally impossible otherwise"
/pedantic
I mean I guess nothing is stopping you from making a C++ compatible vtable and doing inheritance by hand but it doesn't necessarily mean its the right way to do it