英文:
What is the right approach to encapsulate platform specific code in Go?
问题
我想开发一个小型的Go应用程序,用于向演示的观众展示使用的按键快捷方式。
为了钩入键盘事件,我将不得不使用一些特定于平台的代码。**在Go中,如何封装特定于平台的代码?**我一直在搜索编译器开关或平台模块等关键字,但我没有找到相关的内容。
英文:
I want to develop a small Go application, which shows the used key stroke shortcuts to the audience of a presentation.
To hook into the keyboard events I will have to use some platform specific code. What is the Go way to encapsulate platform specific code? I've been searching for keywords like compiler switch or platform modules, but I couldn't really find something about.
答案1
得分: 23
平台特定代码的解决方案是构建约束(build constraints)。
<sup>**注意:在 Go 1.17 之前,语法是以 // +build
开头的注释行,但 Go 1.17 引入了 //go:build
声明,现在是首选方式。</sup>
> 构建约束,也称为构建标签,是一个以行注释形式开始的代码行,其中列出了应将文件包含在包中的条件。约束可以出现在任何类型的源文件中(不仅仅是 Go 文件),但它们必须出现在文件的顶部附近,仅在空行和其他行注释之前。这些规则意味着在 Go 文件中,构建约束必须出现在包声明之前。
因此,每个特定于平台的 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 的情况下构建代码时避免一堆编译器错误。
英文:
The solution to platform specific code is the build constraints.
<sup>Note: Prior to Go 1.17 the syntax was a comment line starting with // +build
, but Go 1.17 introduced the //go:build
pragma which is now the preferred way.</sup>
> A build constraint, also known as a build tag, is a line comment that begins
>
> //go:build
> that lists the conditions under which a file should be included in the package. Constraints may appear in any kind of source file (not just Go), but they must appear near the top of the file, preceded only by blank lines and other line comments. These rules mean that in Go files a build constraint must appear before the package clause.
So basically each platform specific Go code should go into different files, and you can mark each of these Go files with the target they are intended for.
For example if a file contains Linux specific code (e.g. syscalls), start it with:
//go:build linux
If a file contains Windows specific syscalls, start it with:
//go:build windows
A lot more "options" are available, read the linked docs. For example you can specify constraints to the OS, Architecture, Go version, the compiler being used. You can also specify multiple constraints that will be interpreted with logical OR or AND, or you can use negation (e.g. this code is for every target platform except linux).
You can even mark a .go
file to be ignored with the following constraint:
//go:build ignore
Note that build constraints are compiler specific. If a specific compiler does not recognize a build constraint, the compiler will ignore the file. As an example the Go AppEngine SDK comes with a built-in, modified Go compiler which additionally recognizes the
//go:build appengine
constraint, which means the source file is intended for the Google App Engine Platform only. "Regular" Go compilers will ignore the file, giving you the possibility to not have a bunch of compiler errors if someone tries to build the code without the Go AppEngine SDK.
答案2
得分: 7
虽然@icza的答案绝对是对于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
将在任何地方编译(只需确保在给定平台上调用正确的方法!)。
英文:
While @icza's answer is definitely the proper authoritative answer to OP's question about condtional compilation, in this specific case a simpler approach may be worthwhile.
Go is a language designed to compile the same (with the same compiler) on every platform, to encourage cross-platform code and library reuse. Go is not C/C++, for which time must be spent to write explicitly cross-platform code.
Of course, the platform agnosticity only goes as far as Go's runtime, and you're trying to capture keypresses, for which there's no real single platform-agnostic solution.
Thus, my simpler suggestion for this use case resembles the following:
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()
}
}
With the implicit assurance the hypothetical kplib
will compile everywhere (just be sure to call the right methods on the given platform!).
答案3
得分: 0
要使代码适用于特定平台,你可以使用"构建约束"(build constraints)。具体信息请参考这里。
Windows
//go:build windows
// +build windows
非Windows
//go:build !windows
// +build !windows
非Windows和Darwin
//go:build !windows && !darwin
// +build !windows,!darwin
Mac
//go:build darwin
// +build darwin
Linux
//go:build linux
// +build linux
示例
//go:build windows
// +build windows
package main
func main() {
// 适用于Windows的代码
}
或者你可以根据不同的操作系统将代码放在不同的文件中,使用"文件命名约定"(file naming convention)。
例如:
路径
- path.go
- path_windows.go
- path_darwin.go
- path_linux.go
英文:
To make code platform-specific, you can go for build constraints
. For more info
Windows
//go:build windows
// +build windows
Not Windows
//go:build !windows
// +build !windows
Not Windows & Darwin
//go:build !windows && !darwin
// +build !windows,!darwin
Mac
//go:build darwin
// +build darwin
Linux
//go:build linux
// +build linux
Example
//go:build windows
// +build windows
package main
func main() {
// windows specific code
}
Or you can place the code in separate file for different OS with file naming convention
.
Eg:-
path
- path.go
- path_windows.go
- path_darwin.go
- path_linux.go
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论