Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support compiling single file header libraries as C source files #19423

Open
VisenDev opened this issue Mar 24, 2024 · 6 comments
Open

Support compiling single file header libraries as C source files #19423

VisenDev opened this issue Mar 24, 2024 · 6 comments
Labels
enhancement Solving this issue will likely involve adding new logic or components to the codebase. zig build system std.Build, the build runner, `zig build` subcommand, package management
Milestone

Comments

@VisenDev
Copy link

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

@mlugg
Copy link
Member

mlugg commented Mar 24, 2024

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.

@VisenDev
Copy link
Author

VisenDev commented Mar 24, 2024

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,

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

@mlugg mlugg added enhancement Solving this issue will likely involve adding new logic or components to the codebase. zig build system std.Build, the build runner, `zig build` subcommand, package management labels Mar 24, 2024
@rohlem
Copy link
Contributor

rohlem commented Mar 24, 2024

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: #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.

@f-cozzocrea
Copy link
Contributor

Related issue: #3495

@VisenDev
Copy link
Author

@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

@andrewrk
Copy link
Member

andrewrk commented Aug 16, 2024

There is no such thing as "compiling a header file".

There is- it produces an artifact called a "precompiled header". However that's not what this issue is about.

This issue is a feature request for overriding the file extension detection so that instead of being classified as a C header file, it is classified as a C source file (it does not need to generate a file in the cache as suggested by the OP).

@andrewrk andrewrk added this to the 0.15.0 milestone Aug 16, 2024
xxxbxxx added a commit to xxxbxxx/zig that referenced this issue Aug 24, 2024
…ction.

It is normally based on the file extension, however:
- it can be ambiguous. for instance,
    ".h" is often used for c headers or c++ headers.
    ".s" (instead of ".S") assembly files may still need the c preprocessor. (ziglang#20655)

- a singular file may be interpreted with different languages depending on the context.
    in "single-file libraries", the source.h file can be both a c-header to include, or compiled as a C file (with a #define as toggle)  (ziglang#19423)
xxxbxxx added a commit to xxxbxxx/zig that referenced this issue Sep 7, 2024
…ction.

It is normally based on the file extension, however:
- it can be ambiguous. for instance,
    ".h" is often used for c headers or c++ headers.
    ".s" (instead of ".S") assembly files may still need the c preprocessor. (ziglang#20655)

- a singular file may be interpreted with different languages depending on the context.
    in "single-file libraries", the source.h file can be both a c-header to include, or compiled as a C file (with a #define as toggle)  (ziglang#19423)
xxxbxxx added a commit to xxxbxxx/zig that referenced this issue Sep 7, 2024
…ction.

It is normally based on the file extension, however:
- it can be ambiguous. for instance,
    ".h" is often used for c headers or c++ headers.
    ".s" (instead of ".S") assembly files may still need the c preprocessor. (ziglang#20655)

- a singular file may be interpreted with different languages depending on the context.
    in "single-file libraries", the source.h file can be both a c-header to include, or compiled as a C file (with a #define as toggle)  (ziglang#19423)
xxxbxxx added a commit to xxxbxxx/zig that referenced this issue Sep 23, 2024
…ction.

It is normally based on the file extension, however:
- it can be ambiguous. for instance,
    ".h" is often used for c headers or c++ headers.
    ".s" (instead of ".S") assembly files may still need the c preprocessor. (ziglang#20655)

- a singular file may be interpreted with different languages depending on the context.
    in "single-file libraries", the source.h file can be both a c-header to include, or compiled as a C file (with a #define as toggle)  (ziglang#19423)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Solving this issue will likely involve adding new logic or components to the codebase. zig build system std.Build, the build runner, `zig build` subcommand, package management
Projects
None yet
Development

No branches or pull requests

5 participants