Go Build for windows


原文链接: Go Build for windows

StringFileInfo Windows applications

golang编译的exe程序设置图标和右键的详细信息

第一步:Windows 下载MinGW
第二步:新建一个.rc文件,加入文件名为 demo.rc 输入内容
IDI_ICON1 ICON "icon.ico"
其中 icon.ico 是你的ico的地址
第三步:MinGW 执行
windres -o demo.syso demo.rc
需要将demo.syso放到需要编译的go目录下
第四步: go build 编译出exe程序
第五步:需要一个工具,叫做 ResourceHacker , 可以在网上直接搜索下载
第六步:使用ResourceHacker打开编译出的exe程序,点击添加使用脚本模板
第七步:在弹出框里选择VERSION_INFO
第八步: 在新建的文件中修改信息即可,信息的字段说明可以参考如下地址内容
https://msdn.microsoft.com/en-us/library/windows/desktop/aa381049(v=vs.85).aspx
第九步:编辑完之后按F5编译并且保存,基本上就算完成了
补充: 想让go编译的程序在Windows点击运行不启动终端gui,可以在编译的时候加入如下参数
-ldflags "-H windowsgui"

Makefile

.PHONY: all
all: windows

.PHONY: windows-dependencies
windows-dependencies:
	go get github.com/josephspurrier/goversioninfo/cmd/goversioninfo

.PHONY: embed-assets
embed-assets:
	# go get github.com/jteeuwen/go-bindata/...
	# go-bindata ./logos/microBadger_headert.png ./webpage.html

.PHONY: linux
linux: *.go embed-assets
	GOOS=linux GOARCH=amd64 go build -o binaries/microbadger_linux_64bit
	GOOS=linux GOARCH=386 go build -o binaries/microbadger_linux_32bit
	strip binaries/microbadger_linux_*

.PHONY: windows 
windows: *.go windows-dependencies embed-assets
	goversioninfo -icon=icon.ico
	CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags="-s -w -H=windowsgui -linkmode internal" -o binaries/deploy_windows_32bit.exe
	CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags="-s -w -H=windowsgui -linkmode internal" -o binaries/deploy_windows_64bit.exe
	rm resource.syso

.PHONY: osx
osx: *.go embed-assets
	GOOS=darwin GOARCH=amd64 go build -o binaries/microbadger_osx_64bit
	GOOS=darwin GOARCH=386 go build -o binaries/microbadger_osx_32bit


