在Linux下编译C代码并暴露给Swift

12

在Linux上有方法可以编译本地的C或C++代码并将其暴露给Swift吗?我注意到一些苹果库(例如libdispatch)是用纯C编写的,只需导入即可在Swift中访问它们。

以设置示例为例,假设我有两个文件Car.cCar.h,定义了名为Car的结构体。是否有办法通过编写导入语句来编译它们并在Swift中使用它们?

import Car

我尝试在包含 .c, .hPackage.swift 文件的目录中编写 module.modulemap 文件:

module Car {
   header "Car.h"
   export *
}

并运行 swift build。这将产生错误:

<unknown>:0: error: unexpected 'commands' value (expected map)
<unknown>:0: error: unable to load build file

我正在使用Swift版本3.0-dev(2016年3月24日)

[更新1]

我联系了Swift Package Manager的创作者之一Max(mxcl),他告诉我摆脱modulemap并将.c.h文件直接放在Sources文件夹中。我这样做后,软件包编译了,但它不能作为模块使用。此外,我无法调用.h文件中定义的任何函数。


1
请查看Objective-C桥接或模块。 - Jean-Baptiste Yunès
2个回答

15
如果您使用C代码构建库,可以为其创建一个系统模块,然后将其导入到Swift中。有关详细信息,请参见此答案:在Linux上在Swift中使用C库
另一种方法是创建一个桥接头文件,就像@Philip所建议的那样。以下是一个过于简化的示例。考虑以下C代码:
/* In car.h */
int getInt();

/* In car.c */
int getInt() { return 123; }

我们将使用car.h作为桥接头文件。Swift源代码在文件junk.swift中:

print("Hi from swift!")
var i = getInt()
print("And here is an int from C: \(i)!")

首先,从car.c创建一个对象文件car.o

gcc -c car.c

现在按照以下步骤构建可执行文件 junk

swiftc -import-objc-header car.h junk.swift car.o -o junk

运行可执行文件会产生以下结果:

$ ./junk
Hi from swift!
And here is an int from C: 123!

-import-objc-header选项是隐藏的。要查看它和其他一些隐藏选项,请运行:

swiftc -help-hidden 

我是使用Swift 3.0开发快照版本在Ubuntu 14.04上完成的,此版本于2016年4月12日发布,可在此处下载:https://swift.org/builds/development/ubuntu1404/swift-DEVELOPMENT-SNAPSHOT-2016-04-12-a/swift-DEVELOPMENT-SNAPSHOT-2016-04-12-a-ubuntu14.04.tar.gz

如果您想使用C ++,则需要创建一个包装器,编写一个C ++源文件并使用C ++编译器进行编译,但使用 extern "C"从C中调用函数。然后可以像任何C函数一样从Swift调用这些函数。例如,请参见此答案:Can I mix Swift with C++? Like the Objective - C .mm files


如果您正在尝试让它工作,但在“import”语句上遇到“没有这样的模块”错误,请删除导致错误的“import”语句。此方法不会创建用于导入的Swift模块。相反,它会在全局范围内创建函数,例如'getInt()'。 - pauln

5

在 Swift 中使用 C 函数需要一个桥接头文件,其中包含您需要的所有 C 功能。例如,myBridgingHeader.h 包含 #include "Car.h" 和其他您想要的 C 东西。我相信目前不支持 C++。

一旦您有了桥接头文件,您需要让 Swift 知道它的存在。如果您是 Xcode 用户,只需将其添加到项目中即可免费获得此功能。在 Linux 中,编译时请使用 '-import-objc-header /path/to/header' 标志。

编辑:我已经添加了一个完整的示例,由 6 个文件组成,供其他可能遇到此问题的人参考。它基本上与上面的示例相同,但是在我把它放在一起之前我没有看到它哈哈。此外,它对需要链接静态库的人可能有用。

将以下文件内容复制到适当命名的文件中,make,然后 ./hello,这就可以工作了。为了记录,我只在 swift 版本 2.2-dev 上运行过此代码(使用 swift --version 检查您的版本)

  • hello.swift:

    let n: Int32 = 5
    print("Hello, Swift World!")
    print("mult2(\(n,N)) = \(mult2(n,N))")
    print("CONST1=\(CONST1), CONST2=\(CONST2), CONST3=\(CONST3)")
    
  • bridge.h:

    #include "defs.h"
    #include "mult.h"
    
  • defs.h:

    #define CONST1 1
    #define CONST2 2
    #define CONST3 3
    
  • mult.h:

    #define N 7
    int mult2(int,int);
    
  • mult.c:

    #include "defs.h"
    #include "mult.h"
    int mult2(int a, int b)
    {
         return a*b;
    }
    
  • Makefile:

    all: hello
    
    hello: libmult.a
         swiftc hello.swift -import-objc-header ./bridge.h -L. -lmult -o hello
    
    libmult.a: mult.o
         ar -rc libmult.a mult.o
         ranlib libmult.a
    
    mult.o: mult.c mult.h defs.h
         gcc -c mult.c -o mult.o
    
    .PHONY: clean   
    clean:
         rm -f *.o *.a hello
    

3
在Linux上,无论是swift命令还是swift build命令(Swift 3.0版本),都没有-import-objc-header选项。 - Said Sikira
3
抱歉,我应该说明清楚:你应该使用“swiftc”进行编译。 - Philip
然而,仍然没有-import-objc-header选项。 - Said Sikira
1
必须直接使用swiftc进行编译,这意味着无法使用新的构建系统/Package.swift方法。如果我们执意要使用它怎么办? - Feldur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接