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  }