go build tag 实现开发环境与生产环境采用不同的 embed.FS 策略

文章目录

    通过 go embed.FS 将 gin 模板及静态资源文件打包进二进制程序
    确实非常方便在服务器上部署、更新程序,但是开发环境调试前端代码就非常麻烦了。
    因为每次修改都需要重新编译。

    所以,我想利用 go build tag 条件编译来实现,开发环境不启用 embed.FS,只在发布时使用。

    重命名问题

    xxx redeclared in this block

    build tag 的互斥来解决。即

    //go:build !prod
    //go:build prod
    

    省略任何一个,如果定义了同名的函数,或者变量,都会导致重复声明的问题。

    文件命名来区分

    绞尽脑汁起名 load.go / load_embed.go, 不如 dev.go / prod.go 直观。

    实现代码

    dev.go

    //go:build !prod
    
    package templates
    
    import (
    	// ...
    )
    
    // LoadTemplateAndStatic loads from disk
    func LoadTemplateAndStatic(router *gin.Engine, funcMap template.FuncMap) {
    	// ...
    }
    

    prod.go

    //go:build prod
    
    package templates
    
    import (
    	// ...
    )
    
    //go:embed *
    var f embed.FS
    
    // LoadTemplateAndStatic loads from embed fs
    func LoadTemplateAndStatic(router *gin.Engine, funcMap template.FuncMap) {
    	// ...
    }
    

    然后在 main.go 中调用这个同名函数即可。

    验证

    分别编译两个版本,对比一下就能发现,确实生效了。

    go build
    go build -tags prod
    

    gopls 警告信息

    prod.go|3 col 9-17 warning| No packages found for open file /mnt/d/work/tools2/backend/templates/prod.go: <nil>. If this file contains build tags, try adding "-tags=<build tag>" to your gopls "buildFlag" configuration (see (https://github.com/golang/tools/blob/master/gopls/doc/settings.md#buildflags-string). Otherwise, see the troubleshooting guidelines for help investigating (https://github.com/golang/tools/blob/master/gopls/doc/troubleshooting.md).
    

    翻了半天的 vim-go 代码,才找到 gopls build tags 的设置方法。

    function! go#config#BuildTags() abort
      return get(g:, 'go_build_tags', '')
    endfunction
    
    let l:buildtags = go#config#BuildTags()
    if buildtags isnot ''
      let l:config.buildFlags = extend(l:config.buildFlags, ['-tags', go#config#BuildTags()])
    endif
    
    let g:go_build_tags = "prod"
    

    然而并没有作用,我怀疑是 VIM 下 ale 和 vim-go 都用了 gopls,没有都配置才导致此问题。

    不想折腾了,等代码上线在回头来看看这部分的配置。

    init 的使用

    一些 build tags 是通过 init 来执行相关的逻辑的。

    虽然最终没有用这个方案,但是尝试了一个版本实现,还是了解了这部分逻辑:

    • 一个 package 中多个 init 的使用场景: https://stackoverflow.com/questions/45278540/whats-the-purpose-of-golang-allowing-multiple-init-in-one-package
    • 多个 init 的执行顺序: https://stackoverflow.com/questions/32829538/init-order-within-a-package/32829593#32829593

    关于作者 🌱

    我是来自山东烟台的一名开发者,有感兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊,或者关注我的个人公众号“大象工具”, 查看更多联系方式