github.com/klaytn/klaytn@v1.12.1/utils/build/util.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2016 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from internal/build/util.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package build
    22  
    23  import (
    24  	"bytes"
    25  	"flag"
    26  	"fmt"
    27  	"io"
    28  	"log"
    29  	"os"
    30  	"os/exec"
    31  	"path"
    32  	"path/filepath"
    33  	"runtime"
    34  	"strings"
    35  	"text/template"
    36  )
    37  
    38  var DryRunFlag = flag.Bool("n", false, "dry run, don't execute commands")
    39  
    40  // TryRun executes the given command and returns an error if error occurs.
    41  func TryRun(cmd *exec.Cmd) error {
    42  	fmt.Println(">>>", strings.Join(cmd.Args, " "))
    43  	if !*DryRunFlag {
    44  		cmd.Stderr = os.Stderr
    45  		cmd.Stdout = os.Stdout
    46  		return cmd.Run()
    47  	}
    48  	return nil
    49  }
    50  
    51  // TryRunCommand executes the given command and arguments in strings.
    52  func TryRunCommand(cmd string, args ...string) {
    53  	TryRun(exec.Command(cmd, args...))
    54  }
    55  
    56  // MustRun executes the given command and exits the host process for
    57  // any error.
    58  func MustRun(cmd *exec.Cmd) {
    59  	if err := TryRun(cmd); err != nil {
    60  		log.Fatal(err)
    61  	}
    62  }
    63  
    64  func MustRunCommand(cmd string, args ...string) {
    65  	MustRun(exec.Command(cmd, args...))
    66  }
    67  
    68  // GOPATH returns the value that the GOPATH environment
    69  // variable should be set to.
    70  func GOPATH() string {
    71  	if os.Getenv("GOPATH") == "" {
    72  		log.Fatal("GOPATH is not set")
    73  	}
    74  	return os.Getenv("GOPATH")
    75  }
    76  
    77  // VERSION returns the content of the VERSION file.
    78  func VERSION() string {
    79  	version, err := os.ReadFile("VERSION")
    80  	if err != nil {
    81  		log.Fatal(err)
    82  	}
    83  	return string(bytes.TrimSpace(version))
    84  }
    85  
    86  var warnedAboutGit bool
    87  
    88  // RunGit runs a git subcommand and returns its output.
    89  // The command must complete successfully.
    90  func RunGit(args ...string) string {
    91  	cmd := exec.Command("git", args...)
    92  	var stdout, stderr bytes.Buffer
    93  	cmd.Stdout, cmd.Stderr = &stdout, &stderr
    94  	if err := cmd.Run(); err == exec.ErrNotFound {
    95  		if !warnedAboutGit {
    96  			log.Println("Warning: can't find 'git' in PATH")
    97  			warnedAboutGit = true
    98  		}
    99  		return ""
   100  	} else if err != nil {
   101  		log.Fatal(strings.Join(cmd.Args, " "), ": ", err, "\n", stderr.String())
   102  	}
   103  	return strings.TrimSpace(stdout.String())
   104  }
   105  
   106  // readGitFile returns content of file in .git directory.
   107  func readGitFile(file string) string {
   108  	content, err := os.ReadFile(path.Join(".git", file))
   109  	if err != nil {
   110  		return ""
   111  	}
   112  	return strings.TrimSpace(string(content))
   113  }
   114  
   115  // Render renders the given template file into outputFile.
   116  func Render(templateFile, outputFile string, outputPerm os.FileMode, x interface{}) {
   117  	tpl := template.Must(template.ParseFiles(templateFile))
   118  	render(tpl, outputFile, outputPerm, x)
   119  }
   120  
   121  // RenderString renders the given template string into outputFile.
   122  func RenderString(templateContent, outputFile string, outputPerm os.FileMode, x interface{}) {
   123  	tpl := template.Must(template.New("").Parse(templateContent))
   124  	render(tpl, outputFile, outputPerm, x)
   125  }
   126  
   127  func render(tpl *template.Template, outputFile string, outputPerm os.FileMode, x interface{}) {
   128  	if err := os.MkdirAll(filepath.Dir(outputFile), 0o755); err != nil {
   129  		log.Fatal(err)
   130  	}
   131  	out, err := os.OpenFile(outputFile, os.O_CREATE|os.O_WRONLY|os.O_EXCL, outputPerm)
   132  	if err != nil {
   133  		log.Fatal(err)
   134  	}
   135  	if err := tpl.Execute(out, x); err != nil {
   136  		log.Fatal(err)
   137  	}
   138  	if err := out.Close(); err != nil {
   139  		log.Fatal(err)
   140  	}
   141  }
   142  
   143  // CopyFile copies a file.
   144  func CopyFile(dst, src string, mode os.FileMode) {
   145  	if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
   146  		log.Fatal(err)
   147  	}
   148  	destFile, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode)
   149  	if err != nil {
   150  		log.Fatal(err)
   151  	}
   152  	defer destFile.Close()
   153  
   154  	srcFile, err := os.Open(src)
   155  	if err != nil {
   156  		log.Fatal(err)
   157  	}
   158  	defer srcFile.Close()
   159  
   160  	if _, err := io.Copy(destFile, srcFile); err != nil {
   161  		log.Fatal(err)
   162  	}
   163  }
   164  
   165  // GoTool returns the command that runs a go tool. This uses go from GOROOT instead of PATH
   166  // so that go commands executed by build use the same version of Go as the 'host' that runs
   167  // build code. e.g.
   168  //
   169  //	/usr/lib/go-1.8/bin/go run build/ci.go ...
   170  //
   171  // runs using go 1.8 and invokes go 1.8 tools from the same GOROOT. This is also important
   172  // because runtime.Version checks on the host should match the tools that are run.
   173  func GoTool(tool string, args ...string) *exec.Cmd {
   174  	args = append([]string{tool}, args...)
   175  	return exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), args...)
   176  }
   177  
   178  // ExpandPackages expands a packages list if an input contains "...".
   179  func ExpandPackages(packages []string) []string {
   180  	for _, pkg := range packages {
   181  		if strings.Contains(pkg, "...") {
   182  			var newPkgs []string
   183  
   184  			cmd := GoTool("list", packages...)
   185  			out, err := cmd.Output()
   186  			if err != nil {
   187  				log.Fatalf("package listing failed: %v\n%s", err, string(out))
   188  			}
   189  
   190  			for _, line := range strings.Split(string(out), "\n") {
   191  				newPkgs = append(newPkgs, strings.TrimSpace(line))
   192  			}
   193  			return newPkgs
   194  		}
   195  	}
   196  	return packages
   197  }
   198  
   199  // ExcludePackages excludes packages having patterns from the passed package slice and
   200  // returns a slice including only the remained packages.
   201  func ExcludePackages(packages []string, patterns []string) []string {
   202  	// TODO-Klaytn This exclusion code is a naive implementation. Improve this if it hurts build performance.
   203  	packages = ExpandPackages(packages)
   204  
   205  	for _, pattern := range patterns {
   206  		var newPkgs []string
   207  		for _, pkg := range packages {
   208  			if !strings.Contains(pkg, pattern) {
   209  				newPkgs = append(newPkgs, pkg)
   210  			}
   211  		}
   212  		packages = newPkgs
   213  	}
   214  	return packages
   215  }