这是一个普遍的问题,自从Swift成为开源并且linux端口缺乏费用以来,这一问题一直困扰着我。
有许多跨平台编程语言。让我们以Go为例。 (真棒)Go标准库有很多包。一些是基于原始数据类型的有用结构和函数。但其他人实现了I / O,网络,操作系统和同步。
这与Swift和LLVM编译器基础结构相比如何?
对于clang,我认为存在例如跨平台并发,我们可以交叉编译。但对于Swift来说,存在平台差异,其中Mac版本依赖于“Darwin.C”而Linux版本依赖于“Glibc”。这导致一些尴尬的代码片段:
#if os(Linux)
import Glibc
#else
import Darwin.C
#endif
...
#if os(Linux)
let j = Int(random() % (count - i)))) + i
#else
let j = Int(arc4random_uniform(UInt32(count - i))) + i
#endif
Swift / LLVM是否始终在编译器的前端处理这些特定于平台的功能,以便它们依赖于c库?或者他/他们会将它们作为编译器的一部分来实现吗?
我读到Go / Rust编译器本身是用Go / Rust编写的。这让我相信编译器的实现方式不同,每个操作系统都具有并发功能,网络连接 - 独立于c库。
是吗?是这样吗?或者是一些编程语言更好地隐藏它们的依赖关系?答案 0 :(得分:9)
问题可能会因为“太宽泛”而被关闭。"但是......既然你用Go标记了它,我将就Go进行回答。
GoLang为每个平台都有一个编译器。 Go运行时工具(go build
等)是针对每个平台而构建的,来自相同的公共源代码/ repo。您可以看到1.6支持的所有平台:
https://github.com/golang/go/tree/release-branch.go1.6/src/runtime
(请注意_linux_amd64.go
类型后缀。稍后会详细介绍。)
现在,如何将代码编译到特定平台?这是每种语言对每个平台都有自己特定的指令(如果它是跨平台语言)。在之前的Go(1.4及更早版本)中,运行时代码是用C语言编写的,具有特定于平台的C指令,类似于您已经躲过的指令:
#if os(Linux)
import Glibc
#else
import Darwin.C
#endif
这很丑......好吧,Go实际上有一些干净的交叉编译C代码,上面的技巧比较抽象和易于管理。但是我的旧交叉编译的C代码就像那样难看。
但是自从Go 1.5以来,Go现在用Go编写。这意味着: 一个Go运行时用于将Go的源代码编译成Go运行时。 这意味着Go语言使用自己的平台特定指令编译自己的平台特定运行时。
那么Go如何指定不同的平台特定代码?有两种不同的方式:
每个都有起伏。 As stated by Dave Cheney himself:
通常,在构建标记或文件后缀之间进行选择时,您就可以了 当两者之间存在完全匹配时,应选择文件后缀 平台或体系结构以及要包含的文件。
mypkg_linux.go // only builds on linux systems
mypkg_windows_amd64.go // only builds on windows 64bit platforms
相反,如果您的文件适用于多个平台或 体系结构,或者您需要排除特定平台,构建标记 应该使用。例如,
% grep '+build' $HOME/go/src/pkg/os/exec/lp_unix.go
// +build darwin dragonfly freebsd linux netbsd openbsd
构建在所有类似unix的平台上。
% grep '+build' $HOME/go/src/pkg/os/types_notwin.go // +build
!windows
构建在除Windows之外的所有平台上。
如果您正在寻找如何为跨平台编写Go代码的模式,请参阅我的模式。您的里程可能会有所不同。
我个人尝试自己坚持使用文件后缀,因为只需查看文件列表就可以更轻松地表示哪些文件支持哪个平台。
首先,从API的界面开始。 Be sure to follow Go's naming convention如果是出于一个目的。
package cmd
// osshell is a global variable that is implement on a per-OS basis.
var osshell shell
// shell is the interface to an OS-agnostic shell to call commands.
type shell interface {
// Exec executes the command with the system's configured osshell.
Exec(command string) (string, error)
}
// Exec executes the command with the system's configured osshell.
func Exec(command string) (string, error) {
return osshell.Exec(command)
}
由于我没有导出界面,因此我没有用“'”后缀。呃,现在我想起来,我应该有。 :)
现在,实现一个调用sh
:
package cmd
import (
"bytes"
"os/exec"
)
func init() {
osshell = &unixshell{}
}
type unixshell struct {
}
func (s *unixshell) Exec(command string) (string, error) {
cmd := exec.Command("sh", "-c", command)
var buf bytes.Buffer
cmd.Stderr = &buf
cmd.Stdout = &buf
// assign return vars
err := cmd.Run()
out := buf.String()
return out, err
}
在为Unix操作系统编译此程序包时,它将使用带有后缀_unix.go
的此文件。然后,当可执行文件实际运行时,调用init()
并连接unixshell{}
struct实现。
这一切都汇集在一起,允许你编写一个平台无关的程序,它使用这个包并访问这样的shell命令:
package main
import (
"fmt"
"os"
"github.com/eduncan911/go/pkg/cmd"
)
func main() {
output, err := cmd.Exec("echo 'Hello World!'")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println(output)
}
现在运行它:
$ go get github.com/eduncan911/go/pkg/cmd
$ go run main.go
Hello World!
(免责声明,我只为该cmd实现了一个unix版本。下次我在Windows上测试时,我会抛出一些PowerShell版本。)
GoLang易于跨平台开发正是我为什么转而使用Go作为我的主要开发语言的原因,因为我有几个侧面项目我想成为跨平台。