golang交叉编译和条件编译的实际应用

简介: 交叉编译是指在一个平台上生成可在另一平台运行的可执行代码。它允许开发者在资源受限或未准备好的目标环境下提前编译程序。条件编译则根据特定条件选择性地编译代码,常用于适配不同运行环境或区分开发与发布版本。两者结合,能有效提升开发效率与程序适配能力。

什么是交叉编译

先给出维基百科百度百科解释(tips:维基百科只有交叉编译器的解释)

维基百科

交叉编译器(英语:Cross compiler)是指一个在某个系统平台下可以产生另一个系统平台的可执行文件的编译器。交叉编译器在目标系统平台(开发出来的应用程序序所运行的平台)难以或不容易编译时非常有用。

百度百科

交叉编译是在一个平台上生成另一个平台上的可执行代码。同一个体系结构可以运行不同的操作系统;同样,同一个操作系统也可以在不同的体系结构上运行。

虽然维基百科上只有交叉编译器的解释,但是结合百度百科来看,其实意思是一致的,就是可以在A平台编译出B或者C平台的可执行程序

所以交叉编译是在当前基础平台(开发者使用的环境)编译出在分发平台(运行环境)能运行的程序的解决方案。

有时候我们在开发一个项目的时候,目标平台资源并没有准备好,比如在windows开发,但是运行平台是linux,而linux服务器还没购买,或者linux是并不允许安装编译器等等,这时候我们需要在开发机编译出目标机的运行程序,那么交叉编译将变得非常有用。

什么是条件编译

维基百科

百度百科

条件编译允许只编译源文件中满足条件的程序 段,使生成的目标程序较短,从而减少了内存的开销,并提高程序的效率,可以按不同的 条件去编译不同的程序部分,因而产生不同的目标代码文件。这对于程序的移植和调试是很有用的。 [2] 另外,条件编译是为了让程序在各种不同的软硬件环境下都以运行。即,提高了程序的可移植性和灵活性。

所谓的条件编译,就是在指定的条件下编译满足条件的源码文件或者代码段。以达到适配指定的运行环境。实际使用中可以用于调试或者发布(区分开发调试环境和线上部署环境)。

golang中使用交叉编译

我们知道golang一份代码可以编译出在不同系统和cpu架构运行的二进制文件。go也提供了很多环境变量,我们可以设置环境变量的值,来编译不同目标平台。

GOOS 目标平台, GOARCH目标架构

体验AI代码助手

代码解读

复制代码

# 编译目标平台linux 64位
GOOS=linux GOARCH=amd64 go build main.go

# 编译目标平台windows 64位
GOOS=windows GOARCH=amd64 go build main.go

常用的GOOS和GOARCH

GOOS GOARCH 系统版本
linux 386 / amd64 / arm / arm64 / ppc64 / ppc64le >= Linux 2.6
freebsd 386 / amd64 OS X (Snow Leopard + Lion)
windows 386 / amd64 >= FreeBSD 7
darwin 386 / amd64 >= Windows 2000
openbsd 386 / amd64 / arm unknow

golang使用条件编译

golang中有两种使用条件编译的方式,一种是通过文件名的 命名规则,另一种则是注释,一种特别的 标签注释,通过这种注释,golang编译器可以在编译时识别要编译的文件或者代码段。

1、命名规则

体验AI代码助手

代码解读

复制代码

* _GOOS
* _GOARCH
* _GOOS_GOARCH
(示例:source_windows_amd64.go),其中GOOS和GOARCH分别代表任何已知的操作系统和体系结构值(也就是环境变量GOOS和GOARCH的值),符合命名规则的文件会按照隐式约束构建。

注意下命名的顺序。_GOOS_GOARCH是可以的,但是_GOARCH_GOOS不行,也就是说GOOS必须在GOARCH之前。

2、标签注释条件编译标签注释格式以 // +build 开头,比如官网例子:

体验AI代码助手

代码解读

复制代码

// +build linux,cgo darwin,cgo

注意编译标签注释 如果不是写在源码文件的第一行的话,需要上下空一行,与正常的注释和代码隔开,不然的话,编译器会忽略,无法识别。

