mirror of
https://github.com/ziglang/zig.git
synced 2024-11-27 07:32:44 +00:00
add support for compiling Objective-C++ code (#10096)
* add support for compiling Objective-C++ code Prior to this change, calling `step.addCSourceFiles` with Obj-C++ file extensions (`.mm`) would result in an error due to Zig not being aware of that extension. Clang supports an `-ObjC++` compilation mode flag, but it was only possible to use if you violated standards and renamed your `.mm` Obj-C++ files to `.m` (Obj-C) to workaround Zig being unaware of the extension. This change makes Zig aware of `.mm` files so they can be compiled, enabling compilation of projects such as [Google's Dawn WebGPU](https://dawn.googlesource.com/dawn/) using a `build.zig` file only. Helps hexops/mach#21 Signed-off-by: Stephen Gutekanst <stephen@hexops.com> * test/standalone: add ObjC++ compilation/linking test Based on the existing objc example, just tweaked for ObjC++. Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
parent
722c6b9567
commit
9836f1b2f9
@ -3307,7 +3307,7 @@ pub fn addCCArgs(
|
||||
try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple });
|
||||
|
||||
switch (ext) {
|
||||
.c, .cpp, .m, .h => {
|
||||
.c, .cpp, .m, .mm, .h => {
|
||||
try argv.appendSlice(&[_][]const u8{
|
||||
"-nostdinc",
|
||||
"-fno-spell-checking",
|
||||
@ -3316,6 +3316,10 @@ pub fn addCCArgs(
|
||||
try argv.append("-flto");
|
||||
}
|
||||
|
||||
if (ext == .mm) {
|
||||
try argv.append("-ObjC++");
|
||||
}
|
||||
|
||||
// According to Rich Felker libc headers are supposed to go before C language headers.
|
||||
// However as noted by @dimenus, appending libc headers before c_headers breaks intrinsics
|
||||
// and other compiler specific items.
|
||||
@ -3599,6 +3603,7 @@ pub const FileExt = enum {
|
||||
cpp,
|
||||
h,
|
||||
m,
|
||||
mm,
|
||||
ll,
|
||||
bc,
|
||||
assembly,
|
||||
@ -3610,7 +3615,7 @@ pub const FileExt = enum {
|
||||
|
||||
pub fn clangSupportsDepFile(ext: FileExt) bool {
|
||||
return switch (ext) {
|
||||
.c, .cpp, .h, .m => true,
|
||||
.c, .cpp, .h, .m, .mm => true,
|
||||
|
||||
.ll,
|
||||
.bc,
|
||||
@ -3648,6 +3653,10 @@ pub fn hasObjCExt(filename: []const u8) bool {
|
||||
return mem.endsWith(u8, filename, ".m");
|
||||
}
|
||||
|
||||
pub fn hasObjCppExt(filename: []const u8) bool {
|
||||
return mem.endsWith(u8, filename, ".mm");
|
||||
}
|
||||
|
||||
pub fn hasAsmExt(filename: []const u8) bool {
|
||||
return mem.endsWith(u8, filename, ".s") or mem.endsWith(u8, filename, ".S");
|
||||
}
|
||||
@ -3686,6 +3695,8 @@ pub fn classifyFileExt(filename: []const u8) FileExt {
|
||||
return .cpp;
|
||||
} else if (hasObjCExt(filename)) {
|
||||
return .m;
|
||||
} else if (hasObjCppExt(filename)) {
|
||||
return .mm;
|
||||
} else if (mem.endsWith(u8, filename, ".ll")) {
|
||||
return .ll;
|
||||
} else if (mem.endsWith(u8, filename, ".bc")) {
|
||||
@ -3710,6 +3721,7 @@ pub fn classifyFileExt(filename: []const u8) FileExt {
|
||||
test "classifyFileExt" {
|
||||
try std.testing.expectEqual(FileExt.cpp, classifyFileExt("foo.cc"));
|
||||
try std.testing.expectEqual(FileExt.m, classifyFileExt("foo.m"));
|
||||
try std.testing.expectEqual(FileExt.mm, classifyFileExt("foo.mm"));
|
||||
try std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.nim"));
|
||||
try std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so"));
|
||||
try std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so.1"));
|
||||
|
@ -296,6 +296,7 @@ const usage_build_generic =
|
||||
\\ .c C source code (requires LLVM extensions)
|
||||
\\ .cxx .cc .C .cpp C++ source code (requires LLVM extensions)
|
||||
\\ .m Objective-C source code (requires LLVM extensions)
|
||||
\\ .mm Objective-C++ source code (requires LLVM extensions)
|
||||
\\ .bc LLVM IR Module (requires LLVM extensions)
|
||||
\\
|
||||
\\General Options:
|
||||
@ -1190,7 +1191,7 @@ fn buildOutputType(
|
||||
.object, .static_library, .shared_library => {
|
||||
try link_objects.append(arg);
|
||||
},
|
||||
.assembly, .c, .cpp, .h, .ll, .bc, .m => {
|
||||
.assembly, .c, .cpp, .h, .ll, .bc, .m, .mm => {
|
||||
try c_source_files.append(.{
|
||||
.src_path = arg,
|
||||
.extra_flags = try arena.dupe([]const u8, extra_cflags.items),
|
||||
@ -1256,7 +1257,7 @@ fn buildOutputType(
|
||||
.positional => {
|
||||
const file_ext = Compilation.classifyFileExt(mem.spanZ(it.only_arg));
|
||||
switch (file_ext) {
|
||||
.assembly, .c, .cpp, .ll, .bc, .h, .m => try c_source_files.append(.{ .src_path = it.only_arg }),
|
||||
.assembly, .c, .cpp, .ll, .bc, .h, .m, .mm => try c_source_files.append(.{ .src_path = it.only_arg }),
|
||||
.unknown, .shared_library, .object, .static_library => {
|
||||
try link_objects.append(it.only_arg);
|
||||
},
|
||||
|
@ -69,6 +69,11 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
|
||||
.build_modes = true,
|
||||
.requires_macos_sdk = true,
|
||||
});
|
||||
// Try to build and run an Objective-C++ executable.
|
||||
cases.addBuildFile("test/standalone/objcpp/build.zig", .{
|
||||
.build_modes = true,
|
||||
.requires_macos_sdk = true,
|
||||
});
|
||||
|
||||
// Ensure the development tools are buildable.
|
||||
cases.add("tools/gen_spirv_spec.zig");
|
||||
|
7
test/standalone/objcpp/Foo.h
Normal file
7
test/standalone/objcpp/Foo.h
Normal file
@ -0,0 +1,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface Foo : NSObject
|
||||
|
||||
- (NSString *)name;
|
||||
|
||||
@end
|
11
test/standalone/objcpp/Foo.mm
Normal file
11
test/standalone/objcpp/Foo.mm
Normal file
@ -0,0 +1,11 @@
|
||||
#import "Foo.h"
|
||||
|
||||
@implementation Foo
|
||||
|
||||
- (NSString *)name
|
||||
{
|
||||
NSString *str = [[NSString alloc] initWithFormat:@"Zig"];
|
||||
return str;
|
||||
}
|
||||
|
||||
@end
|
36
test/standalone/objcpp/build.zig
Normal file
36
test/standalone/objcpp/build.zig
Normal file
@ -0,0 +1,36 @@
|
||||
const std = @import("std");
|
||||
const Builder = std.build.Builder;
|
||||
const CrossTarget = std.zig.CrossTarget;
|
||||
|
||||
fn isRunnableTarget(t: CrossTarget) bool {
|
||||
// TODO I think we might be able to run this on Linux via Darling.
|
||||
// Add a check for that here, and return true if Darling is available.
|
||||
if (t.isNative() and t.getOsTag() == .macos)
|
||||
return true
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn build(b: *Builder) void {
|
||||
const mode = b.standardReleaseOptions();
|
||||
const target = b.standardTargetOptions(.{});
|
||||
|
||||
const test_step = b.step("test", "Test the program");
|
||||
|
||||
const exe = b.addExecutable("test", null);
|
||||
b.default_step.dependOn(&exe.step);
|
||||
exe.addIncludeDir(".");
|
||||
exe.addCSourceFile("Foo.mm", &[0][]const u8{});
|
||||
exe.addCSourceFile("test.mm", &[0][]const u8{});
|
||||
exe.setBuildMode(mode);
|
||||
exe.setTarget(target);
|
||||
exe.linkLibCpp();
|
||||
// TODO when we figure out how to ship framework stubs for cross-compilation,
|
||||
// populate paths to the sysroot here.
|
||||
exe.linkFramework("Foundation");
|
||||
|
||||
if (isRunnableTarget(target)) {
|
||||
const run_cmd = exe.run();
|
||||
test_step.dependOn(&run_cmd.step);
|
||||
}
|
||||
}
|
14
test/standalone/objcpp/test.mm
Normal file
14
test/standalone/objcpp/test.mm
Normal file
@ -0,0 +1,14 @@
|
||||
#import "Foo.h"
|
||||
#import <assert.h>
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
@autoreleasepool {
|
||||
Foo *foo = [[Foo alloc] init];
|
||||
NSString *result = [foo name];
|
||||
std::cout << "Hello from C++ and " << [result UTF8String];
|
||||
assert([result isEqualToString:@"Zig"]);
|
||||
return 0;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user