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

J0-*_*nas 1 programming-languages llvm go swift

自从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
Run Code Online (Sandbox Code Playgroud)

...

#if os(Linux)
            let j = Int(random() % (count - i)))) + i
#else
            let j = Int(arc4random_uniform(UInt32(count - i))) + i
#endif
Run Code Online (Sandbox Code Playgroud)

Swift/LLVM是否总是在编译器的前端处理这些平台特定的功能,以便它们依赖于c库?或者他/他们会将它们作为编译器的一部分来实现吗?

我读到Go/Rust编译器本身是用Go/Rust编写的.这使我相信编译器的实现方式不同,每个操作系统都具有并发性,网络功能 - 独立于c库.

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

edu*_*911 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
Run Code Online (Sandbox Code Playgroud)

这很丑陋......好吧,Go实际上有一些干净的交叉编译C代码,上面的技巧比较抽象和易于管理.但是我的旧交叉编译的C代码就像那样难看.

但是自从Go 1.5以来,Go现在用Go编写.这意味着:Go运行时用于将Go的源代码编译为Go运行时. 这意味着Go语言使用自己的特定于平台的指令来编译自己的平台特定运行时.

Go如何指定平台

那么Go如何指定不同的平台特定代码?有两种不同的方式:

  • 建立标志
  • 文件后缀

每个都有起伏. 正如Dave Cheney本人所说:

通常,在构建标记或文件后缀之间进行选择时,如果平台或体系结构与要包含的文件之间存在完全匹配,则应选择文件后缀.

mypkg_linux.go         // only builds on linux systems
mypkg_windows_amd64.go // only builds on windows 64bit platforms
Run Code Online (Sandbox Code Playgroud)

相反,如果您的文件适用于多个平台或体系结构,或者您需要排除特定平台,则应使用构建标记.例如,

% grep '+build' $HOME/go/src/pkg/os/exec/lp_unix.go 
// +build darwin dragonfly freebsd linux netbsd openbsd
Run Code Online (Sandbox Code Playgroud)

构建在所有类似unix的平台上.

% grep '+build' $HOME/go/src/pkg/os/types_notwin.go  // +build
!windows
Run Code Online (Sandbox Code Playgroud)

构建在除Windows之外的所有平台上.

如何编写跨平台的Go代码

如果您正在寻找关于如何为跨平台编写Go代码的模式,这是我的模式.你的旅费可能会改变.

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

首先,从API的接口开始. 如果出于一个目的,请务必遵循Go的命名约定.

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)
}
Run Code Online (Sandbox Code Playgroud)

由于我没有导出界面,因此我没有使用'er'后缀.呃,现在我想起来,我应该有.:)

现在,实现一个调用的Unix版本sh:

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
}
Run Code Online (Sandbox Code Playgroud)

在为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)
}
Run Code Online (Sandbox Code Playgroud)

现在运行它:

$ go get github.com/eduncan911/go/pkg/cmd
$ go run main.go
Hello World!
Run Code Online (Sandbox Code Playgroud)

(免责声明,我只为该cmd实现了一个unix版本.下次我在Windows上测试时,我会抛出一些powershell版本.)

GoLang易于跨平台开发正是我为什么切换到Go作为我的主要开发语言的原因,因为我有几个侧面项目我想要跨平台.