.PHONY: clean
clean:
	rm -rf binaries/*

go - How do you set the application icon in golang? - Stack Overflow

goversioninfo

  1. Install
    go get github.com/josephspurrier/goversioninfo/cmd/goversioninfo

  2. 创建 versioninfo.json

{
    "FixedFileInfo": {
        "FileVersion": {
            "Major": 2018,
            "Minor": 6,
            "Patch": 9,
            "Build": 0
        },
        "ProductVersion": {
            "Major": 2018,
            "Minor": 6,
            "Patch": 9,
            "Build": 0
        },
        "FileFlagsMask": "3f",
        "FileFlags ": "00",
        "FileOS": "040004",
        "FileType": "01",
        "FileSubType": "00"
    },
    "StringFileInfo": {
        "FileDescription": "1.文件说明",
        "FileVersion": "1.0.1",
        "ProductName": "3.产品名称",
        "ProductVersion": "1.0.1",
        "LegalCopyright": "Copyright@pytool",
        "OriginalFilename": "deploy.exe",
        "Comments": " ",
        "CompanyName": "Company",
        "InternalName": "",
        "LegalTrademarks": "",
        "PrivateBuild": "",
        "SpecialBuild": ""
    },
    "VarFileInfo": {
        "Translation": {
            "LangID": "0409",
            "CharsetID": "04B0"
        }
    },
    "IconPath": "",
    "ManifestPath": ""
}

//go:generate goversioninfo -icon=icon.ico
go build -ldflags -H=windowsgui
CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags="-s -w -H=windowsgui -linkmode internal"

rsrc

  1. 创建 dll harden.exe.manifest
    Windows对于Manifest的处理
      XP以前版本的windows,会像以前那样执行这个exe文件,寻找相应的dll,没有分别,Manifest只是个多余的文件或资源,dll文件会直接到system32的目录下查找,并且调用。
      而XP及其以后的操作系统,则会首先读取Manifest,获得exe文件需要调用的DLL列表(此时获得的,并不直接是DLL文件的本身的位置,而是DLL的manifest)操作系统再根据DLL的Manifest提供的信息去寻找对应的DLL ,这样就可能区别不同版本的同一个DLL文件。
      这就说明了为什么我的程序可以在2000下面运行,而在XP及2003上无法运行。这也使得很多木马可以利用这个特点实现限制安全软件。

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    	<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    		<assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="Hardentools" type="win32"/>
    		<dependency>
    			<dependentAssembly>
    				<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
    			</dependentAssembly>
    		</dependency>
    		<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    			<security>
    				<requestedPrivileges>
    					<!-- <requestedExecutionLevel level="asInvoker" uiAccess="false" />   -->
    					<!-- <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />   -->
    					<requestedExecutionLevel level="highestAvailable" uiAccess="false" />  
    				</requestedPrivileges>
    			</security>
    		</trustInfo>
    	</assembly>
    
    <?xml version="1.0" encoding="utf-8"?>
    <asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <assemblyIdentity version="1.0.0.0" name="MyApplication.app" />
    <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
        <!-- UAC 清单选项
            如果要更改 Windows 用户帐户控制级别,请用以下节点之一替换 
            requestedExecutionLevel 节点。
    
        <requestedExecutionLevel  level="asInvoker" uiAccess="false" />
        <requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />
        <requestedExecutionLevel  level="highestAvailable" uiAccess="false" />
    
            指定 requestedExecutionLevel 节点将会禁用文件和注册表虚拟化。
            如果要利用文件和注册表虚拟化实现向后 
            兼容性,则删除 requestedExecutionLevel 节点。
        -->
        <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
      </requestedPrivileges>
      <applicationRequestMinimum>
        <defaultAssemblyRequest permissionSetReference="Custom" />
        <PermissionSet class="System.Security.PermissionSet" version="1" ID="Custom" SameSite="site" Unrestricted="true" />
      </applicationRequestMinimum>
    </security>
    </trustInfo>
    <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    	<application> 
    		<!-- Windows 10 --> 
    		<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
    		<!-- Windows 8.1 -->
    		<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
    		<!-- Windows Vista -->
    		<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> 
    		<!-- Windows 7 -->
    		<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
    		<!-- Windows 8 -->
    		<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
    </application> 
    </compatibility>
    <!-- 启用 Windows 公共控件和对话框的主题(Windows XP 和更高版本) -->
    <!-- <dependency>
    <dependentAssembly>
      <assemblyIdentity
          type="win32"
          name="Microsoft.Windows.Common-Controls"
          version="6.0.0.0"
          processorArchitecture="*"
          publicKeyToken="6595b64144ccf1df"
          language="*"
        />
    </dependentAssembly>
    </dependency>-->
    </asmv1:assembly>
    

//go:generate rsrc -manifest harden.manifest -ico harden.ico -o rsrc.syso

manifest 权限

asInvoker : 应用程序就是以当前的权限运行。
highestAvailable: 这个是以当前用户可以获得的最高权限运行。
requireAdministrator: 这个是仅以系统管理员权限运行。

默认情况下是 asInvoker。
highestAvailable 和 requireAdministrator 这两个选项都可以提示用户获取系统管理员权限。那么这两个选项的区别在哪里呢?
他们的区别在于,如果我们不是以管理员帐号登录,那么如果应用程序设置为 requireAdministrator ,那么应用程序就直接运行失败,无法启动。而如果设置为 highestAvailable,则应用程序可以运行成功,但是是以当前帐号的权限运行而不是系统管理员权限运行。如果我们希望程序在非管理员帐号登录时也可以运行(这种情况下应该某些功能受限制) ,那么建议采用 highestAvailable 来配置。

go env

GOARCH="amd64"
GOBIN="/home/ubuntu/go/bin"
GOCACHE="/home/ubuntu/.cache/go-build"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/ubuntu/go"
GORACE=""
GOROOT="/usr/local/go"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build269284781=/tmp/go-build -gno-record-gcc-switches"

CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build main.go
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags '-s -w' -ldflags="-H windowsgui" main.go
CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags="-s -w -linkmode internal"
GOOS:目标平台的操作系统(darwin、freebsd、linux、windows)
GOARCH:目标平台的体系架构(386、amd64、arm)

Window walk 编译

GOOS=windows GOARCH=386 CC=i686-w64-mingw32-gcc CGO_ENABLED=1 go get -u --ldflags '-s -w -extldflags "-static" -H windowsgui' github.com/lxn/win

GOOS=windows GOARCH=386 CC=i686-w64-mingw32-gcc CGO_ENABLED=1 go get -u --ldflags '-s -w -extldflags "-static" -H windowsgui' github.com/lxn/walk

golang 调用cmd下程序隐藏黑窗口

隐藏程序自身黑窗口的方法:go build -ldflags="-H windowsgui"
隐藏子进程黑窗口的方法: cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}

  1. command_windows.go
    ```go
    // +build windows !linux !darwin !freebsd

package main

import (
"os/exec"
"syscall"
)

func hideWindow(cmd *exec.Cmd) {
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
}

2. command.go
```go
// +build linux darwin freebsd !windows

package main

import (
	"os/exec"
)

func hideWindow(cmd *exec.Cmd) {
}
  1. main.go
    ```go
    package main

import (

"context"
"fmt"
"os"
"os/exec"
"syscall"
"time"

)

func main() {

ctx, cancel := context.WithCancel(context.Background())

cmd := exec.CommandContext(ctx, "ls")
hideWindow(cmd)

cmd.Stdout = os.Stdout
cmd.Start()
time.Sleep(10 * time.Second)
fmt.Println("退出程序中...", cmd.Process.Pid)
cancel()
cmd.Wait()

}
```

关闭编译器代

建议使用 -gcflags "-N -l" 参数关闭编译器代码优化和函数
内联,避免断点和单步执行无法准确对应源码行,避免小函数和局部变量被优化掉。
go build -gcflags "-N -l" -o test test.go

go help build

构建编译由导入路径命名的包,以及它们的依赖关系,但它不会安装结果.
使用

go build [-o 输出名] [-i] [编译标记] [包名]

如果参数为***.go文件或文件列表,则编译为一个个单独的包。
当编译单个main包(文件),则生成可执行文件。
当编译单个或多个包非主包时,只构建编译包,但丢弃生成的对象(.a),仅用作检查包可以构建。
当编译包时,会自动忽略'_test.go'的测试文件。
参数
-o output 指定编译输出的名称,代替默认的包名。

-i install 安装作为目标的依赖关系的包(用于增量编译提速)。

以下 build 参数可用在 build, clean, get, install, list, run, test

-a 完全编译,不理会-i产生的.a文件(文件会比不带-a的编译出来要大?)
-n 仅打印输出build需要的命令,不执行build动作(少用)。
-p n 开多少核cpu来并行编译,默认为本机CPU核数(少用)。
-race 同时检测数据竞争状态,只支持 linux/amd64, freebsd/amd64, darwin/amd64 和 windows/amd64.
-msan 启用与内存消毒器的互操作。仅支持linux / amd64,并且只用Clang / LLVM作为主机C编译器(少用)。
-v 打印出被编译的包名(少用).
-work 打印临时工作目录的名称,并在退出时不删除它(少用)。
-x 同时打印输出执行的命令名(-n)(少用).
-asmflags 'flag list'

传递每个go工具asm调用的参数(少用)

-buildmode mode

编译模式(少用)
'go help buildmode'

-compiler name

使用的编译器 == runtime.Compiler
(gccgo or gc)(少用).

-gccgoflags 'arg list'

gccgo 编译/链接器参数(少用)

-gcflags 'arg list'

垃圾回收参数(少用).

-installsuffix suffix

??????不明白
a suffix to use in the name of the package installation directory,
in order to keep output separate from default builds.
If using the -race flag, the install suffix is automatically set to race
or, if set explicitly, has _race appended to it.  Likewise for the -msan
flag.  Using a -buildmode option that requires non-default compile flags
has a similar effect.

-ldflags 'flag list'

'-s -w': 压缩编译后的体积
-s: 去掉符号表
-w: 去掉调试信息,不能gdb调试了

-linkshared

链接到以前使用创建的共享库
-buildmode=shared.

-pkgdir dir

从指定位置,而不是通常的位置安装和加载所有软件包。例如,当使用非标准配置构建时,使用-pkgdir将生成的包保留在单独的位置。

-tags 'tag list'

构建出带tag的版本.

-toolexec 'cmd args'

??????不明白
a program to use to invoke toolchain programs like vet and asm.
For example, instead of running asm, the go command will run
'cmd args /path/to/asm <arguments for asm>'.

以上命令,单引号/双引号均可。

对包的操作'go help packages'
对路径的描述'go help gopath'
对 C/C++ 的互操作'go help c'

注意

构建遵守某些约定('go help gopath'),但不是所有的项目都遵循这些约定,当使用自己的惯例或使用单独的软件构建系统时可以选择使用较低级别的调用go tool compile和go tool link来避免一些构建工具的开销和设计决策

你是刚开始使用Go工具么?或者你想扩展知识?这篇文章将会描述每个人都需要知道的Go工具参数。

$ go build -x

-x 会列出来go build调用到的所有命令。

如果你对Go的工具链好奇,或者使用了一个跨C编译器,并且想知道调用外部编译器用到的具体参数,或者怀疑链接器有bug;使用-x来查看所有调用。

$ go build -x
WORK=/var/folders/00/1b8h8000h01000cxqpysvccm005d21/T/go-build600909754
mkdir -p $WORK/hello/perf/_obj/
mkdir -p $WORK/hello/perf/_obj/exe/
cd /Users/jbd/src/hello/perf
/Users/jbd/go/pkg/tool/darwin_amd64/compile -o $WORK/hello/perf.a -trimpath $WORK -p main -complete -buildid bbf8e880e7dd4114f42a7f57717f9ea5cc1dd18d -D _/Users/jbd/src/hello/perf -I $WORK -pack ./perf.go
cd .
/Users/jbd/go/pkg/tool/darwin_amd64/link -o $WORK/hello/perf/_obj/exe/a.out -L $WORK -extld=clang -buildmode=exe -buildid=bbf8e880e7dd4114f42a7f57717f9ea5cc1dd18d $WORK/hello/perf.a
mv $WORK/hello/perf/_obj/exe/a.out perf

$ go build -gcflags

这个参数将会传递给编译器。go tool compile -help列出来了所有我们可以传递给编译器的参数。

禁用编译器优化和内联优化,你可以使用下面的参数:

$ go build -gcflags="-N -I"

$ go test -v

这个命令可以为测试提供完整的输出。它会打印测试名称、状态(成功或者失败)、测试所耗费的时间,还有测试的日志等等。

如果不使用-v参数来测试,输出很少很多,我经常使用-v参数来打开详细测试日志。例子:

$ go test -v context

$ go test -race

现在可以使用Go工具提供的-race参数进行竞争检测。它会检测并报告竞争。开发的过程中用这个命令来检测一下。

注:完整的命令是:

$ go test -race mypkg // to test the package
$ go run -race mysrc.go // to run the source file
$ go build -race mycmd // to build the command

$ go test -run

你可以在测试的时候通过-run参数来正则匹配过滤需要测试的代码。下面的命令只会运行test examples。

$ go test -run=Example

$ go test -coverprofile

当测试一个包的时候,可以输出一个测试覆盖率,然后使用命令go tool来在浏览器里面可视化。

$ go test -coverprofile=c.out && go tool cover -html=c.out

上面的命令将会创建一个测试覆盖率文件在浏览器打开结果。可视化的结果看起来是下面的结果:

IMG-THUMBNAIL

注:测试fmt包

go test -coverprofile=c.out fmt

$ go test -exec

一般很少有人知道Go的这个功能,你可以通过-exec插入另一个程序。这个参数允许通过Go工具完成一些外部工作。

一个常见的需求场景是你需要在一些宿主机上面执行一些测试。我们可以通过-exec命令调用adb命令来把二进制文件导入安卓设备并且可以收集到结果信息。参考这个来在安卓设备上面执行。
$ go get -u

如果你通过go get命令获取Go包,而这个包已经存在于本地的GOPATH,那么这个命令并不会帮你更新包。-u可以强制更新到最新版。

如果你是一个库作者,你最好在你的安装说明上加上-u参数,例如,golint是这么做的:

$ go get -u github.com/golang/lint/golint

$ go get -d

如果你想clone一个代码仓库到GOPATH里面,跳过编译和安装环节,使用-d参数。这样它只会下载包并且在编译和安装之前停止。

当需要clone虚拟网址代码仓库的时候,我经常使用这个命令来代替git clone,因为这样可以把Go代码自动放入合适的目录下面。例如:

$ go get -d golang.org/x/oauth2/...

这样可以克隆到$GOPATH/src/golang.org/x/ouath2目录下面。假设golang.org/x/oauth2是一个虚拟网址,通过go get获取这个代码仓库要比找出仓库的真实地址(go.googlesource.com/oauth2)更简单。
$ go get -t

如果你的测试包的有附加的依赖包,-t可以一并下载测试包的依赖包。如果没有加这个参数,go get只会下载非测试包的依赖包。
$ go list -f

这个命令可以列出来Go的所有包,并且可以指定格式。这个写脚本的时候很有用。

下面这个命令将会打印所有依赖的runtime包

go list -f ‘’ runtime [runtime/internal/atomic runtime/internal/sys unsafe]

`