drv16821 2016-03-28 13:45
浏览 132

如何通过编程语言实现跨平台功能? [关闭]

This is more of a general question that bugs me ever since Swift became open source and the linux port lacked feeatures.

There are many cross platform programming languages. Lets take Go for example. The (awesome) Go standard library has many packages. Some are helpful structs and functions based on primitive data types. But others implement I/O, networking, os, and sync.

How does this compare to Swift and the LLVM compiler infastructure?

For clang I think there exists e.g. cross platform concurrency, and we can cross compile. But for Swift there are platform differences where the Mac version depends on "Darwin.C" and the Linux one on "Glibc". This results in some awkward code snippets:

#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

Does Swift/LLVM handle these platform specific features always on the front end of the compiler such that they depend on c libraries? Or do/will they implement them as part of the compiler?

I read that the Go/Rust compiler is itself written in Go/Rust. This leads me to believe that the compiler is implemented differently for each OS to feature concurrenty, networking - is independent of c libraries.

Is that so? Or are some programming language just better at hiding their dependencies?

  • 写回答

1条回答 默认 最新

  • dongyan1899 2016-03-28 16:44
    关注

    The question may be closed as "too broad." But... Since you tagged it with Go, I'll answer it in regards to Go.

    GoLang has a compiler for each platform. The Go runtime tools (go build, etc) are built specific to each platform from the same common source code/repo. You can see all the platforms supported with 1.6:

    https://github.com/golang/go/tree/release-branch.go1.6/src/runtime

    (Take note of the _linux_amd64.go type suffixes. More on this in a bit.)

    Now, how does one compile code to specific platforms? This is where each language has its own specific directives for each platform (if it is a cross-platform language). In previous Go (1.4 and earlier), the runtime code was written in C with platform-specific C directives, similar to what you've already eluded to:

    #if os(Linux)
    import Glibc
    #else
    import Darwin.C
    #endif
    

    It was ugly... Well, Go actually had some clean cross-compiling C code with tricks like that above more abstracted and manageable. But my old cross-compiled C code was, well, ugly like that.

    But since Go 1.5, Go is now written in Go. What this means is: a Go runtime is used to compile Go's source code into a Go runtime. This means the Go language uses its own platform-specific directives to compile its own platform specific runtime.

    How Go specifies a platform

    So how does Go specify different platform-specific code? There are two different ways:

    • build flags
    • file suffixes

    There are ups and downs to each. As stated by Dave Cheney himself:

    In general, when choosing between a build tag or a file suffix, you should choose a file suffix when there is an exact match between the platform or architecture and the file you want to include.

    mypkg_linux.go         // only builds on linux systems
    mypkg_windows_amd64.go // only builds on windows 64bit platforms
    

    Conversely if your file is applicable to more than one platform or architecture, or you need to exclude a specific platform, a build tag should be used. eg,

    % grep '+build' $HOME/go/src/pkg/os/exec/lp_unix.go 
    // +build darwin dragonfly freebsd linux netbsd openbsd
    

    builds on all unix like platforms.

    % grep '+build' $HOME/go/src/pkg/os/types_notwin.go  // +build
    !windows
    

    builds on all platforms except Windows.

    How to write Go code for cross-platform

    If you were looking for a pattern on how to write Go code for cross-platform, here's my pattern. Your mileage may vary.

    I personally try to stick to file suffixes myself as it makes it easier to denote what files support what platform by just viewing the list of files.

    First, start with an interface for your API. Be sure to follow Go's naming convention if for a single purpose.

    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)
    }
    

    Since I am not exporting the interface, I didn't suffix with 'er'. Eh, now that I think about it I should have. :)

    Now, implement a Unix version that calls 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
    }
    

    When compiling this package for a Unix operating system, it will use this file with the suffix _unix.go. Then, when the executable actually runs, the init() is called and wires up the unixshell{} struct implementation.

    This all comes together to allow you to write a platform-agnostic program that consumes this package and to access shell commands like this:

    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)
    }
    

    And now run it:

    $ go get github.com/eduncan911/go/pkg/cmd
    $ go run main.go
    Hello World!
    

    (Disclaimer, I only have a unix version implemented for that cmd. I'll throw up some powershell version the next time I am on Windows to test it.)

    GoLang's ease of cross-platform development is exactly why I switched to Go for my primary development language as I have several side projects I'd like to be cross-platform.

    评论

报告相同问题?

悬赏问题

  • ¥30 这是哪个作者做的宝宝起名网站
  • ¥60 版本过低apk如何修改可以兼容新的安卓系统
  • ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏
  • ¥50 有数据,怎么建立模型求影响全要素生产率的因素
  • ¥50 有数据,怎么用matlab求全要素生产率
  • ¥15 TI的insta-spin例程
  • ¥15 完成下列问题完成下列问题
  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换
  • ¥15 YoloV5 第三方库的版本对照问题
  • ¥15 请完成下列相关问题!