在C,Go和Swift中构建小型跨平台CLI工具

这就是我写一个简单工具的方式。3次,用不同的语言。

本实验

我的目标是编写一个非常简单的命令行工具,该工具可以生成与Google Authenticator兼容的一次性密码。 Google身份验证器使用基于时间的一次性密码算法(TOTP)生成代码。 我不想使用自己的实现,而是想使用现有的TOTP库,因为已经有很多不错的库。

本质上,我要我的工具要做的就是接受一个秘密作为单个输入,然后调用现有的TOTP库生成代码,并将生成的访问代码打印到标准输出中。

我问自己的问题是:假设我想在多个平台(Mac,Windows,Ubuntu)上使用该工具,并且希望
在少数人(不一定是技术人员)(例如同事)中分配工具,哪种编程语言将是最实用/可行/有趣的选择?

当然,您可以从多个角度看待这个问题。 让我们专注于构建和分发工具。 然后,这些是我的“应有”要求:

  • 应该有可能将工具分发为“开箱即用”的单个可执行文件,这意味着用户不必安装运行时,框架,库等依赖项。
  • 使用相同的代码库(但可能使用不同的工具链),应该可以为多个平台生成构建。

语言选择

我想为这个特定的实验创建一个二进制文件,这就是为什么我没有为此特定工具考虑诸如Node.js,Ruby和Python之类的解释语言的原因。 当然,尽管总的来说,这些语言都能为编写跨平台命令行工具提供完全可行的选择。

这些语言还有一个缺点,那就是最终用户需要安装运行时(例如Node.js)。 尽管许多平台都预先安装了常见的运行时,但是用户可能需要安装其他版本。 对于非技术用户而言,这并不总是一件琐碎的任务。

(我知道有一些工具可以将解释后的语言编译为独立的可执行文件,但这有点像在作弊)

最后,我的选择是尝试使用CGoSwift

我决定留在“编程语言舒适区”,因为学习一种新语言不是我实验的一部分。 因此,我没有尝试过(很有用)非常有趣的其他语言,例如Rust ,我将在以后尝试 (随时对您的Rust经验发表评论)。 也许还需要注意:在本实验中,我考虑了C ++的过大杀伤力(或者实际上,我的C ++ 只是缺乏知识)。

我学到的是

C

  • 通常,使用C构建的可执行文件是动态链接的。 这意味着最终用户需要安装依赖项(链接的库)才能运行该工具。 那绝对不是理想的。
  • 有很多解决方法,但是所有这些都有一些缺点:
    静态链接:创建单个二进制文件,其中将包含所有必需的二进制代码。 但是,这要求您使用的所有库(例如TOTP库)都支持静态链接。 绝对不是总是这样。 此外,Apple在Mac OS X上不支持静态链接的二进制文件。
    将链接的动态库与您的应用程序一起分发 。 这意味着对于每个目标操作系统,您都必须预先构建所有链接的库,请确保可执行文件可以找到这些库(例如,在macOS上更改rpath),并将其与应用程序捆绑在一起。 换句话说,您需要将.dll (Windows) .dylib (macOS)或.so (Linux)文件编译并捆绑到您的应用程序中。
  • C没有需要与应用程序捆绑在一起的运行时。 因此,生成的可执行文件非常小。 唯一的依赖项(动态库)是C标准库libc ,默认情况下,该库在我要定位的OS上可用。
  • 在不同平台上构建单个C代码库可能会很痛苦。 我通常更喜欢为平台使用“默认”或得到最广泛支持的构建链。 我认为这是Windows上的Visual Studio,Mac上的Xcode(或Mac命令行上的GCC)和Linux上的GCC。 但这意味着对于每个平台,您都需要安装和设置一个完全不同的构建环境(项目文件,构建脚本等)。
  • 很难从源代码为多个平台编译依赖关系。 就像我上面提到的,在不同平台上为自己的代码建立构建链可能已经很困难。 从源代码为多个平台编译第三方库更加困难。 有些相对容易使用跨平台,但是有些却是真正的痛苦,因为它们缺乏对跨平台构建的支持或文档。

  • 默认情况下,Golang构建的可执行文件是静态链接的。 这意味着用户不必安装任何依赖项,也不需要与应用程序一起分发动态库。 对于小型命令行应用程序,唯一需要分发的是可执行文件。
  • 不幸的是,由于静态链接,生成的可执行文件相对较大。 这是因为Go二进制文件包含Go运行时,因此最终用户不需要安装Go。 (但正如Dotan Nahum指出的那样,有一些方法可以减少一些脂肪)
  • Go是我感兴趣的所有目标平台上的二进制发行版。这使得设置构建环境和在这些平台上构建变得轻松。
  • Go的一大优点是,您可以在一台机器上轻松地针对多个平台进行编译。

迅速

  • 建议静态链接到Swift标准库,这样生成的可执行文件就不会绑定到生成它的特定版本的Swift。 这将导致一个很大的二进制文件(对于一个简单的工具,它的大小超过10 MB)。 由于Swift缺乏ABI稳定性,因此需要进行静态链接。 不过,这是在将来的Swift版本中要解决的路线图上。 (相比之下,Objective-C确实具有ABI稳定性)。
  • 跨平台支持尚未成熟。 您可以在Mac和Linux(尚无正式的Windows版本)上编译Swift程序,但是跨平台的构建系统Swift Package Manager(SPM)却不如MacOS上的Xcode成熟。 此外,CocoaPods或Carthage(仅适用于MacOS)上提供的许多库尚不支持SPM(跨平台)。

结论

在构建跨平台和分发工具时, Go给了我最好的开发人员经验。

由于使用了默认的静态链接,因此很容易创建用于分发的单个可执行文件。

在不同平台上构建Go程序也非常容易。 无需编写特定于平台的构建脚本或使用依赖于平台的工具链。

缺点是生成的可执行文件相对较大(几兆字节),但是在我看来,这不是真正的问题。

接下来是C。 由于缺乏运行时的约束,因此用C编写总是给我一种愉悦的控制感和一种自由的感觉。 当然,这样做的缺点是您可以轻松地用脚射击自己。 但是这里最大的问题是,没有像Go那样完美地跨平台工作的构建工具链。

最后, 斯威夫特 。 尽管我真的很喜欢Swift作为一种语言,但在专门为macOS编写命令行工具时,我只会将Swift视为显而易见的选择。 斯威夫特对我来说太过“移动目标”了。 这有几个含义,一个重要的含义是,在其他平台上使用Swift并不容易。 对我来说,另一个问题是Windows尚未正式支持。

最后一点:我想分享我的经验,但是最后,最适合您的语言取决于个人喜好和语言的当前状态。 明年可能会有所不同。