Commit 9836f1b2f9
Changed files (7)
test
src/Compilation.zig
@@ -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"));
src/main.zig
@@ -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);
},
test/standalone/objcpp/build.zig
@@ -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);
+ }
+}
test/standalone/objcpp/Foo.h
@@ -0,0 +1,7 @@
+#import <Foundation/Foundation.h>
+
+@interface Foo : NSObject
+
+- (NSString *)name;
+
+@end
test/standalone/objcpp/Foo.mm
@@ -0,0 +1,11 @@
+#import "Foo.h"
+
+@implementation Foo
+
+- (NSString *)name
+{
+ NSString *str = [[NSString alloc] initWithFormat:@"Zig"];
+ return str;
+}
+
+@end
test/standalone/objcpp/test.mm
@@ -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;
+ }
+}
test/standalone.zig
@@ -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");