编译标签注释之间也会有逻辑运算,对应关系如下

规则 逻辑运算符
空格 OR
逗号 AND
感叹号 NOT
换行 AND

按照官网的例子来说明下

体验AI代码助手

代码解读

复制代码

// +build linux,386 darwin,!cgo
# 对应的逻辑运算:
(linux AND 386) OR (darwin AND (NOT cgo))

体验AI代码助手

代码解读

复制代码

// +build linux darwin
// +build 386
# 对应的逻辑运算:
(linux OR darwin) AND 386

对应的条件可以有如下值:

操作系统, 值可以通过 runtime.GOOS 获取,比如 linux

CPU架构, 值可以通过 runtime.GOARCH 获取 , 比如 amd64 编译器,如 gc, gccgo

是否开启Cgo, cgo

语言版本, Go版本如 go1.1,...,go1.12

自定义标签, 任意标签,可以是发布版本号,开发版本等等, 比如生产环境prod

实际应用

在实际开发中,一般正常的商业项目都会区分开发环境测试环境灰度验证环境正式环境。那么这么多环境,数据源,redis,日志级别等的配置一般也不一样。如何保证在不同的环境使用不同的配置呢。

1、 启动时 指定配置文件

编译后的可执行文件在目标环境执行时,可以通过指定参数的方式来确定执行环境,读取的配置文件。

体验AI代码助手

代码解读

复制代码

# 假设编译后的执行文件名为 server, $exec_path为可执行文件所在路径,比如/usr/local
# 开发调试环境
$exec_path/server debug
# 线上正式环境
$exec_path/server prod

如果使用这种方式就需要在代码里判断传递的参数,然后使用对应的配置。编写文件server.go如下

体验AI代码助手

代码解读

复制代码

package main

import (
	"fmt"
	"os"
)

func main()  {
	serverMode := os.Args[1]
	switch serverMode {
	case "debug":
		// 加载调试模式配置
		fmt.Printf("传递模式为%s,加载调试模式配置\r\n", serverMode)
	case "prod":
		// 加载正式环境配置
		fmt.Printf("传递模式为%s,加载正式环境配置\r\n", serverMode)
	default:
		panic("启动模式错误")
	}
}

编译启动

体验AI代码助手

代码解读

复制代码

# 编译
go build server.go 
# 执行
./server debug
#输出如下
传递模式为debug,加载调试模式配置

2、 通过 环境变量

体验AI代码助手

代码解读

复制代码

# 设置环境变量(linux环境)
export SERVER_MODE='debug'

# 在代码里读取变量,然后可以按照读取的值加载不同的配置。
os.Getenv("SERVER_MODE")

3、 编译时 使用ldflags 编写config.go

体验AI代码助手

代码解读

复制代码

package main

import "fmt"

var mode string

func main()  {
	fmt.Println("mode value is:", mode)
}

编译运行

体验AI代码助手

代码解读

复制代码

# 编译
go build -ldflags '-X main.mode=prod' config.go

# 运行
./config

# 输出如下
mode value is: prod

既然编译时可以确定mode的值,那么想要根据mode加载不同的配置,那么就轻而易举的解决了。

4、 使用条件编译分别编写config_prod.go 和 config_dev.go 分别代表生产环境和开发环境的配置。 项目布局如下

体验AI代码助手

代码解读

复制代码

.
├── config
│   ├── config_dev.go
│   └── config_prod.go
└── main.go

config_prod.go

体验AI代码助手

代码解读

复制代码

//+build prod

package config

var String = "this is prod mode"
var String2 = "prod test"

func Config() string {
	return String2
}

config_dev.go

体验AI代码助手

代码解读

复制代码

//+build dev

package config

var String1 = "this is debug mode"
var String2 = "debug test"

func Config() string {
	return String2
}

main.go

体验AI代码助手

代码解读

复制代码

package main

import (
	"config"
	"fmt"
)

func main()  {
	fmt.Println("this build is: ", config.String1, config.String2, config.Config())
}

编译运行

体验AI代码助手

代码解读

复制代码

# 编译(-o 指定编译后生成的可执行文件名)
go build -tags prod -o main

