zig
zig copied to clipboard
Support compiling single file header libraries as C source files
Currently there are issues when you try to compile a single file header as a C source file
build.zig
const raygui = b.dependency("raygui", .{});
exe.addCSourceFile(.{ .file = raygui.path("src/raygui.h"), .flags = &.{"-DRAYGUI_IMPLEMENTATION"} });
exe.addIncludePath(raygui.path("src"));
error: unknown file type for an object file
note: while parsing /Users/Robert/zig/gears/zig-cache/o/13610c1b0230abbf5a473e142a3e4be4/raygui.o
To fix this you have to manually create a .c file that includes the .h file, and then pass that .c file to exe.addCSourceFile. Given that single file header libraries are fairly common in c, you shouldn't need to use a workaround to gain this functionality.
From my understanding, this error is caused by issues with how clang compiles header files. However, this could be fairly easily fixed by the zig build system automatically generating a .c file somewhere in the cache that includes the .h file. Then zig can pass that .c file to clang for compilation, rather than requiring the user to do this manually.
Fixing this bug would make managing C dependencies thru the zig package manager much nicer
From my understanding, this error is caused by issues with how clang compiles header files
There is no such thing as "compiling a header file".
Is there any reason you can't just @cInclude
the header from Zig to use the API? I won't necessarily be surprised if that doesn't work, since it'll just use translate-c
to convert the C header to Zig code and some C constructs can't be translated, but if it works that would be the idiomatic solution.
Is there any reason you can't just
@cInclude
the header from Zig to use the API? I won't necessarily be surprised if that doesn't work, since it'll just usetranslate-c
to convert the C header to Zig code and some C constructs can't be translated,
Yes, there are usually some issues with translate C that prevent this from working in my experience
There is no such thing as "compiling a header file".
What I am referring to is when normal c code is embedded in a .h file under a special compilation flag. Normally the file just acts as a header, but when the compilation flag is used, it needs to be compiled as source code
See https://github.com/nothings/stb
I've done the same thing previously, trying to compile the header file directly leads to an error as mentioned above; for me that error looked a little different, maybe due to the version difference since then, or because I'm on Windows: https://github.com/ziglang/zig/issues/14144
My workaround for now was instead compiling a stub .c
file of only this line:
#include INCLUDE_STUB_C_FILE_PATH_TO_INCLUDE
then adding this stub file in the build.zig
:
stb_truetype_object.linkLibC();
stb_truetype_object.addSystemIncludePath(.{ .path = stb_truetype_header_directory_path });
stb_truetype_object.addCSourceFile(.{
.file = .{ .path = include_stub_path },
.flags = &.{ "-DSTB_TRUETYPE_IMPLEMENTATION", "-DINCLUDE_STUB_C_FILE_PATH_TO_INCLUDE=<" ++ stb_truetype_h_file_name ++ ">" },
});
While it's true that that's not how header files are meant to be used, this usage exists in practice.
I agree that having support for this directly would be nicer, even if just by standardizing this stub file approach in std.Build
.
Related issue: #3495
@nektro So my plan to fix this would be as follows, I'm not super familiar with compiler internals so I would appreciate any feedback.
In lib/std/Build/Step/Compile.zig
, I would go to where link objects are added to args
line 1024
// Inherit dependencies on system libraries and static libraries.
for (module.link_objects.items) |link_object| {
//...
.c_source_file => |c_source_file| l: {
if (!my_responsibility) break :l;
if (c_source_file.flags.len == 0) {
if (prev_has_cflags) {
try zig_args.append("-cflags");
try zig_args.append("--");
prev_has_cflags = false;
}
} else {
try zig_args.append("-cflags");
for (c_source_file.flags) |arg| {
try zig_args.append(arg);
}
try zig_args.append("--");
prev_has_cflags = true;
}
try zig_args.append(c_source_file.file.getPath2(module.owner, step));
total_linker_objects += 1;
},
I will create an isHeader
function which detects if a filepath is a header file. If isHeader
returns true, I will check the temp cache for a .c
version of the header file (I'm reading thru Cache.zig
right now to get a better idea of how to generate cache files). Then I will replace this line
try zig_args.append(c_source_file.file.getPath2(module.owner, step));
With a line that appends the path to the .c
file stored in the cache rather than the original .h
file.
It also seems odd to me that .c_source_file
and .c_source_files
are implemented separately. It seems like these should be combined since their implementation is almost identical