me
me copied to clipboard
学习 MacOS 开发 (Part 9: Swift 编译环境)
Packaging
swift的hello world都是从hello.swift开始,
print("hello world")
无需编译直接运行:
~ swift hello.swift
hello world
那我们现在再创建一个foo.swift
func hello() {
print("hello world")
}
同时,将hello.swift改为调用,
hello()
这时候问题就来了,
- nodejs通过
require './foo'
就可以引用其他文件,一个带package.json的目录自动就可以是一个module,需要社区复用时可以借助npm - python通过import相对目录和绝对目录直接可以导入,一个目录直接成模块
- c/c++是通过每个c/c++自成object file然后link组团,同Java, C#
常规来说,c/c++是编译型语言,javascript/python是解释型语言,那么swift是什么呢?
- 我理解swift同时实现了这两个类型,通过swift命令实现了解释功能,通过swiftc实现了编译功能
- swift/swiftc和java/javac是有区别的,javac将java程序编译成java bytecode,即JIT可以理解的中间语言,java则是通过JIT运行java bytecode,而swift是直接解释script,而swiftc则是编译成机器码, 后续运行不需要中间过程
- 所以需要同时需要2套思维去看swift, 事实上,我们用的更多的是编译这套体系,解释只是cookie而已,所以应该用c/c++/oc的思维去理解swift会更合适
在官网的Swift.org - Getting Started中介绍了Package Manager,可以通过swift package init
, swift build
, swift run
来实现打包, 我理解内部是通过swiftc来实现的。
- 所以swift hello.swift更类似swift < hello.swift,只是一个字符串的输入,如果你需要解释来运行多个文件,网上也有人说先merge所有swift然后交给swift
swiftc
hello.swift
print("hello world")
当swiftc hello.swift
后会生成hello可执行文件,这个一点问题没有。
我们来看foo.swift和main.swift两个文件,
foo.swift
func foo() { print("I'm foo") }
main.swift
foo()
编译和运行:
➜ swiftc -c main.swift foo.swift -module-name hello
➜ swiftc -emit-executable main.o foo.o -o app
➜ ./app
i'm foo
- 你会发现原来main和foo需要先编译出object file,这就走回了c/oc的老路
- 然后链接形成app
- 当编译多个文件时,有且仅有一个main.swift,只有这个文件可以在方法外直接写代码,直接写的代码等同于entry point
- 当用其他文件时需要辅以"@main"的标记
- 可以使用通配符
swiftc -c *.swift -module-name hello
,swiftc -emit-executable *.o -o app
来完成这个过程
对应的Makefile:
app: obj
swiftc -emit-executable main.o foo.o -o app
obj: main.swift foo.swift
swiftc -c main.swift foo.swift -module-name hello
run:
./app
通过swiftc --help
你可以看到如下指令:
- -emit-assembly: Emit assembly file(s) (-S)
- -emit-executable: Emit a linked executable
- -emit-library: Emit a linked library
- -emit-object: Emit object file(s) (-c)
此外, 可以通过file, nm, gobjdump来观察一下编译出来的object file和可执行文件,这里就省略了。
swift build
当你理解了swiftc后再去理解swift build
就会有不一样的体感。
- 官方: apple/swift-package-manager: The Package Manager for the Swift Programming Language
- swift build需要一个Package.swift, 通过
swift package init --type executable
生成,具体spec参考Package — Swift Package Manager
对应刚才结构的Package.swift
import PackageDescription
let package = Package(
name: "foo",
targets: [
.executableTarget(
name: "foo",
path: ".",
exclude: ["Makefile", "README.md"],
sources: ["main.swift", "foo.swift"]
)
]
)
默认的path是Sources,这个文档中都有介绍:
/// Creates an executable target.
///
/// An executable target can contain either Swift or C-family source files, but not both. It contains code that
/// is built as an executable module that can be used as the main target of an executable product. The target
/// is expected to either have a source file named `main.swift`, `main.m`, `main.c`, or `main.cpp`, or a source
/// file that contains the `@main` keyword.
///
/// - Parameters:
/// - name: The name of the target.
/// - dependencies: The dependencies of the target. A dependency can be another target in the package or a product from a package dependency.
/// - path: The custom path for the target. By default, the Swift Package Manager requires a target's sources to reside at predefined search paths;
/// for example, `[PackageRoot]/Sources/[TargetName]`.
/// Don't escape the package root; for example, values like `../Foo` or `/Foo` are invalid.
/// - exclude: A list of paths to files or directories that the Swift Package Manager shouldn't consider to be source or resource files.
/// A path is relative to the target's directory.
/// This parameter has precedence over the `sources` parameter.
/// - sources: An explicit list of source files. If you provide a path to a directory,
/// the Swift Package Manager searches for valid source files recursively.
/// - resources: An explicit list of resources files.
/// - publicHeadersPath: The directory containing public headers of a C-family library target.
/// - cSettings: The C settings for this target.
/// - cxxSettings: The C++ settings for this target.
/// - swiftSettings: The Swift settings for this target.
/// - linkerSettings: The linker settings for this target.
static func executableTarget(
name: String,
dependencies: [Target.Dependency] = [],
path: String? = nil,
exclude: [String] = [],
sources: [String]? = nil,
resources: [Resource]? = nil,
publicHeadersPath: String? = nil,
cSettings: [CSetting]? = nil,
cxxSettings: [CXXSetting]? = nil,
swiftSettings: [SwiftSetting]? = nil,
linkerSettings: [LinkerSetting]? = nil
) -> Target
library
swiftc生成.a
swiftc -emit-module -emit-library -static bar.swift
swiftc生成.dylib
swiftc -emit-module -emit-library bar.swift
swift build在swiftc基础上解决自动依赖处理的逻辑,可以直接引用github的repo
关于library后续再展开。
更新1:
-
swiftc -o app main.swift content.swift
可以一步完成编译和链接。