# 运行
 ./main 
 
# 输出结果
this build is:  this is prod mode prod test prod test

可见使用这种方式也达到了不同环境使用不同配置的目的。实际项目开发中,根据项目规划需要来选择。

Tips: 在goland中添加自定义的编译标签时,会提示如下错误:

并且在main.go里面识别不到config里面的内容:

这是因为自定义编译标签goland是无法识别的。只需要点击右上角的 Edit Go Project Settings 填入自定义标签即可。

填入当前使用的标签

总结

上面简单介绍了交叉编译和条件编译,以及在实际项目中如何来区分环境,可以通过4种不同的方式来达到我们的目的,有更好方式欢迎交流和补充。


转载来源:https://juejin.cn/post/6844903998739202055

相关文章
|
消息中间件 Kubernetes 数据安全/隐私保护
milvus本地集群部署(非k8s)
milvus本地集群部署(非k8s)
1376 0
|
9月前
|
人工智能 编解码 Java
Go-如何优雅的使用字节池
在Go语言中,为了优化大量使用字节数组带来的性能损耗,可通过对象池技术实现字节数组复用。本文介绍了几种常见的字节池实现方式,包括使用 `sync.Pool`、`bytes.Buffer` 以及基于 `channel+select` 的固定大小字节池,并通过性能测试对比了不同方法的效率差异。最终总结出适用于不同场景的字节池设计方案,以提升程序性能。
193 1
|
8月前
|
人工智能 监控 中间件
如何用go语言实现类似AOP的功能
本文介绍了如何在 Go 语言中借鉴 Java 的 AOP(面向切面编程)思想,通过 Gin 框架的中间件和函数包装机制实现日志记录、权限校验等横切关注点与业务逻辑的解耦。内容涵盖 AOP 的优点、Go 中的实现方式、Gin 中间件与 AOP 的异同,帮助开发者提升代码模块化与可维护性。
308 0
|
9月前
|
人工智能 安全 调度
Python并发编程之线程同步详解
并发编程在Python中至关重要,线程同步确保多线程程序正确运行。本文详解线程同步机制,包括互斥锁、信号量、事件、条件变量和队列,探讨全局解释器锁(GIL)的影响及解决线程同步问题的最佳实践,如避免全局变量、使用线程安全数据结构、精细化锁的使用等。通过示例代码帮助开发者理解并提升多线程程序的性能与可靠性。
283 0
|
9月前
|
人工智能 安全 Java
Go与Java泛型原理简介
本文介绍了Go与Java泛型的实现原理。Go通过单态化为不同类型生成函数副本,提升运行效率;而Java则采用类型擦除,将泛型转为Object类型处理,保持兼容性但牺牲部分类型安全。两种机制各有优劣,适用于不同场景。
449 24
|
9月前
|
SQL 人工智能 关系型数据库
有哪些方式优化慢 SQL?
慢 SQL 优化主要包括 SQL 语句和数据库设计两方面。应避免使用 `SELECT *`,只选择必要列;对深分页进行优化,如延迟关联或使用 ID 偏移量;合理使用索引,如覆盖索引、避免 `OR` 查询和不等于操作符;优化 JOIN 操作,减少多表关联;并利用索引排序、条件下推等方式提升查询效率。
239 1
|
9月前
|
人工智能 前端开发 安全
Java开发不可不知的秘密:类加载器实现机制
类加载器是Java中负责动态加载类到JVM的组件,理解其工作原理对开发复杂应用至关重要。本文详解类加载过程、双亲委派模型及常见类加载器,并介绍自定义类加载器的实现与应用场景。
355 4
|
9月前
|
人工智能 测试技术 持续交付
Golang深入浅出之-Go语言中的持续集成与持续部署(CI/CD)
持续集成与持续部署(CI/CD)是现代软件开发的关键实践,尤其适用于Go语言项目。本文探讨了Go项目中常见的CI/CD问题,如测试覆盖不足、版本不一致和构建时间过长,并提供解决方案及GitHub Actions示例代码,帮助开发者优化流程,提升交付效率和质量。
282 5
下一篇
开通oss服务