github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/build/ci.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:32</date> 10 //</624450066521788416> 11 12 13 //+不建 14 15 /* 16 从连续集成脚本调用CI命令。 17 18 用法:go run build/ci.go<command>><command flags/arguments> 19 20 可用命令包括: 21 22 安装[-architecture][-cc compiler][packages…]——构建包和可执行文件 23 测试[-coverage][packages…]——运行测试 24 lint—运行某些预先选定的linter 25 archive[-architecture][-type zip tar][-signer key envvar][-upload dest]--存档生成的工件 26 importkeys—从env导入签名密钥 27 debsrc[-signer key id][-upload dest]--创建Debian源包 28 nsis--创建Windows nsis安装程序 29 aar[-local][-sign key id][-deploy repo][-upload dest]--创建Android存档 30 xcode[-local][-sign key id][-deploy repo][-upload dest]--创建iOS xcode框架 31 xgo[-alltools][options]--根据选项交叉构建 32 purge[-store blobstore][-days threshold]--从blobstore中清除旧存档 33 34 对于所有命令,-n防止执行外部程序(干运行模式)。 35 36 **/ 37 38 package main 39 40 import ( 41 "bufio" 42 "bytes" 43 "encoding/base64" 44 "flag" 45 "fmt" 46 "go/parser" 47 "go/token" 48 "io/ioutil" 49 "log" 50 "os" 51 "os/exec" 52 "path/filepath" 53 "regexp" 54 "runtime" 55 "strings" 56 "time" 57 58 "github.com/ethereum/go-ethereum/internal/build" 59 "github.com/ethereum/go-ethereum/params" 60 sv "github.com/ethereum/go-ethereum/swarm/version" 61 ) 62 63 var ( 64 //最后出现在geth*.zip存档中的文件。 65 gethArchiveFiles = []string{ 66 "COPYING", 67 executablePath("geth"), 68 } 69 70 //最后出现在geth alltools*.zip存档中的文件。 71 allToolsArchiveFiles = []string{ 72 "COPYING", 73 executablePath("abigen"), 74 executablePath("bootnode"), 75 executablePath("evm"), 76 executablePath("geth"), 77 executablePath("puppeth"), 78 executablePath("rlpdump"), 79 executablePath("wnode"), 80 } 81 82 //最后出现在swarm.zip文件中的文件。 83 swarmArchiveFiles = []string{ 84 "COPYING", 85 executablePath("swarm"), 86 } 87 88 //为此处列出的所有可执行文件创建Debian包。 89 debExecutables = []debExecutable{ 90 { 91 BinaryName: "abigen", 92 Description: "Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages.", 93 }, 94 { 95 BinaryName: "bootnode", 96 Description: "Ethereum bootnode.", 97 }, 98 { 99 BinaryName: "evm", 100 Description: "Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode.", 101 }, 102 { 103 BinaryName: "geth", 104 Description: "Ethereum CLI client.", 105 }, 106 { 107 BinaryName: "puppeth", 108 Description: "Ethereum private network manager.", 109 }, 110 { 111 BinaryName: "rlpdump", 112 Description: "Developer utility tool that prints RLP structures.", 113 }, 114 { 115 BinaryName: "wnode", 116 Description: "Ethereum Whisper diagnostic tool", 117 }, 118 } 119 120 //为此处列出的所有可执行文件创建Debian包。 121 debSwarmExecutables = []debExecutable{ 122 { 123 BinaryName: "swarm", 124 PackageName: "ethereum-swarm", 125 Description: "Ethereum Swarm daemon and tools", 126 }, 127 } 128 129 debEthereum = debPackage{ 130 Name: "ethereum", 131 Version: params.Version, 132 Executables: debExecutables, 133 } 134 135 debSwarm = debPackage{ 136 Name: "ethereum-swarm", 137 Version: sv.Version, 138 Executables: debSwarmExecutables, 139 } 140 141 //Debian元软件包,用于构建和推送到Ubuntu PPA 142 debPackages = []debPackage{ 143 debSwarm, 144 debEthereum, 145 } 146 147 //要由xgo命令交叉编译的包 148 allCrossCompiledArchiveFiles = append(allToolsArchiveFiles, swarmArchiveFiles...) 149 150 //为其创建包的发行版。 151 //注:Vivid不支持,因为它没有Golang-1.6包。 152 //注意:不支持wily,因为它在lanchpad上被正式否决。 153 //注意:不支持Yakkety,因为它在Lanchpad上被正式否决。 154 //注意:不支持Zesty,因为它在LanchPad上被正式否决。 155 //注意:不支持Artful,因为它在Lanchpad上被正式否决。 156 debDistros = []string{"trusty", "xenial", "bionic", "cosmic"} 157 ) 158 159 var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin")) 160 161 func executablePath(name string) string { 162 if runtime.GOOS == "windows" { 163 name += ".exe" 164 } 165 return filepath.Join(GOBIN, name) 166 } 167 168 func main() { 169 log.SetFlags(log.Lshortfile) 170 171 if _, err := os.Stat(filepath.Join("build", "ci.go")); os.IsNotExist(err) { 172 log.Fatal("this script must be run from the root of the repository") 173 } 174 if len(os.Args) < 2 { 175 log.Fatal("need subcommand as first argument") 176 } 177 switch os.Args[1] { 178 case "install": 179 doInstall(os.Args[2:]) 180 case "test": 181 doTest(os.Args[2:]) 182 case "lint": 183 doLint(os.Args[2:]) 184 case "archive": 185 doArchive(os.Args[2:]) 186 case "debsrc": 187 doDebianSource(os.Args[2:]) 188 case "nsis": 189 doWindowsInstaller(os.Args[2:]) 190 case "aar": 191 doAndroidArchive(os.Args[2:]) 192 case "xcode": 193 doXCodeFramework(os.Args[2:]) 194 case "xgo": 195 doXgo(os.Args[2:]) 196 case "purge": 197 doPurge(os.Args[2:]) 198 default: 199 log.Fatal("unknown command ", os.Args[1]) 200 } 201 } 202 203 //编译 204 205 func doInstall(cmdline []string) { 206 var ( 207 arch = flag.String("arch", "", "Architecture to cross build for") 208 cc = flag.String("cc", "", "C compiler to cross build with") 209 ) 210 flag.CommandLine.Parse(cmdline) 211 env := build.Env() 212 213 //检查Go版本。人们经常公开关于汇编的问题 214 //落伍失败。这样可以省去他们的麻烦。 215 if !strings.Contains(runtime.Version(), "devel") { 216 //由于无法进行文本比较,因此请计算次要版本号(1.10<1.9) 217 var minor int 218 fmt.Sscanf(strings.TrimPrefix(runtime.Version(), "go1."), "%d", &minor) 219 220 if minor < 9 { 221 log.Println("You have Go version", runtime.Version()) 222 log.Println("go-ethereum requires at least Go version 1.9 and cannot") 223 log.Println("be compiled with an earlier version. Please upgrade your Go installation.") 224 os.Exit(1) 225 } 226 } 227 //编译作为参数提供的包,如果没有参数,则编译所有内容。 228 packages := []string{"./..."} 229 if flag.NArg() > 0 { 230 packages = flag.Args() 231 } 232 packages = build.ExpandPackagesNoVendor(packages) 233 234 if *arch == "" || *arch == runtime.GOARCH { 235 goinstall := goTool("install", buildFlags(env)...) 236 goinstall.Args = append(goinstall.Args, "-v") 237 goinstall.Args = append(goinstall.Args, packages...) 238 build.MustRun(goinstall) 239 return 240 } 241 //如果我们交叉编译到armv5 armv6或armv7,请清除以前的任何版本。 242 if *arch == "arm" { 243 os.RemoveAll(filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_arm")) 244 for _, path := range filepath.SplitList(build.GOPATH()) { 245 os.RemoveAll(filepath.Join(path, "pkg", runtime.GOOS+"_arm")) 246 } 247 } 248 //看来我们是在交叉编译,围绕禁地工作 249 goinstall := goToolArch(*arch, *cc, "install", buildFlags(env)...) 250 goinstall.Args = append(goinstall.Args, "-v") 251 goinstall.Args = append(goinstall.Args, []string{"-buildmode", "archive"}...) 252 goinstall.Args = append(goinstall.Args, packages...) 253 build.MustRun(goinstall) 254 255 if cmds, err := ioutil.ReadDir("cmd"); err == nil { 256 for _, cmd := range cmds { 257 pkgs, err := parser.ParseDir(token.NewFileSet(), filepath.Join(".", "cmd", cmd.Name()), nil, parser.PackageClauseOnly) 258 if err != nil { 259 log.Fatal(err) 260 } 261 for name := range pkgs { 262 if name == "main" { 263 gobuild := goToolArch(*arch, *cc, "build", buildFlags(env)...) 264 gobuild.Args = append(gobuild.Args, "-v") 265 gobuild.Args = append(gobuild.Args, []string{"-o", executablePath(cmd.Name())}...) 266 gobuild.Args = append(gobuild.Args, "."+string(filepath.Separator)+filepath.Join("cmd", cmd.Name())) 267 build.MustRun(gobuild) 268 break 269 } 270 } 271 } 272 } 273 } 274 275 func buildFlags(env build.Environment) (flags []string) { 276 var ld []string 277 if env.Commit != "" { 278 ld = append(ld, "-X", "main.gitCommit="+env.Commit) 279 } 280 if runtime.GOOS == "darwin" { 281 ld = append(ld, "-s") 282 } 283 284 if len(ld) > 0 { 285 flags = append(flags, "-ldflags", strings.Join(ld, " ")) 286 } 287 return flags 288 } 289 290 func goTool(subcmd string, args ...string) *exec.Cmd { 291 return goToolArch(runtime.GOARCH, os.Getenv("CC"), subcmd, args...) 292 } 293 294 func goToolArch(arch string, cc string, subcmd string, args ...string) *exec.Cmd { 295 cmd := build.GoTool(subcmd, args...) 296 cmd.Env = []string{"GOPATH=" + build.GOPATH()} 297 if arch == "" || arch == runtime.GOARCH { 298 cmd.Env = append(cmd.Env, "GOBIN="+GOBIN) 299 } else { 300 cmd.Env = append(cmd.Env, "CGO_ENABLED=1") 301 cmd.Env = append(cmd.Env, "GOARCH="+arch) 302 } 303 if cc != "" { 304 cmd.Env = append(cmd.Env, "CC="+cc) 305 } 306 for _, e := range os.Environ() { 307 if strings.HasPrefix(e, "GOPATH=") || strings.HasPrefix(e, "GOBIN=") { 308 continue 309 } 310 cmd.Env = append(cmd.Env, e) 311 } 312 return cmd 313 } 314 315 //运行测试 316 // 317 //“测试”还包括静态分析工具,如vet。 318 319 func doTest(cmdline []string) { 320 coverage := flag.Bool("coverage", false, "Whether to record code coverage") 321 flag.CommandLine.Parse(cmdline) 322 env := build.Env() 323 324 packages := []string{"./..."} 325 if len(flag.CommandLine.Args()) > 0 { 326 packages = flag.CommandLine.Args() 327 } 328 packages = build.ExpandPackagesNoVendor(packages) 329 330 //运行实际测试。 331 //一次测试一个包。CI建设者很慢 332 //一些测试在负载下会超时。 333 gotest := goTool("test", buildFlags(env)...) 334 gotest.Args = append(gotest.Args, "-p", "1", "-timeout", "5m") 335 if *coverage { 336 gotest.Args = append(gotest.Args, "-covermode=atomic", "-cover") 337 } 338 339 gotest.Args = append(gotest.Args, packages...) 340 build.MustRun(gotest) 341 } 342 343 //在请求的包上运行gometalinter 344 func doLint(cmdline []string) { 345 flag.CommandLine.Parse(cmdline) 346 347 packages := []string{"./..."} 348 if len(flag.CommandLine.Args()) > 0 { 349 packages = flag.CommandLine.Args() 350 } 351 //获取metallinter并安装所有支持的过梁 352 build.MustRun(goTool("get", "gopkg.in/alecthomas/gometalinter.v2")) 353 build.MustRunCommand(filepath.Join(GOBIN, "gometalinter.v2"), "--install") 354 355 //一起运行快速过梁 356 configs := []string{ 357 "--vendor", 358 "--tests", 359 "--deadline=2m", 360 "--disable-all", 361 "--enable=goimports", 362 "--enable=varcheck", 363 "--enable=vet", 364 "--enable=gofmt", 365 "--enable=misspell", 366 "--enable=goconst", 367 "--min-occurrences=6", //为GOCOST 368 } 369 build.MustRunCommand(filepath.Join(GOBIN, "gometalinter.v2"), append(configs, packages...)...) 370 371 //一个接一个地运行缓慢的过梁 372 for _, linter := range []string{"unconvert", "gosimple"} { 373 configs = []string{"--vendor", "--tests", "--deadline=10m", "--disable-all", "--enable=" + linter} 374 build.MustRunCommand(filepath.Join(GOBIN, "gometalinter.v2"), append(configs, packages...)...) 375 } 376 } 377 378 //释放包装 379 func doArchive(cmdline []string) { 380 var ( 381 arch = flag.String("arch", runtime.GOARCH, "Architecture cross packaging") 382 atype = flag.String("type", "zip", "Type of archive to write (zip|tar)") 383 signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. LINUX_SIGNING_KEY)`) 384 upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`) 385 ext string 386 ) 387 flag.CommandLine.Parse(cmdline) 388 switch *atype { 389 case "zip": 390 ext = ".zip" 391 case "tar": 392 ext = ".tar.gz" 393 default: 394 log.Fatal("unknown archive type: ", atype) 395 } 396 397 var ( 398 env = build.Env() 399 400 basegeth = archiveBasename(*arch, params.ArchiveVersion(env.Commit)) 401 geth = "geth-" + basegeth + ext 402 alltools = "geth-alltools-" + basegeth + ext 403 404 baseswarm = archiveBasename(*arch, sv.ArchiveVersion(env.Commit)) 405 swarm = "swarm-" + baseswarm + ext 406 ) 407 maybeSkipArchive(env) 408 if err := build.WriteArchive(geth, gethArchiveFiles); err != nil { 409 log.Fatal(err) 410 } 411 if err := build.WriteArchive(alltools, allToolsArchiveFiles); err != nil { 412 log.Fatal(err) 413 } 414 if err := build.WriteArchive(swarm, swarmArchiveFiles); err != nil { 415 log.Fatal(err) 416 } 417 for _, archive := range []string{geth, alltools, swarm} { 418 if err := archiveUpload(archive, *upload, *signer); err != nil { 419 log.Fatal(err) 420 } 421 } 422 } 423 424 func archiveBasename(arch string, archiveVersion string) string { 425 platform := runtime.GOOS + "-" + arch 426 if arch == "arm" { 427 platform += os.Getenv("GOARM") 428 } 429 if arch == "android" { 430 platform = "android-all" 431 } 432 if arch == "ios" { 433 platform = "ios-all" 434 } 435 return platform + "-" + archiveVersion 436 } 437 438 func archiveUpload(archive string, blobstore string, signer string) error { 439 //如果请求签名,则生成签名文件 440 if signer != "" { 441 pgpkey, err := base64.StdEncoding.DecodeString(os.Getenv(signer)) 442 if err != nil { 443 return fmt.Errorf("invalid base64 %s", signer) 444 } 445 if err := build.PGPSignFile(archive, archive+".asc", string(pgpkey)); err != nil { 446 return err 447 } 448 } 449 //如果请求上载到Azure,则可能使用其签名推送存档 450 if blobstore != "" { 451 auth := build.AzureBlobstoreConfig{ 452 Account: strings.Split(blobstore, "/")[0], 453 Token: os.Getenv("AZURE_BLOBSTORE_TOKEN"), 454 Container: strings.SplitN(blobstore, "/", 2)[1], 455 } 456 if err := build.AzureBlobstoreUpload(archive, filepath.Base(archive), auth); err != nil { 457 return err 458 } 459 if signer != "" { 460 if err := build.AzureBlobstoreUpload(archive+".asc", filepath.Base(archive+".asc"), auth); err != nil { 461 return err 462 } 463 } 464 } 465 return nil 466 } 467 468 //跳过某些生成配置的存档。 469 func maybeSkipArchive(env build.Environment) { 470 if env.IsPullRequest { 471 log.Printf("skipping because this is a PR build") 472 os.Exit(0) 473 } 474 if env.IsCronJob { 475 log.Printf("skipping because this is a cron job") 476 os.Exit(0) 477 } 478 if env.Branch != "master" && !strings.HasPrefix(env.Tag, "v1.") { 479 log.Printf("skipping because branch %q, tag %q is not on the whitelist", env.Branch, env.Tag) 480 os.Exit(0) 481 } 482 } 483 484 //Debian包装 485 func doDebianSource(cmdline []string) { 486 var ( 487 signer = flag.String("signer", "", `Signing key name, also used as package author`) 488 upload = flag.String("upload", "", `Where to upload the source package (usually "ppa:ethereum/ethereum")`) 489 workdir = flag.String("workdir", "", `Output directory for packages (uses temp dir if unset)`) 490 now = time.Now() 491 ) 492 flag.CommandLine.Parse(cmdline) 493 *workdir = makeWorkdir(*workdir) 494 env := build.Env() 495 maybeSkipArchive(env) 496 497 //导入签名密钥。 498 if b64key := os.Getenv("PPA_SIGNING_KEY"); b64key != "" { 499 key, err := base64.StdEncoding.DecodeString(b64key) 500 if err != nil { 501 log.Fatal("invalid base64 PPA_SIGNING_KEY") 502 } 503 gpg := exec.Command("gpg", "--import") 504 gpg.Stdin = bytes.NewReader(key) 505 build.MustRun(gpg) 506 } 507 508 //创建Debian包并上载它们 509 for _, pkg := range debPackages { 510 for _, distro := range debDistros { 511 meta := newDebMetadata(distro, *signer, env, now, pkg.Name, pkg.Version, pkg.Executables) 512 pkgdir := stageDebianSource(*workdir, meta) 513 debuild := exec.Command("debuild", "-S", "-sa", "-us", "-uc") 514 debuild.Dir = pkgdir 515 build.MustRun(debuild) 516 517 changes := fmt.Sprintf("%s_%s_source.changes", meta.Name(), meta.VersionString()) 518 changes = filepath.Join(*workdir, changes) 519 if *signer != "" { 520 build.MustRunCommand("debsign", changes) 521 } 522 if *upload != "" { 523 build.MustRunCommand("dput", *upload, changes) 524 } 525 } 526 } 527 } 528 529 func makeWorkdir(wdflag string) string { 530 var err error 531 if wdflag != "" { 532 err = os.MkdirAll(wdflag, 0744) 533 } else { 534 wdflag, err = ioutil.TempDir("", "geth-build-") 535 } 536 if err != nil { 537 log.Fatal(err) 538 } 539 return wdflag 540 } 541 542 func isUnstableBuild(env build.Environment) bool { 543 if env.Tag != "" { 544 return false 545 } 546 return true 547 } 548 549 type debPackage struct { 550 Name string //要生成的Debian包的名称,例如“以太坊”或“以太坊群” 551 Version string //debpackage的干净版本,例如1.8.12或0.3.0,没有任何元数据 552 Executables []debExecutable //要包含在包中的可执行文件 553 } 554 555 type debMetadata struct { 556 Env build.Environment 557 558 PackageName string 559 560 //进入正在构建的以太坊版本。注意这个 561 //不是Debian包版本。包版本 562 //由versionString构造。 563 Version string 564 565 Author string //“name<email>”,同时选择签名密钥 566 Distro, Time string 567 Executables []debExecutable 568 } 569 570 type debExecutable struct { 571 PackageName string 572 BinaryName string 573 Description string 574 } 575 576 //package返回包的名称(如果存在),或者 577 //回退到BinaryName 578 func (d debExecutable) Package() string { 579 if d.PackageName != "" { 580 return d.PackageName 581 } 582 return d.BinaryName 583 } 584 585 func newDebMetadata(distro, author string, env build.Environment, t time.Time, name string, version string, exes []debExecutable) debMetadata { 586 if author == "" { 587 //没有签名密钥,请使用默认作者。 588 author = "Ethereum Builds <fjl@ethereum.org>" 589 } 590 return debMetadata{ 591 PackageName: name, 592 Env: env, 593 Author: author, 594 Distro: distro, 595 Version: version, 596 Time: t.Format(time.RFC1123Z), 597 Executables: exes, 598 } 599 } 600 601 //name返回依赖于 602 //在所有可执行包上。 603 func (meta debMetadata) Name() string { 604 if isUnstableBuild(meta.Env) { 605 return meta.PackageName + "-unstable" 606 } 607 return meta.PackageName 608 } 609 610 //versionString返回包的Debian版本。 611 func (meta debMetadata) VersionString() string { 612 vsn := meta.Version 613 if meta.Env.Buildnum != "" { 614 vsn += "+build" + meta.Env.Buildnum 615 } 616 if meta.Distro != "" { 617 vsn += "+" + meta.Distro 618 } 619 return vsn 620 } 621 622 //exelist返回所有可执行包的列表。 623 func (meta debMetadata) ExeList() string { 624 names := make([]string, len(meta.Executables)) 625 for i, e := range meta.Executables { 626 names[i] = meta.ExeName(e) 627 } 628 return strings.Join(names, ", ") 629 } 630 631 //exename返回可执行包的包名称。 632 func (meta debMetadata) ExeName(exe debExecutable) string { 633 if isUnstableBuild(meta.Env) { 634 return exe.Package() + "-unstable" 635 } 636 return exe.Package() 637 } 638 639 //execonflicts返回conflicts字段的内容 640 //对于可执行包。 641 func (meta debMetadata) ExeConflicts(exe debExecutable) string { 642 if isUnstableBuild(meta.Env) { 643 //设置冲突列表,以便*-不稳定的包 644 //不能与常规版本一起安装。 645 // 646 //https://www.debian.org/doc/debian-policy/ch-relationships.html网站 647 //对冲突非常明确:并说应该 648 //首选,冲突文件应通过 649 //交替。我们最终可能会这样做,但使用冲突是 650 //现在更容易了。 651 return "ethereum, " + exe.Package() 652 } 653 return "" 654 } 655 656 func stageDebianSource(tmpdir string, meta debMetadata) (pkgdir string) { 657 pkg := meta.Name() + "-" + meta.VersionString() 658 pkgdir = filepath.Join(tmpdir, pkg) 659 if err := os.Mkdir(pkgdir, 0755); err != nil { 660 log.Fatal(err) 661 } 662 663 //复制源代码。 664 build.MustRunCommand("git", "checkout-index", "-a", "--prefix", pkgdir+string(filepath.Separator)) 665 666 //将Debian构建文件放置到位。 667 debian := filepath.Join(pkgdir, "debian") 668 build.Render("build/deb/"+meta.PackageName+"/deb.rules", filepath.Join(debian, "rules"), 0755, meta) 669 build.Render("build/deb/"+meta.PackageName+"/deb.changelog", filepath.Join(debian, "changelog"), 0644, meta) 670 build.Render("build/deb/"+meta.PackageName+"/deb.control", filepath.Join(debian, "control"), 0644, meta) 671 build.Render("build/deb/"+meta.PackageName+"/deb.copyright", filepath.Join(debian, "copyright"), 0644, meta) 672 build.RenderString("8\n", filepath.Join(debian, "compat"), 0644, meta) 673 build.RenderString("3.0 (native)\n", filepath.Join(debian, "source/format"), 0644, meta) 674 for _, exe := range meta.Executables { 675 install := filepath.Join(debian, meta.ExeName(exe)+".install") 676 docs := filepath.Join(debian, meta.ExeName(exe)+".docs") 677 build.Render("build/deb/"+meta.PackageName+"/deb.install", install, 0644, exe) 678 build.Render("build/deb/"+meta.PackageName+"/deb.docs", docs, 0644, exe) 679 } 680 681 return pkgdir 682 } 683 684 //Windows安装程序 685 func doWindowsInstaller(cmdline []string) { 686 //分析标志并在prs上生成跳过安装程序 687 var ( 688 arch = flag.String("arch", runtime.GOARCH, "Architecture for cross build packaging") 689 signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. WINDOWS_SIGNING_KEY)`) 690 upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`) 691 workdir = flag.String("workdir", "", `Output directory for packages (uses temp dir if unset)`) 692 ) 693 flag.CommandLine.Parse(cmdline) 694 *workdir = makeWorkdir(*workdir) 695 env := build.Env() 696 maybeSkipArchive(env) 697 698 //安装程序中包含的聚合二进制文件 699 var ( 700 devTools []string 701 allTools []string 702 gethTool string 703 ) 704 for _, file := range allToolsArchiveFiles { 705 if file == "COPYING" { //许可证,稍后复制 706 continue 707 } 708 allTools = append(allTools, filepath.Base(file)) 709 if filepath.Base(file) == "geth.exe" { 710 gethTool = file 711 } else { 712 devTools = append(devTools, file) 713 } 714 } 715 716 //呈现NSI脚本:安装程序NSI包含两个安装程序部分, 717 //第一部分包含geth二进制文件,第二部分包含dev工具。 718 templateData := map[string]interface{}{ 719 "License": "COPYING", 720 "Geth": gethTool, 721 "DevTools": devTools, 722 } 723 build.Render("build/nsis.geth.nsi", filepath.Join(*workdir, "geth.nsi"), 0644, nil) 724 build.Render("build/nsis.install.nsh", filepath.Join(*workdir, "install.nsh"), 0644, templateData) 725 build.Render("build/nsis.uninstall.nsh", filepath.Join(*workdir, "uninstall.nsh"), 0644, allTools) 726 build.Render("build/nsis.pathupdate.nsh", filepath.Join(*workdir, "PathUpdate.nsh"), 0644, nil) 727 build.Render("build/nsis.envvarupdate.nsh", filepath.Join(*workdir, "EnvVarUpdate.nsh"), 0644, nil) 728 build.CopyFile(filepath.Join(*workdir, "SimpleFC.dll"), "build/nsis.simplefc.dll", 0755) 729 build.CopyFile(filepath.Join(*workdir, "COPYING"), "COPYING", 0755) 730 731 //构建安装程序。这假设所有需要的文件以前 732 //构建(不要混合构建和打包以保持交叉编译的复杂性 733 //最低限度)。 734 version := strings.Split(params.Version, ".") 735 if env.Commit != "" { 736 version[2] += "-" + env.Commit[:8] 737 } 738 installer, _ := filepath.Abs("geth-" + archiveBasename(*arch, params.ArchiveVersion(env.Commit)) + ".exe") 739 build.MustRunCommand("makensis.exe", 740 "/DOUTPUTFILE="+installer, 741 "/DMAJORVERSION="+version[0], 742 "/DMINORVERSION="+version[1], 743 "/DBUILDVERSION="+version[2], 744 "/DARCH="+*arch, 745 filepath.Join(*workdir, "geth.nsi"), 746 ) 747 748 //签署并发布安装程序。 749 if err := archiveUpload(installer, *upload, *signer); err != nil { 750 log.Fatal(err) 751 } 752 } 753 754 //Android档案 755 756 func doAndroidArchive(cmdline []string) { 757 var ( 758 local = flag.Bool("local", false, `Flag whether we're only doing a local build (skip Maven artifacts)`) 759 signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. ANDROID_SIGNING_KEY)`) 760 deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "https://oss.sonatype.org”)`) 761 upload = flag.String("upload", "", `Destination to upload the archive (usually "gethstore/builds")`) 762 ) 763 flag.CommandLine.Parse(cmdline) 764 env := build.Env() 765 766 //健全性检查sdk和ndk是否已安装和设置 767 if os.Getenv("ANDROID_HOME") == "" { 768 log.Fatal("Please ensure ANDROID_HOME points to your Android SDK") 769 } 770 if os.Getenv("ANDROID_NDK") == "" { 771 log.Fatal("Please ensure ANDROID_NDK points to your Android NDK") 772 } 773 //构建Android存档和Maven资源 774 build.MustRun(goTool("get", "golang.org/x/mobile/cmd/gomobile", "golang.org/x/mobile/cmd/gobind")) 775 build.MustRun(gomobileTool("init", "--ndk", os.Getenv("ANDROID_NDK"))) 776 build.MustRun(gomobileTool("bind", "-ldflags", "-s -w", "--target", "android", "--javapkg", "org.ethereum", "-v", "github.com/ethereum/go-ethereum/mobile")) 777 778 if *local { 779 //如果我们在本地构建,请将bundle复制到build dir并跳过maven 780 os.Rename("geth.aar", filepath.Join(GOBIN, "geth.aar")) 781 return 782 } 783 meta := newMavenMetadata(env) 784 build.Render("build/mvn.pom", meta.Package+".pom", 0755, meta) 785 786 //跳过Maven Deploy和Azure上载以进行pr生成 787 maybeSkipArchive(env) 788 789 //签名并将存档上载到Azure 790 archive := "geth-" + archiveBasename("android", params.ArchiveVersion(env.Commit)) + ".aar" 791 os.Rename("geth.aar", archive) 792 793 if err := archiveUpload(archive, *upload, *signer); err != nil { 794 log.Fatal(err) 795 } 796 //签名并将所有工件上传到Maven Central 797 os.Rename(archive, meta.Package+".aar") 798 if *signer != "" && *deploy != "" { 799 //将签名密钥导入本地GPG实例 800 b64key := os.Getenv(*signer) 801 key, err := base64.StdEncoding.DecodeString(b64key) 802 if err != nil { 803 log.Fatalf("invalid base64 %s", *signer) 804 } 805 gpg := exec.Command("gpg", "--import") 806 gpg.Stdin = bytes.NewReader(key) 807 build.MustRun(gpg) 808 809 keyID, err := build.PGPKeyID(string(key)) 810 if err != nil { 811 log.Fatal(err) 812 } 813 //将工件上传到Sonatype和/或Maven Central 814 repo := *deploy + "/service/local/staging/deploy/maven2" 815 if meta.Develop { 816 repo = *deploy + "/content/repositories/snapshots" 817 } 818 build.MustRunCommand("mvn", "gpg:sign-and-deploy-file", "-e", "-X", 819 "-settings=build/mvn.settings", "-Durl="+repo, "-DrepositoryId=ossrh", 820 "-Dgpg.keyname="+keyID, 821 "-DpomFile="+meta.Package+".pom", "-Dfile="+meta.Package+".aar") 822 } 823 } 824 825 func gomobileTool(subcmd string, args ...string) *exec.Cmd { 826 cmd := exec.Command(filepath.Join(GOBIN, "gomobile"), subcmd) 827 cmd.Args = append(cmd.Args, args...) 828 cmd.Env = []string{ 829 "GOPATH=" + build.GOPATH(), 830 "PATH=" + GOBIN + string(os.PathListSeparator) + os.Getenv("PATH"), 831 } 832 for _, e := range os.Environ() { 833 if strings.HasPrefix(e, "GOPATH=") || strings.HasPrefix(e, "PATH=") { 834 continue 835 } 836 cmd.Env = append(cmd.Env, e) 837 } 838 return cmd 839 } 840 841 type mavenMetadata struct { 842 Version string 843 Package string 844 Develop bool 845 Contributors []mavenContributor 846 } 847 848 type mavenContributor struct { 849 Name string 850 Email string 851 } 852 853 func newMavenMetadata(env build.Environment) mavenMetadata { 854 //从repo根目录收集作者列表 855 contribs := []mavenContributor{} 856 if authors, err := os.Open("AUTHORS"); err == nil { 857 defer authors.Close() 858 859 scanner := bufio.NewScanner(authors) 860 for scanner.Scan() { 861 //跳过作者列表中的任何空白 862 line := strings.TrimSpace(scanner.Text()) 863 if line == "" || line[0] == '#' { 864 continue 865 } 866 //拆分作者并插入为参与者 867 re := regexp.MustCompile("([^<]+) <(.+)>") 868 parts := re.FindStringSubmatch(line) 869 if len(parts) == 3 { 870 contribs = append(contribs, mavenContributor{Name: parts[1], Email: parts[2]}) 871 } 872 } 873 } 874 //呈现版本和包字符串 875 version := params.Version 876 if isUnstableBuild(env) { 877 version += "-SNAPSHOT" 878 } 879 return mavenMetadata{ 880 Version: version, 881 Package: "geth-" + version, 882 Develop: isUnstableBuild(env), 883 Contributors: contribs, 884 } 885 } 886 887 //Xcode框架 888 889 func doXCodeFramework(cmdline []string) { 890 var ( 891 local = flag.Bool("local", false, `Flag whether we're only doing a local build (skip Maven artifacts)`) 892 signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. IOS_SIGNING_KEY)`) 893 deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "trunk")`) 894 upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`) 895 ) 896 flag.CommandLine.Parse(cmdline) 897 env := build.Env() 898 899 //构建iOS Xcode框架 900 build.MustRun(goTool("get", "golang.org/x/mobile/cmd/gomobile", "golang.org/x/mobile/cmd/gobind")) 901 build.MustRun(gomobileTool("init")) 902 bind := gomobileTool("bind", "-ldflags", "-s -w", "--target", "ios", "--tags", "ios", "-v", "github.com/ethereum/go-ethereum/mobile") 903 904 if *local { 905 //如果我们在本地生成,请使用build文件夹,然后停止 906 bind.Dir, _ = filepath.Abs(GOBIN) 907 build.MustRun(bind) 908 return 909 } 910 archive := "geth-" + archiveBasename("ios", params.ArchiveVersion(env.Commit)) 911 if err := os.Mkdir(archive, os.ModePerm); err != nil { 912 log.Fatal(err) 913 } 914 bind.Dir, _ = filepath.Abs(archive) 915 build.MustRun(bind) 916 build.MustRunCommand("tar", "-zcvf", archive+".tar.gz", archive) 917 918 //跳过cocoapods deploy和azure upload for pr builds 919 maybeSkipArchive(env) 920 921 //签名并将框架上载到Azure 922 if err := archiveUpload(archive+".tar.gz", *upload, *signer); err != nil { 923 log.Fatal(err) 924 } 925 //准备并上传一个podspec到cocoapods 926 if *deploy != "" { 927 meta := newPodMetadata(env, archive) 928 build.Render("build/pod.podspec", "Geth.podspec", 0755, meta) 929 build.MustRunCommand("pod", *deploy, "push", "Geth.podspec", "--allow-warnings", "--verbose") 930 } 931 } 932 933 type podMetadata struct { 934 Version string 935 Commit string 936 Archive string 937 Contributors []podContributor 938 } 939 940 type podContributor struct { 941 Name string 942 Email string 943 } 944 945 func newPodMetadata(env build.Environment, archive string) podMetadata { 946 //从repo根目录收集作者列表 947 contribs := []podContributor{} 948 if authors, err := os.Open("AUTHORS"); err == nil { 949 defer authors.Close() 950 951 scanner := bufio.NewScanner(authors) 952 for scanner.Scan() { 953 //跳过作者列表中的任何空白 954 line := strings.TrimSpace(scanner.Text()) 955 if line == "" || line[0] == '#' { 956 continue 957 } 958 //拆分作者并插入为参与者 959 re := regexp.MustCompile("([^<]+) <(.+)>") 960 parts := re.FindStringSubmatch(line) 961 if len(parts) == 3 { 962 contribs = append(contribs, podContributor{Name: parts[1], Email: parts[2]}) 963 } 964 } 965 } 966 version := params.Version 967 if isUnstableBuild(env) { 968 version += "-unstable." + env.Buildnum 969 } 970 return podMetadata{ 971 Archive: archive, 972 Version: version, 973 Commit: env.Commit, 974 Contributors: contribs, 975 } 976 } 977 978 //交叉汇编 979 980 func doXgo(cmdline []string) { 981 var ( 982 alltools = flag.Bool("alltools", false, `Flag whether we're building all known tools, or only on in particular`) 983 ) 984 flag.CommandLine.Parse(cmdline) 985 env := build.Env() 986 987 //确保xgo可用于交叉编译 988 gogetxgo := goTool("get", "github.com/karalabe/xgo") 989 build.MustRun(gogetxgo) 990 991 //如果请求所有工具生成,则生成生成器需要的所有内容 992 args := append(buildFlags(env), flag.Args()...) 993 994 if *alltools { 995 args = append(args, []string{"--dest", GOBIN}...) 996 for _, res := range allCrossCompiledArchiveFiles { 997 if strings.HasPrefix(res, GOBIN) { 998 //找到二进制工具,显式地交叉构建它 999 args = append(args, "./"+filepath.Join("cmd", filepath.Base(res))) 1000 xgo := xgoTool(args) 1001 build.MustRun(xgo) 1002 args = args[:len(args)-1] 1003 } 1004 } 1005 return 1006 } 1007 //否则,执行显式交叉编译 1008 path := args[len(args)-1] 1009 args = append(args[:len(args)-1], []string{"--dest", GOBIN, path}...) 1010 1011 xgo := xgoTool(args) 1012 build.MustRun(xgo) 1013 } 1014 1015 func xgoTool(args []string) *exec.Cmd { 1016 cmd := exec.Command(filepath.Join(GOBIN, "xgo"), args...) 1017 cmd.Env = []string{ 1018 "GOPATH=" + build.GOPATH(), 1019 "GOBIN=" + GOBIN, 1020 } 1021 for _, e := range os.Environ() { 1022 if strings.HasPrefix(e, "GOPATH=") || strings.HasPrefix(e, "GOBIN=") { 1023 continue 1024 } 1025 cmd.Env = append(cmd.Env, e) 1026 } 1027 return cmd 1028 } 1029 1030 //二元分布清理 1031 1032 func doPurge(cmdline []string) { 1033 var ( 1034 store = flag.String("store", "", `Destination from where to purge archives (usually "gethstore/builds")`) 1035 limit = flag.Int("days", 30, `Age threshold above which to delete unstable archives`) 1036 ) 1037 flag.CommandLine.Parse(cmdline) 1038 1039 if env := build.Env(); !env.IsCronJob { 1040 log.Printf("skipping because not a cron job") 1041 os.Exit(0) 1042 } 1043 //创建Azure身份验证并列出当前存档 1044 auth := build.AzureBlobstoreConfig{ 1045 Account: strings.Split(*store, "/")[0], 1046 Token: os.Getenv("AZURE_BLOBSTORE_TOKEN"), 1047 Container: strings.SplitN(*store, "/", 2)[1], 1048 } 1049 blobs, err := build.AzureBlobstoreList(auth) 1050 if err != nil { 1051 log.Fatal(err) 1052 } 1053 //迭代blob,收集并排序所有不稳定的构建 1054 for i := 0; i < len(blobs); i++ { 1055 if !strings.Contains(blobs[i].Name, "unstable") { 1056 blobs = append(blobs[:i], blobs[i+1:]...) 1057 i-- 1058 } 1059 } 1060 for i := 0; i < len(blobs); i++ { 1061 for j := i + 1; j < len(blobs); j++ { 1062 if blobs[i].Properties.LastModified.After(blobs[j].Properties.LastModified) { 1063 blobs[i], blobs[j] = blobs[j], blobs[i] 1064 } 1065 } 1066 } 1067 //筛选出比给定阈值更新的所有存档 1068 for i, blob := range blobs { 1069 if time.Since(blob.Properties.LastModified) < time.Duration(*limit)*24*time.Hour { 1070 blobs = blobs[:i] 1071 break 1072 } 1073 } 1074 //删除所有标记为此类的内容并返回 1075 if err := build.AzureBlobstoreDelete(auth, blobs); err != nil { 1076 log.Fatal(err) 1077 } 1078 } 1079