【问题标题】:Compile C code and expose it to Swift under Linux在 Linux 下编译 C 代码并暴露给 Swift
【发布时间】:2016-04-12 10:25:40
【问题描述】:

有没有办法在 Linux 上编译原生 C 或 C++ 代码并将其公开给 Swift?我可以看到像 libdispatch 这样的几个 Apple 库是用纯 C 编写的,您只需导入它们就可以在 Swift 中访问它们。

举个例子,假设我有两个文件Car.cCar.h,它们定义了名为Car 的结构。有没有办法通过编写 import 语句来编译它们并在 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]

我已经联系了 Max(mxcl) - Swift Package Manager 的创建者之一,他告诉我删除 modulemap 并将 .c.h 文件直接放在 Sources 文件夹中。在我编译了那个包但它不能作为模块使用之后。我也无法调用.h 文件中定义的任何函数。

【问题讨论】:

  • 看看 Objective-C 桥接或模块。

标签: c linux swift swift2


【解决方案1】:

如果你用你的 C 代码构建一个库,你可以为它创建一个系统模块,然后可以将它导入到 Swift 中,参见这个答案:Use a C library in Swift on Linux

按照@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 

我从 4 月 12 日开始使用 Ubuntu 14.04 的 Swift 3.0 开发快照完成此操作,可在此处获取: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 中调用函数。然后可以从 Swift 调用这些函数作为任何 C 函数。例如,请参阅此答案:Can I mix Swift with C++? Like the Objective - C .mm files

【讨论】:

  • 如果你试图让它工作并且你在'import'语句中得到一个“没有这样的模块”错误,只需删除导致错误的'import'语句。此方法不会创建用于导入的 Swift 模块。相反,它会在全局范围内创建函数,例如“getInt()”。
【解决方案2】:

在 swift 中使用 C 函数需要一个包含您需要的所有 C 功能的桥接头。例如,myBridgingHeader.h 包含 #include "Car.h" 以及您想要的任何其他 C 内容。我相信目前不支持 C++。

一旦你有了桥接头,你就需要迅速意识到它。 Xcode 用户在将它添加到项目时免费获得它。在 Linux 中,编译时使用 '-import-objc-header /path/to/header' 标志。

编辑:我在下面添加了一个完整的示例,其中包含 6 个文件,供其他可能有此问题的人使用。和上面的基本一样,但我还没看到,直到我已经把它放在一起了哈哈。此外,它可能对需要链接静态库的人有用。

将下面的文件内容复制到适当命名的文件,make,然后是./hello,这样应该可以。作为记录,我只在 swift 版本 2.2-dev 上运行过这个(使用 swift --version 来检查你的)

  • 你好.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;
    }
    
  • 生成文件:

    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
    

【讨论】:

  • Linux 上的swiftswift build 命令都没有-import-objc-header 选项(Swift 3.0)
  • 对不起,应该澄清一下:你应该用'swiftc'编译
  • 仍然没有-import-objc-header 选项。
  • 必须直接用 swiftc 编译意味着不能使用新的构建系统 / Package.swift 方法。如果我们致力于使用它呢?
猜你喜欢
  • 2020-10-18
  • 2016-08-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-16
  • 1970-01-01
相关资源
最近更新 更多