如何编程语言实现跨平台功能?

时间:2016-03-28 13:45:56

标签: swift go programming-languages llvm

这是一个普遍的问题,自从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库。

是吗?是这样吗?或者是一些编程语言更好地隐藏它们的依赖关系?

1 个答案:

答案 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语言使用自己的平台特定指令编译自己的平台特定运行时。

How如何指定平台

那么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代码

如果您正在寻找如何为跨平台编写Go代码的模式,请参阅我的模式。您的里程可能会有所不同。

我个人尝试自己坚持使用文件后缀,因为只需查看文件列表就可以更轻松地表示哪些文件支持哪个平台。

首先,从API的界面开始。 Be sure to follow Go's naming convention如果是出于一个目的。

shell.go

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

的Unix版本

shell_unix.go

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作为我的主要开发语言的原因,因为我有几个侧面项目我想成为跨平台。