github.com/codysnider/go-ethereum@v1.10.18-0.20220420071915-14f4ae99222a/internal/build/util.go (about)

     1  // Copyright 2016 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package build
    18  
    19  import (
    20  	"bufio"
    21  	"bytes"
    22  	"flag"
    23  	"fmt"
    24  	"go/parser"
    25  	"go/token"
    26  	"io"
    27  	"io/ioutil"
    28  	"log"
    29  	"os"
    30  	"os/exec"
    31  	"path"
    32  	"path/filepath"
    33  	"strings"
    34  	"text/template"
    35  	"time"
    36  )
    37  
    38  var DryRunFlag = flag.Bool("n", false, "dry run, don't execute commands")
    39  
    40  // MustRun executes the given command and exits the host process for
    41  // any error.
    42  func MustRun(cmd *exec.Cmd) {
    43  	fmt.Println(">>>", strings.Join(cmd.Args, " "))
    44  	if !*DryRunFlag {
    45  		cmd.Stderr = os.Stderr
    46  		cmd.Stdout = os.Stdout
    47  		if err := cmd.Run(); err != nil {
    48  			log.Fatal(err)
    49  		}
    50  	}
    51  }
    52  
    53  func MustRunCommand(cmd string, args ...string) {
    54  	MustRun(exec.Command(cmd, args...))
    55  }
    56  
    57  var warnedAboutGit bool
    58  
    59  // RunGit runs a git subcommand and returns its output.
    60  // The command must complete successfully.
    61  func RunGit(args ...string) string {
    62  	cmd := exec.Command("git", args...)
    63  	var stdout, stderr bytes.Buffer
    64  	cmd.Stdout, cmd.Stderr = &stdout, &stderr
    65  	if err := cmd.Run(); err != nil {
    66  		if e, ok := err.(*exec.Error); ok && e.Err == exec.ErrNotFound {
    67  			if !warnedAboutGit {
    68  				log.Println("Warning: can't find 'git' in PATH")
    69  				warnedAboutGit = true
    70  			}
    71  			return ""
    72  		}
    73  		log.Fatal(strings.Join(cmd.Args, " "), ": ", err, "\n", stderr.String())
    74  	}
    75  	return strings.TrimSpace(stdout.String())
    76  }
    77  
    78  // readGitFile returns content of file in .git directory.
    79  func readGitFile(file string) string {
    80  	content, err := ioutil.ReadFile(path.Join(".git", file))
    81  	if err != nil {
    82  		return ""
    83  	}
    84  	return strings.TrimSpace(string(content))
    85  }
    86  
    87  // Render renders the given template file into outputFile.
    88  func Render(templateFile, outputFile string, outputPerm os.FileMode, x interface{}) {
    89  	tpl := template.Must(template.ParseFiles(templateFile))
    90  	render(tpl, outputFile, outputPerm, x)
    91  }
    92  
    93  // RenderString renders the given template string into outputFile.
    94  func RenderString(templateContent, outputFile string, outputPerm os.FileMode, x interface{}) {
    95  	tpl := template.Must(template.New("").Parse(templateContent))
    96  	render(tpl, outputFile, outputPerm, x)
    97  }
    98  
    99  func render(tpl *template.Template, outputFile string, outputPerm os.FileMode, x interface{}) {
   100  	if err := os.MkdirAll(filepath.Dir(outputFile), 0755); err != nil {
   101  		log.Fatal(err)
   102  	}
   103  	out, err := os.OpenFile(outputFile, os.O_CREATE|os.O_WRONLY|os.O_EXCL, outputPerm)
   104  	if err != nil {
   105  		log.Fatal(err)
   106  	}
   107  	if err := tpl.Execute(out, x); err != nil {
   108  		log.Fatal(err)
   109  	}
   110  	if err := out.Close(); err != nil {
   111  		log.Fatal(err)
   112  	}
   113  }
   114  
   115  // UploadSFTP uploads files to a remote host using the sftp command line tool.
   116  // The destination host may be specified either as [user@]host[: or as a URI in
   117  // the form sftp://[user@]host[:port].
   118  func UploadSFTP(identityFile, host, dir string, files []string) error {
   119  	sftp := exec.Command("sftp")
   120  	sftp.Stderr = os.Stderr
   121  	if identityFile != "" {
   122  		sftp.Args = append(sftp.Args, "-i", identityFile)
   123  	}
   124  	sftp.Args = append(sftp.Args, host)
   125  	fmt.Println(">>>", strings.Join(sftp.Args, " "))
   126  	if *DryRunFlag {
   127  		return nil
   128  	}
   129  
   130  	stdin, err := sftp.StdinPipe()
   131  	if err != nil {
   132  		return fmt.Errorf("can't create stdin pipe for sftp: %v", err)
   133  	}
   134  	stdout, err := sftp.StdoutPipe()
   135  	if err != nil {
   136  		return fmt.Errorf("can't create stdout pipe for sftp: %v", err)
   137  	}
   138  	if err := sftp.Start(); err != nil {
   139  		return err
   140  	}
   141  	in := io.MultiWriter(stdin, os.Stdout)
   142  	for _, f := range files {
   143  		fmt.Fprintln(in, "put", f, path.Join(dir, filepath.Base(f)))
   144  	}
   145  	fmt.Fprintln(in, "exit")
   146  	// Some issue with the PPA sftp server makes it so the server does not
   147  	// respond properly to a 'bye', 'exit' or 'quit' from the client.
   148  	// To work around that, we check the output, and when we see the client
   149  	// exit command, we do a hard exit.
   150  	// See
   151  	// https://github.com/kolban-google/sftp-gcs/issues/23
   152  	// https://github.com/mscdex/ssh2/pull/1111
   153  	aborted := false
   154  	go func() {
   155  		scanner := bufio.NewScanner(stdout)
   156  		for scanner.Scan() {
   157  			txt := scanner.Text()
   158  			fmt.Println(txt)
   159  			if txt == "sftp> exit" {
   160  				// Give it .5 seconds to exit (server might be fixed), then
   161  				// hard kill it from the outside
   162  				time.Sleep(500 * time.Millisecond)
   163  				aborted = true
   164  				sftp.Process.Kill()
   165  			}
   166  		}
   167  	}()
   168  	stdin.Close()
   169  	err = sftp.Wait()
   170  	if aborted {
   171  		return nil
   172  	}
   173  	return err
   174  }
   175  
   176  // FindMainPackages finds all 'main' packages in the given directory and returns their
   177  // package paths.
   178  func FindMainPackages(dir string) []string {
   179  	var commands []string
   180  	cmds, err := ioutil.ReadDir(dir)
   181  	if err != nil {
   182  		log.Fatal(err)
   183  	}
   184  	for _, cmd := range cmds {
   185  		pkgdir := filepath.Join(dir, cmd.Name())
   186  		pkgs, err := parser.ParseDir(token.NewFileSet(), pkgdir, nil, parser.PackageClauseOnly)
   187  		if err != nil {
   188  			log.Fatal(err)
   189  		}
   190  		for name := range pkgs {
   191  			if name == "main" {
   192  				path := "./" + filepath.ToSlash(pkgdir)
   193  				commands = append(commands, path)
   194  				break
   195  			}
   196  		}
   197  	}
   198  	return commands
   199  }