github.com/tirogen/go-ethereum@v1.10.12-0.20221226051715-250cfede41b6/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  	"log"
    28  	"os"
    29  	"os/exec"
    30  	"path"
    31  	"path/filepath"
    32  	"strconv"
    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(">>>", printArgs(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 printArgs(args []string) string {
    54  	var s strings.Builder
    55  	for i, arg := range args {
    56  		if i > 0 {
    57  			s.WriteByte(' ')
    58  		}
    59  		if strings.IndexByte(arg, ' ') >= 0 {
    60  			arg = strconv.QuoteToASCII(arg)
    61  		}
    62  		s.WriteString(arg)
    63  	}
    64  	return s.String()
    65  }
    66  
    67  func MustRunCommand(cmd string, args ...string) {
    68  	MustRun(exec.Command(cmd, args...))
    69  }
    70  
    71  var warnedAboutGit bool
    72  
    73  // RunGit runs a git subcommand and returns its output.
    74  // The command must complete successfully.
    75  func RunGit(args ...string) string {
    76  	cmd := exec.Command("git", args...)
    77  	var stdout, stderr bytes.Buffer
    78  	cmd.Stdout, cmd.Stderr = &stdout, &stderr
    79  	if err := cmd.Run(); err != nil {
    80  		if e, ok := err.(*exec.Error); ok && e.Err == exec.ErrNotFound {
    81  			if !warnedAboutGit {
    82  				log.Println("Warning: can't find 'git' in PATH")
    83  				warnedAboutGit = true
    84  			}
    85  			return ""
    86  		}
    87  		log.Fatal(strings.Join(cmd.Args, " "), ": ", err, "\n", stderr.String())
    88  	}
    89  	return strings.TrimSpace(stdout.String())
    90  }
    91  
    92  // readGitFile returns content of file in .git directory.
    93  func readGitFile(file string) string {
    94  	content, err := os.ReadFile(path.Join(".git", file))
    95  	if err != nil {
    96  		return ""
    97  	}
    98  	return strings.TrimSpace(string(content))
    99  }
   100  
   101  // Render renders the given template file into outputFile.
   102  func Render(templateFile, outputFile string, outputPerm os.FileMode, x interface{}) {
   103  	tpl := template.Must(template.ParseFiles(templateFile))
   104  	render(tpl, outputFile, outputPerm, x)
   105  }
   106  
   107  // RenderString renders the given template string into outputFile.
   108  func RenderString(templateContent, outputFile string, outputPerm os.FileMode, x interface{}) {
   109  	tpl := template.Must(template.New("").Parse(templateContent))
   110  	render(tpl, outputFile, outputPerm, x)
   111  }
   112  
   113  func render(tpl *template.Template, outputFile string, outputPerm os.FileMode, x interface{}) {
   114  	if err := os.MkdirAll(filepath.Dir(outputFile), 0755); err != nil {
   115  		log.Fatal(err)
   116  	}
   117  	out, err := os.OpenFile(outputFile, os.O_CREATE|os.O_WRONLY|os.O_EXCL, outputPerm)
   118  	if err != nil {
   119  		log.Fatal(err)
   120  	}
   121  	if err := tpl.Execute(out, x); err != nil {
   122  		log.Fatal(err)
   123  	}
   124  	if err := out.Close(); err != nil {
   125  		log.Fatal(err)
   126  	}
   127  }
   128  
   129  // UploadSFTP uploads files to a remote host using the sftp command line tool.
   130  // The destination host may be specified either as [user@]host[: or as a URI in
   131  // the form sftp://[user@]host[:port].
   132  func UploadSFTP(identityFile, host, dir string, files []string) error {
   133  	sftp := exec.Command("sftp")
   134  	sftp.Stderr = os.Stderr
   135  	if identityFile != "" {
   136  		sftp.Args = append(sftp.Args, "-i", identityFile)
   137  	}
   138  	sftp.Args = append(sftp.Args, host)
   139  	fmt.Println(">>>", printArgs(sftp.Args))
   140  	if *DryRunFlag {
   141  		return nil
   142  	}
   143  
   144  	stdin, err := sftp.StdinPipe()
   145  	if err != nil {
   146  		return fmt.Errorf("can't create stdin pipe for sftp: %v", err)
   147  	}
   148  	stdout, err := sftp.StdoutPipe()
   149  	if err != nil {
   150  		return fmt.Errorf("can't create stdout pipe for sftp: %v", err)
   151  	}
   152  	if err := sftp.Start(); err != nil {
   153  		return err
   154  	}
   155  	in := io.MultiWriter(stdin, os.Stdout)
   156  	for _, f := range files {
   157  		fmt.Fprintln(in, "put", f, path.Join(dir, filepath.Base(f)))
   158  	}
   159  	fmt.Fprintln(in, "exit")
   160  	// Some issue with the PPA sftp server makes it so the server does not
   161  	// respond properly to a 'bye', 'exit' or 'quit' from the client.
   162  	// To work around that, we check the output, and when we see the client
   163  	// exit command, we do a hard exit.
   164  	// See
   165  	// https://github.com/kolban-google/sftp-gcs/issues/23
   166  	// https://github.com/mscdex/ssh2/pull/1111
   167  	aborted := false
   168  	go func() {
   169  		scanner := bufio.NewScanner(stdout)
   170  		for scanner.Scan() {
   171  			txt := scanner.Text()
   172  			fmt.Println(txt)
   173  			if txt == "sftp> exit" {
   174  				// Give it .5 seconds to exit (server might be fixed), then
   175  				// hard kill it from the outside
   176  				time.Sleep(500 * time.Millisecond)
   177  				aborted = true
   178  				sftp.Process.Kill()
   179  			}
   180  		}
   181  	}()
   182  	stdin.Close()
   183  	err = sftp.Wait()
   184  	if aborted {
   185  		return nil
   186  	}
   187  	return err
   188  }
   189  
   190  // FindMainPackages finds all 'main' packages in the given directory and returns their
   191  // package paths.
   192  func FindMainPackages(dir string) []string {
   193  	var commands []string
   194  	cmds, err := os.ReadDir(dir)
   195  	if err != nil {
   196  		log.Fatal(err)
   197  	}
   198  	for _, cmd := range cmds {
   199  		pkgdir := filepath.Join(dir, cmd.Name())
   200  		pkgs, err := parser.ParseDir(token.NewFileSet(), pkgdir, nil, parser.PackageClauseOnly)
   201  		if err != nil {
   202  			log.Fatal(err)
   203  		}
   204  		for name := range pkgs {
   205  			if name == "main" {
   206  				path := "./" + filepath.ToSlash(pkgdir)
   207  				commands = append(commands, path)
   208  				break
   209  			}
   210  		}
   211  	}
   212  	return commands
   213  }