#Calling main function from C file?

1 messages · Page 1 of 1 (latest)

granite loom
#

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?

gloomy glen
#

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

granite loom
# gloomy glen use addLibrary

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

gloomy glen
#

will have to give me a bit

granite loom
#

@gloomy glen hey

rain elm
#

@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

granite loom
#

@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.

rain elm
#

Another caveat. You can't do this for C++ main files. You have to have a C file

rain elm
granite loom
#

I am not getting it @rain elm

#

What changes do i need to do in my build.zig?

rain elm
#

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

gloomy glen
#

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

rain elm
gloomy glen
#

this is not the optimal way to do it

rain elm
#

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

gloomy glen
#

the better way is to use a static library

#

no need to stub out main

rain elm
#

I mean, very well. Listen to him then

granite loom
granite loom
rain elm
#

Did you add

pub const _start = void;
pub const WinMainCRTStartup = void;

To your renderer.zig?

rain elm
#

_start should work for mac

#

pub const _start = void;

granite loom
gloomy glen
#

one sec, I will write up the necessary parts

granite loom
rain elm
#

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

gloomy glen
#

just to be clear, what does your main.c look like?

#

or how is it intended to look like

granite loom
#

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

gloomy glen
#

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
}
granite loom
#

Can't I just use main.m as my starting instead of some zig file? by saying not to use zig main

gloomy glen
#

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

rain elm
#

How is this different from what I said?

granite loom
#

yeah @rain elm

gloomy glen
#

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

granite loom
#

@gloomy glen

rain elm
#

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

granite loom
#

but i think the version is different

gloomy glen
#

using a library has no such requirement

rain elm
#

But that's what you do, with your c_main_module (or at least is implied)

gloomy glen
#

ah good point

#

I muddled concepts

granite loom
#

yeah. but 0.15.x you must give a root_module for executable

gloomy glen
#

indeed, you don't specify the root source file

#

you do just addCSourceFile

#

mb

#

and yeah

#

one sec

granite loom
#

okay

gloomy glen
#
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)

granite loom
#

okay

rain elm
#

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

granite loom
#

Yeahhhhhh

#

Its working now

#

Thanks @gloomy glen

gloomy glen
#

yes, probably, but fundamentally it boils down to C code depending on a C library that exports one or more functions

#

np

rain elm
#

Word

gloomy glen
#

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

granite loom
#

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?

rain elm
#

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

gloomy glen
#

I imagine you can't do that in zig in the usual way

#

though it might just have some good defaults

rain elm
gloomy glen
#

I have often ended up giving slightly skewed advice based on previous experiences colored by niche requirements

rain elm
gloomy glen
#

that's why #1019652020308824145 is useful, we can debate among peers until the correct answer is found

rain elm
gloomy glen
#

well, "fundamentally", you're able to make (compiler-dependent) C++-compatible layout structs that represent classes in zig

rain elm
#

Well like you have to override methods for example

gloomy glen
#

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

rain elm
#

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