import process; import process.popen; import console; import zlib.httpFile; import win.path; import fsys; import string.cmdline; assert(!golang,"请先引入 golang 库,然后再引入 golang 命名空间下的其他库。"); class golang{ ctor(workDir,root){ var customVersion; var version = _WINXP ? "1.11" : "1.20.10"/*最后一个支持 Win7 版本*/ if(type(root)=="string" && ..string.match(root,"^\d+\.\d+\.\d+$")){ version = root; customVersion = root; root = null; } if(!root){ var appDataGoRoot = ..io.appData("aardio/std/golang/" + version + "/go"); var install = function(){ var ok,err = ..zlib.httpFile.download("https://mirrors.aliyun.com/golang/go"+version +( _WIN_64 ? ".windows-amd64.zip" : ".windows-386.zip" ) ,"正在下载 Go 编译器" ,..io.appData("aardio/std/golang/" + version) ,..io.appData("aardio/std/golang/" + version) ) if( !ok ){ ..console.log(err); ..fsys.delete(appDataGoRoot); } return ok; } if( ..fsys.searchFile("bin\go.exe",appDataGoRoot) ){ root = appDataGoRoot; } elseif(customVersion) { return; //明确限定了 Go 版本 if( install() && ..fsys.searchFile("bin\go.exe",appDataGoRoot) ){ root = appDataGoRoot; } else { error("未安装Go编译器",2); } } //没找到,继续找 if( ! ..io.exist(root) ){ if( ..io.exist("~\lib\golang\.res\go\bin\go.exe") ){ root = ..io.fullpath("~\lib\golang\.res\go") } } if( ! ..io.exist(root) ){ root = ..string.load( ..io.appData("aardio/std/golang/latest") ); } if( ! ..io.exist(root) ){ root = ..string.getenv("GOROOT"); if( ! ..io.exist(root) ){ this.path = ..win.path.search("go.exe"); if( this.path ){ root = ..fsys.getParentDir(this.path) root = root ? ..fsys.getParentDir(root) if(!root) this.path = null; } } } if( ! ..io.exist(root) ){ if(..io.exist("C:\Go\bin\go.exe")){ root = "C:\Go" } } if( ! ..io.exist(root) ){ install(); if( ..fsys.searchFile("bin\go.exe",appDataGoRoot) ){ root = appDataGoRoot; } } } this.root = ..io.fullpath(root); ..io.appData("aardio/std/golang/latest",this.root) ..string.setenv("GOROOT",this.root); ..string.setenv("GO111MODULE","auto"); ..string.setenv("GOPROXY","https://goproxy.io,direct"); ..string.setenv("CGO_ENABLED","1"); ..string.setenv("GOARCH","386"); ..string.setenv("GOOS","windows"); ..string.save(..io.joinpath(this.root,"/src/aardio/aardio.go"),$"~\lib\golang\.res\aardio\aardio.go"); ..string.save(..io.joinpath(this.root,"/src/aardio/jsonrpc/jsonrpc.go"),$"~\lib\golang\.res\aardio\jsonrpc\jsonrpc.go"); ..string.save(..io.joinpath(this.root,"/src/aardio/jsonrpc/tcp/tcp.go"),$"~\lib\golang\.res\aardio\jsonrpc\tcp\tcp.go"); ..win.path.add( ..io.joinpath(this.root,"\bin") ); this.path = ..io.joinpath(this.root,"\bin\go.exe") this.environment = {}; this.workDir = workDir : ..io.fullpath("/"); ..io.createDir(this.workDir) this.command = function(...){ ..console.open() var args = ..string.cmdline.arguments(...); var prcs = ..process( this.path,args ,{workDir=this.workDir;environment=this.environment} ); prcs.wait(); prcs.free(); }; this.goPath = function(){ var path = ..string.getenv("GOPATH"); if(!path){ path = ..io.getSpecial(0x28/*_CSIDL_PROFILE*/,"go") } return path } this.binPath = function(filename){ var path = this.goPath(); path = ..io.joinpath(path,"bin","windows_"+..string.getenv("GOARCH") ); path = ..io.joinpath(path,filename); return path; } this.command("version"); }; runCode = function(path,code,...){ ..string.save(path,code) return this.command("run",..io.localpath(path) : path,...); }; run = function(path,...){ return this.command("run",..io.localpath(path) : path,...); }; build = function(path,...){ path = ..io.localpath(path) : path; if( ..string.endsWith(path,".go",true)){ if(this.main) ..string.save(path, this.main); return this.command("build","-o",..string.left(path,-4) + ".exe",path,...); } return this.command("build",path,...); }; buildStrip = function(path,...){ path = ..io.localpath(path) : path; if( ..string.endsWith(path,".go",true)){ if(this.main) ..string.save(path, this.main); return this.command("build","-o",..string.left(path,-4) + ".exe","-ldflags","-s -w",path,...); } return this.command("build","-o",outpath ,"-ldflags","-s -w" ,"-ldflags","-X buildTime="+tonumber(..time()) ,path,...); }; build64 = function(...){ ..string.setenv("GOARCH","amd64"); var r,e = this.build(...); ..string.setenv("GOARCH","386"); return r,e; }; buildStrip64 = function(...){ ..string.setenv("GOARCH","amd64"); var r,e = this.buildStrip(...); ..string.setenv("GOARCH","386"); return r,e; }; addPath = function(path){ ..win.path.add(path); }; setGoProxy = function(proxy){ ..string.setenv("GOPROXY",proxy); ..table.mixin(this.environment,{ GOPROXY=proxy; }); }; setHttpProxy = function(proxy,git){ ..table.mixin(this.environment,{ http_proxy=proxy; https_proxy=proxy; }); if(git){ import process.git; process.git(this.workDir).setHttpProxy(proxy) } }; //@Deprecated setProxy = function(...){ return this.setHttpProxy(...); }; enableCgo = function(){ import process.gcc; process.gcc.check(); ..string.setenv("CGO_ENABLED","1"); }; enableCgo64 = function(){ import process.gcc; process.gcc.check64(); ..string.setenv("CGO_ENABLED","1"); }; buildShared = function(path,outpath,...){ this.enableCgo(); if(this.main) ..string.save(path, this.main); if(!outpath){ if( ..string.endsWith(path,".go",true)){ outpath = ..string.left(path,-4) + ".dll" } else { outpath = path + ".dll" } } return this.command("build","-buildmode=c-shared" ,"-ldflags","-s -w" //-s 去掉符号表,-w是去掉DWARF调试信息,可减小生成文件体积 ,"-o",..io.localpath(outpath) : outpath,..io.localpath(path) : path,...); }; buildArchive = function(path,outpath,...){ if(this.main) ..string.save(path, this.main); if(!outpath){ if( ..string.endsWith(path,".go",true)){ outpath = ..string.left(path,-4) + ".dll" } else { outpath = path + ".dll" } } return this.command("build","-buildmode=c-archive","-ldflags","-s -w","-o",..io.localpath(outpath) : outpath,..io.localpath(path) : path,...); }; get = function(paths,...){ import process.git; for item in ..string.lines(paths) { if(#item)this.command("get",item,...); } }; mod = function(...){ var args = ..string.cmdline.arguments(...); ..table.unshift(args,"mod"); this.command(args); }; install = function(path,...){ return this.command("install",..io.localpath(path) : path,...); }; generate = function(...){ var args = ..string.cmdline.arguments(...); ..table.unshift(args,"generate"); this.command(args); }; cmd = function(cmd,...){ ..console.open() var args; if(...===null){ args = ..string.cmdline.arguments(cmd); cmd = ..table.shift(args,1); if(!..string.endsWith(cmd,".exe",true)){ cmd = cmd + ".exe"; } } else { args = ..string.cmdline.arguments(...); } var prcs,err = ..process.popen( this.binPath(cmd) , ..string.args.join(args) ,{workDir=this.workDir;environment=this.environment} ); if(prcs){ prcs.codepage = 65001; prcs.killOnExit(); } return prcs,err; }; yaegiExtract = function(...){ var path = this.binPath("yaegi.exe"); if( !..io.exist(path) ){ this.install("github.com/traefik/yaegi/cmd/yaegi@latest") } ..console.showLoading("正在提取符号表"); var args = ..string.cmdline.arguments(...); ..table.unshift(args,"extract"); var prcs = this.cmd("yaegi.exe",args); if(prcs){ prcs.logResponse(); } }; version = function(){ return this.command("version"); }; env = function(){ return this.command("env"); }; updatePeTime = function(path,value){ var file = ..io.file(path,"r+b"); if(!file) return null,"打开文件失败!" if( (file.read(2) != '\x4d\x5a' ) || (file.seek("set",60)!=60 ) ){ file.close(); return null,"文件不是 PE 格式" } var r,e; var headerAddress = file.read({INT lfanew})[["lfanew"]]; if( headerAddress ? file.seek("set",headerAddress+8) ){ r,e = file.write({ INT timeDateStamp = tonumber(..time(value).utc()) } ); } file.close(); return r,e; } } import golang.string; /**intellisense() golang = 调用 Go 编译器。\n调用 Go 编译后的执行程序并不需要此扩展库。\n\n此扩展库可自动下载配置 Go 编译器 golang( = 创建 Go 编译器调用程序 golang(.("Go项目目录","Go安装目录") = 创建 Go 编译器调用程序,所有参数可选。\nGo 项目目录默认为 aardio 应用程序根目录的 "/" 。\n如果不指定 Go 安装目录,\n刚搜索可能的 Go 安装目录或环境变量 GOROOT ,找不到则安装默认版本 Go 编译器。\n具体规则请查看 golang 支持库源码。 golang(.("Go项目目录","1.22.3") = 创建 Go 编译器调用程序。\n参数 @1 如果为 null ,则默认设为应用程序根目录 "/" 。\n参数 @2 限定 Go 编译器的版本号,必须是 "数值.数值.数值" 格式。\n如果 aardio 未安装指定版本 Go 编译器则先下载安装。\n不使用系统安装的其他 Go 编译器 golang() = !golang. !golang.command(__) = 执行 go 命令。\n可指定一个或多个命令行参数。\n可指定字符串或参数表等任何 string.args.join 支持的参数。 !golang.run(__) = 执行 go run 命令,参数中指定要编译运行的代码 !golang.runCode(.("代码路径","Go代码") = 保存 Go 代码并运行文件 !golang.build(__) = 执行 go build 命令生成 EXE 文件。\n参数 @1 指定要编译的代码文件,可增加多个参数。\n默认按第一个参数指定的 *.go 文件路径输出同名 *.exe 文件 !golang.buildShared( = 执行 go build 命令生成DLL动态库。\n如果未找 gcc 到则自动安装 Mingw-w64 !golang.enableCgo() = 启用 32 位 cgo ,依赖 gcc 。\n如果未找到 32 位 gcc 到则自动安装 Mingw-w64 32 位编译环境。 !golang.enableCgo64() = 启用 64 位 cgo ,依赖 gcc 。\n如果未找到 64 位 gcc 到则自动安装 Mingw-w64 64 位编译环境。 !golang.buildShared(.("源文件文件路径","输出文件路径") = 执行 go build 命令生成 DLL 动态库,\n参数@1必须指定要编译的代码文件,可选指定输出 DLL 文件路径,\n可增加多个参数\n\n编译 DLL 需要依赖 GCC,请先调用 process.gcc.check\n\n注意每个Go导出函数前面都要加上 //export 要导出的函数名 !golang.buildArchive(.("源文件文件路径","输出文件路径") = 执行 go build -buildmode=c-archive 命令生成 C 静态库 !golang.buildStrip(__) = 执行 go build 命令生成体积较小的 EXE 文件。\n参数@1指定要编译的代码文件,可增加多个参数。\n默认按第一个参数指定的 *.go 文件路径输出同名 *.exe 文件。\n默认添加编译参数 -ldflags "-s -w" 以移除调试信息 !golang.build64(__) = 执行 go build 命令生成 64 位 EXE 文件。\n注意 aardio 里调用 64 位或 32 位 EXE 没有区别,都支持。\n参数@1指定要编译的代码文件,可增加多个参数。\n默认按第一个参数指定的 *.go 文件路径输出同名 *.exe 文件 !golang.buildStrip64(__) = 执行 go build 命令生成体积较小的 64 位 EXE 文件。\n注意 aardio 里调用 64 位或 32 位 EXE 没有区别,都支持。\n参数@1指定要编译的代码文件,可增加多个参数。\n默认按第一个参数指定的 *.go 文件路径输出同名 *.exe 文件。\n默认添加编译参数 -ldflags "-s -w" 以移除调试信息 !golang.install(__) = 执行 go install 命令,参数中指定要编译安装的代码。 !golang.addPath(__) = 添加路径到 PATH 环境变量 !golang.setGoProxy("__") = 设置 GOPROXY 环境变量指定的 Go 镜像服务器 !golang.setHttpProxy("代理服务器地址",是否设为GIT代理) = 设置代理服务器,\n参数 @1 示例: "socks5://127.0.0.1:10801",\n设为 null 清除代理\n\n参数 @2 可省略,\n设为 true 则同时设置 GIT 的 HTTP / HTTPS 代理 !golang.mod("init __/*模块名*/")= 执行 go mod 命令,\n参数可以是一个字符串,以空格分隔多个参数。\n也可传入多个参数,参数用法请参考 process 启动参数说明 !golang.get(__) = 执行 go get 命令,安装远程代码包,\n可在参数 @1 中使用多行字符串指定多个远程包,\n需要先安装 GIT ,如果未安装就自动安装\n\n建议先调用 setProxy 函数设置代理。\n并调用 process.git 的 setHttpProxy 函数设置代理 !golang.env() = 执行 go env 命令,显示环境变量 !golang.version() = 执行 go version 命令,显示版本 !golang.main = @.main = /**********\npackage main\n\nimport "C" \nimport (\n "aardio"\n "fmt"\n)\n\n__/*如果在这里指定了 Go 码,\n则调用 build 前缀的函数将自动覆盖参数 @1 指定的 *.go 文件。*/\nfunc init() {}\n\nfunc main() {} \n**********/ !golang.goPath() = 返回 GOPATH 路径。 !golang.binPath(.(filename) = 返回 go install 安装的执行文件路径 !golang.cmd(.(exe,->->->) = 执行 go install 安装的执行文件命令。\n参数 @1 指定 EXE 文件名。\n后面可以指定一个或多个参数,\n可指定字符串或参数表等任何 string.args.join 支持的参数。\n\n此函数返回 process.popen 对象。 !golang.cmd() = !process_popen. !golang.generate(__) = 执行 generate 命令,可添加一个或多个命令行参数。\n\n可指定字符串或参数表等任何 string.args.join 支持的参数。 !golang.yaegiExtract(.(->->->) = 执行 yaegi extract 命令导出模块符号表。\n参数可以是一个或多个命令行参数,\n这些参数可以是 string.args.join 支持的字符串或表参数。\n\nyaegi 用法可参考 golang.excelize 扩展库的 DLL 源码。\n不要看网上过时的文章,这个命令没有重定向,也不需要指定输出文件名。\n直接到应用程序根目录找默认输出的文件。 !golang.updatePeTime(.(path,tm) = 设置 PE 时间戳。\n参数 @path 指定文件路径。\n参数 @tm 可以是数值或 time 对象。\n省略 @tm 参数则设为当前时间。\n\n- Go 编译的执行文件默认不会更新 PE 时间戳。\n- 使用 raw.pefile.getTime 函数读取 PE 时间戳。 end intellisense**/ /**details(必读) 调用 Go 写的 DLL 请注意: 1、 加载 Go 写的 DLL 然后迅速(几秒以内)退出,Go 程序可能会崩溃。 这不是因为你写的代码有任何问题,而是 Go 需要额外启动运行时,无法应付这种快速退出的情况。 这时在后面加一句 thread.delay(2000) 就可以解决。 实际上除了写测试代码,一般也不会打开一个程序就在几秒内退出。 所以稍加注意一下,避免这个问题并不难。 只有 Go 写的 DLL 有这个问题,其他语言写的 DLL 没这种问题。 2、在同一个进程内, Go 写的同一个 DLL 应当只加载一次。 当然在 DLL 没有卸载前,反复调用 raw.loadDll() 只是增加引用计数,不会重复加载 DLL。 如果多线程内存加载同一个 Go 写的 DLL 就会加载多个不同的副本。 这时候务必在 raw.loadDll("go.dll","共享名称") 的第 2 个参数指定共享名称,以避免重复加载。 3、要注意在 aardio 中 DLL 不应当作为线程参数传递,实际上也没必要这样做。 只要用 raw.loadDll() 加载同名 DLL (或加载相同共享名称的内存 DLL) 是不会重复加载的。 end details**/