github.com/Songmu/goxz@v0.9.1/builder.go (about) 1 package goxz 2 3 import ( 4 "bytes" 5 "compress/flate" 6 "compress/gzip" 7 "io/ioutil" 8 "log" 9 "os" 10 "os/exec" 11 "path/filepath" 12 "strings" 13 14 "github.com/mholt/archiver/v3" 15 "github.com/pkg/errors" 16 ) 17 18 type builder struct { 19 name, version string 20 platform *platform 21 output string 22 buildLdFlags, buildTags, buildInstallSuffix string 23 pkgs []string 24 workDirBase string 25 zipAlways, static, trimpath bool 26 resources []string 27 projDir string 28 } 29 30 func (bdr *builder) build() (string, error) { 31 dirStuff := []string{bdr.name} 32 if bdr.version != "" { 33 dirStuff = append(dirStuff, bdr.version) 34 } 35 dirStuff = append(dirStuff, bdr.platform.os, bdr.platform.arch) 36 dirname := strings.Join(dirStuff, "_") 37 workDir := filepath.Join(bdr.workDirBase, dirname) 38 if err := os.Mkdir(workDir, 0755); err != nil { 39 return "", err 40 } 41 42 for _, pkg := range bdr.pkgs { 43 log.Printf("Building %s for %s/%s\n", pkg, bdr.platform.os, bdr.platform.arch) 44 var stdout, stderr bytes.Buffer 45 cmd := exec.Command("go", "list", "-f", "{{.Name}}", pkg) 46 cmd.Stdout, cmd.Stderr = &stdout, &stderr 47 if err := cmd.Run(); err != nil { 48 return "", errors.Errorf("go list failed with following output: %q", stderr.String()) 49 } 50 pkgName := strings.TrimSpace(stdout.String()) 51 if pkgName != "main" { 52 return "", errors.Errorf("can't build artifact for non main package: %q", pkgName) 53 } 54 output := bdr.output 55 if output == "" { 56 output = filepath.Base(pkg) 57 if output == "." { 58 wd, err := os.Getwd() 59 if err != nil { 60 return "", err 61 } 62 output = filepath.Base(wd) 63 } 64 if bdr.platform.os == "windows" { 65 output += ".exe" 66 } 67 } 68 cmdArgs := []string{"build", "-o", filepath.Join(workDir, output)} 69 // ref. https://github.com/golang/go/issues/26492#issuecomment-435462350 70 if bdr.buildLdFlags != "" || bdr.static { 71 var flags string 72 if bdr.static { 73 switch bdr.platform.os { 74 case "freebsd", "netbsd", "linux", "windows": 75 flags = `-extldflags "-static"` 76 case "darwin": 77 flags = `-s -extldflags "-sectcreate __TEXT __info_plist Info.plist"` 78 case "android": 79 flags = `-s` 80 } 81 } 82 if bdr.buildLdFlags != "" { 83 if flags == "" { 84 flags = bdr.buildLdFlags 85 } else { 86 flags += " " + bdr.buildLdFlags 87 } 88 } 89 if flags != "" { 90 cmdArgs = append(cmdArgs, "-ldflags", flags) 91 } 92 } 93 if bdr.buildTags != "" || bdr.static { 94 var tags string 95 if bdr.static { 96 switch bdr.platform.os { 97 case "windows", "freebsd", "netbsd": 98 tags = "netgo" 99 case "linux": 100 tags = "netgo osusergo" 101 } 102 } 103 if bdr.buildTags != "" { 104 if tags == "" { 105 tags = bdr.buildTags 106 } else { 107 tags += " " + bdr.buildTags 108 } 109 } 110 if tags != "" { 111 cmdArgs = append(cmdArgs, "-tags", tags) 112 } 113 } 114 if bdr.trimpath { 115 cmdArgs = append(cmdArgs, "-trimpath") 116 } 117 if bdr.buildInstallSuffix != "" { 118 cmdArgs = append(cmdArgs, "-installsuffix", bdr.buildInstallSuffix) 119 } 120 cmdArgs = append(cmdArgs, pkg) 121 122 cmd = exec.Command("go", cmdArgs...) 123 cmd.Env = append(os.Environ(), "GOOS="+bdr.platform.os, "GOARCH="+bdr.platform.arch) 124 bs, err := cmd.CombinedOutput() 125 if err != nil { 126 return "", errors.Wrapf(err, 127 "go build failed while building %q for %s/%s with following output:\n%s", 128 pkg, bdr.platform.os, bdr.platform.arch, string(bs)) 129 } 130 } 131 files, err := ioutil.ReadDir(workDir) 132 if err != nil { 133 return "", err 134 } 135 if len(files) == 0 { 136 return "", errors.Errorf("No binaries are built from [%s] for %s/%s", 137 strings.Join(bdr.pkgs, " "), bdr.platform.os, bdr.platform.arch) 138 } 139 140 for _, rc := range bdr.resources { 141 rel, _ := filepath.Rel(bdr.projDir, rc) 142 dest := filepath.Join(workDir, rel) 143 if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil { 144 return "", err 145 } 146 if err := os.Link(rc, dest); err != nil { 147 return "", err 148 } 149 } 150 151 var arch archiver.Archiver 152 var archiveFilePath string 153 if bdr.zipAlways || bdr.platform.os == "windows" || bdr.platform.os == "darwin" { 154 arch = &archiver.Zip{ 155 CompressionLevel: flate.DefaultCompression, 156 MkdirAll: true, 157 SelectiveCompression: true, 158 FileMethod: archiver.Deflate, 159 } 160 archiveFilePath = workDir + ".zip" 161 } else { 162 arch = &archiver.TarGz{ 163 CompressionLevel: gzip.DefaultCompression, 164 Tar: &archiver.Tar{ 165 MkdirAll: true, 166 }, 167 } 168 archiveFilePath = workDir + ".tar.gz" 169 } 170 log.Printf("Archiving %s\n", filepath.Base(archiveFilePath)) 171 err = arch.Archive([]string{workDir}, archiveFilePath) 172 if err != nil { 173 return "", err 174 } 175 return archiveFilePath, nil 176 }