【问题标题】:What is the right approach to encapsulate platform specific code in Go?在 Go 中封装特定于平台的代码的正确方法是什么?
【发布时间】:2022-02-09 15:05:23
【问题描述】:

我想开发一个小型 Go 应用程序,向演示文稿的观众展示使用的击键快捷键。

要挂钩键盘事件,我将不得不使用一些特定于平台的代码。 Go 封装平台特定代码的方法是什么?我一直在搜索编译器开关或平台模块等关键字,但我真的找不到。

【问题讨论】:

  • 出于好奇,“封装”是什么意思?另外,runtime.GOOS 的 if 语句有什么问题?
  • 我使用了封装,因为我想使用 Go-way 来分离平台特定的代码。 runtime.GOOS 没有任何问题,如果它可以处理特定于平台的代码,而这些代码可能无法在其他平台上编译。
  • 啊,这更有意义。我不确定封装是正确的词,但足够公平。对于您的第二点,老实说,我想不出一个可以在一个 平台 上编译但不能在另一个平台上编译的有效 Go 代码示例。 (不同的编译器是一个不同的问题。)但是,我可以想到一些可能崩溃或中断的示例,但这就是 if runtime.GOOS == "windows" {} 的想法。
  • 用什么词比较好?我总是想学习一些东西:-)
  • 嗯,对我来说,“封装”的含义是 A)荒谬的 OOP 对象模型和设计/API 原则(Java#,有人吗?)或 B)通过源代码混淆隐藏正在发生的事情/更荒谬API 暴露原则。更好的词可能是“单独的”,或者在这种情况下可能是“有条件地编译”。 :)

标签: go architecture cross-platform


【解决方案1】:

平台特定代码的解决方案是build constraints

注意:在 Go 1.17 之前,语法是以 // +build 开头的注释行,但 Go 1.17 引入了 //go:build pragma,现在是首选方式。

构建约束,也称为构建标记,是开始的行注释

//go:build

列出了文件应包含在包中的条件。约束可能出现在任何类型的源文件中(不仅仅是 Go),但它们必须出现在文件顶部附近,前面只能有空行和其他行 cmets。这些规则意味着在 Go 文件中,构建约束必须出现在 package 子句之前。

因此,基本上每个平台特定的 Go 代码都应该放入不同的文件中,并且您可以将这些 Go 文件中的每一个标记为它们的目标。

例如,如果文件包含 Linux 特定代码(例如系统调用),则以:

开头
//go:build linux

如果文件包含 Windows 特定的系统调用,请使用以下命令开始:

//go:build windows

还有更多“选项”可用,请阅读链接文档。例如,您可以指定对操作系统、体系结构、Go 版本、正在使用的编译器的约束。您还可以指定将使用逻辑 OR 或 AND 解释的多个约束,或者您可以使用否定(例如,此代码适用于除 linux 之外的每个目标平台)。

您甚至可以使用以下约束将.go 文件标记为被忽略:

//go:build ignore

请注意,构建约束是特定于编译器的。如果特定编译器无法识别构建约束,编译器将忽略该文件。例如,Go AppEngine SDK 带有一个内置的、经过修改的 Go 编译器,该编译器还可以识别

//go:build appengine

约束,这意味着源文件仅适用于 Google App Engine 平台。 “常规”Go 编译器将忽略该文件,如果有人尝试在没有 Go AppEngine SDK 的情况下构建代码,您可能不会遇到一堆编译器错误。

【讨论】:

    【解决方案2】:

    虽然@icza's answer 绝对是 OP 关于条件编译的问题的正确权威答案,但在这种特定情况下,更简单的方法可能是值得的。

    Go 是一种旨在在每个平台上编译相同(使用相同编译器)的语言,以鼓励跨平台代码和库重用。 Go 不是 C/C++,因此必须花费时间来显式地编写跨平台代码。

    当然,平台不可知性仅在 Go 的运行时范围内进行,并且您正在尝试捕获按键,对此没有真正的单一平台不可知解决方案。

    因此,我对这个用例的更简单的建议类似于以下内容:

    package main
    
    import (
        "runtime"
        kp "kplib"
    )
    
    func main () {
        switch runtime.GOOS {
        case "windows":
            kp.WinKey()
        case "darwin":
            kp.MacKey()
        case "linux":
            kp.UnixKey()
        default:
            kp.TryKey()
        }
    }
    

    通过隐式保证,假设的kplib 将在任何地方编译(只要确保在给定平台上调用正确的方法!)。

    【讨论】:

      【解决方案3】:

      要使代码特定于平台,您可以使用build constraintsFor more info

      Windows

      //go:build windows
      // +build windows
      
      

      不是 Windows

      //go:build !windows
      // +build !windows
      
      

      不是 Windows 和达尔文

      //go:build !windows && !darwin
      // +build !windows,!darwin
      
      

      苹果机

      //go:build darwin
      // +build darwin
      
      

      Linux

      //go:build linux
      // +build linux
      
      

      示例

      //go:build windows
      // +build windows
      
      package main
      
      func main() {
         // windows specific code
      }
      
      

      或者您可以使用file naming convention 将代码放在不同操作系统的单独文件中。

      例如:-

      路径

      • path.go
      • path_windows.go
      • path_darwin.go
      • path_linux.go

      Building go applications for different OS & Architecture

      【讨论】:

        猜你喜欢
        • 2022-02-09
        • 1970-01-01
        • 2020-02-02
        • 2021-01-26
        • 1970-01-01
        • 1970-01-01
        • 2012-11-30
        • 1970-01-01
        • 2020-06-22
        相关资源
        最近更新 更多