github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/performance/utils/dolt_builder/run.go (about)

     1  // Copyright 2019-2022 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package dolt_builder
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"fmt"
    21  	"os"
    22  	"os/signal"
    23  	"path/filepath"
    24  	"runtime"
    25  	"sync"
    26  	"syscall"
    27  
    28  	"golang.org/x/sync/errgroup"
    29  )
    30  
    31  const envDoltBin = "DOLT_BIN"
    32  
    33  func Run(parentCtx context.Context, commitList []string, profilePath string) error {
    34  	if profilePath != "" && len(commitList) > 1 {
    35  		return errors.New("cannot build more that one binary when a profile is supplied")
    36  	}
    37  
    38  	doltBin, err := getDoltBin()
    39  	if err != nil {
    40  		return err
    41  	}
    42  
    43  	// check for git on path
    44  	err = GitVersion(parentCtx)
    45  	if err != nil {
    46  		return err
    47  	}
    48  
    49  	cwd, err := os.Getwd()
    50  	if err != nil {
    51  		return err
    52  	}
    53  
    54  	// make temp dir for cloning/copying dolt source
    55  	tempDir := filepath.Join(cwd, "clones-copies")
    56  	err = os.MkdirAll(tempDir, os.ModePerm)
    57  	if err != nil {
    58  		return err
    59  	}
    60  
    61  	// clone dolt source
    62  	err = GitCloneBare(parentCtx, tempDir, GithubDolt)
    63  	if err != nil {
    64  		return err
    65  	}
    66  
    67  	repoDir := filepath.Join(tempDir, "dolt.git")
    68  
    69  	withKeyCtx, cancel := context.WithCancel(parentCtx)
    70  	g, ctx := errgroup.WithContext(withKeyCtx)
    71  
    72  	// handle user interrupt
    73  	quit := make(chan os.Signal, 1)
    74  	signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
    75  	var wg sync.WaitGroup
    76  	wg.Add(1)
    77  	go func() {
    78  		<-quit
    79  		defer wg.Done()
    80  		signal.Stop(quit)
    81  		cancel()
    82  	}()
    83  
    84  	for _, commit := range commitList {
    85  		commit := commit // https://golang.org/doc/faq#closures_and_goroutines
    86  		g.Go(func() error {
    87  			return buildBinaries(ctx, tempDir, repoDir, doltBin, profilePath, commit)
    88  		})
    89  	}
    90  
    91  	builderr := g.Wait()
    92  	close(quit)
    93  	wg.Wait()
    94  
    95  	// remove clones-copies after all go routines complete
    96  	// will exit successfully if removal fails
    97  	if err := os.RemoveAll(tempDir); err != nil {
    98  		fmt.Printf("WARN: %s was not removed\n", tempDir)
    99  		fmt.Printf("WARN: error: %v\n", err)
   100  	}
   101  
   102  	if builderr != nil {
   103  		return builderr
   104  	}
   105  
   106  	return nil
   107  }
   108  
   109  // getDoltBin creates and returns the absolute path for DOLT_BIN
   110  // if it was found, otherwise uses the current working directory
   111  // as the parent directory for a `doltBin` directory
   112  func getDoltBin() (string, error) {
   113  	var doltBin string
   114  	dir := os.Getenv(envDoltBin)
   115  	if dir == "" {
   116  		cwd, err := os.Getwd()
   117  		if err != nil {
   118  			return "", err
   119  		}
   120  		doltBin = filepath.Join(cwd, "doltBin")
   121  	} else {
   122  		abs, err := filepath.Abs(dir)
   123  		if err != nil {
   124  			return "", err
   125  		}
   126  		doltBin = abs
   127  	}
   128  	err := os.MkdirAll(doltBin, os.ModePerm)
   129  	if err != nil {
   130  		return "", err
   131  	}
   132  	return doltBin, nil
   133  }
   134  
   135  // buildBinaries builds a dolt binary at the given commit and stores it in the doltBin
   136  func buildBinaries(ctx context.Context, tempDir, repoDir, doltBinDir, profilePath, commit string) error {
   137  	checkoutDir := filepath.Join(tempDir, commit)
   138  	if err := os.MkdirAll(checkoutDir, os.ModePerm); err != nil {
   139  		return err
   140  	}
   141  
   142  	err := GitCheckoutTree(ctx, repoDir, checkoutDir, commit)
   143  	if err != nil {
   144  		return err
   145  	}
   146  
   147  	commitDir := filepath.Join(doltBinDir, commit)
   148  	if err := os.MkdirAll(commitDir, os.ModePerm); err != nil {
   149  		return err
   150  	}
   151  
   152  	command, err := goBuild(ctx, checkoutDir, commitDir, profilePath)
   153  	if err != nil {
   154  		return err
   155  	}
   156  
   157  	return doltVersion(ctx, commitDir, command)
   158  }
   159  
   160  // goBuild builds the dolt binary and returns the filename
   161  func goBuild(ctx context.Context, source, dest, profilePath string) (string, error) {
   162  	goDir := filepath.Join(source, "go")
   163  	doltFileName := "dolt"
   164  	if runtime.GOOS == "windows" {
   165  		doltFileName = "dolt.exe"
   166  	}
   167  
   168  	args := make([]string, 0)
   169  	args = append(args, "build")
   170  
   171  	if profilePath != "" {
   172  		args = append(args, fmt.Sprintf("-pgo=%s", profilePath))
   173  	}
   174  
   175  	toBuild := filepath.Join(dest, doltFileName)
   176  	args = append(args, "-o", toBuild, filepath.Join(goDir, "cmd", "dolt"))
   177  
   178  	build := ExecCommand(ctx, "go", args...)
   179  	build.Dir = goDir
   180  	err := build.Run()
   181  	if err != nil {
   182  		return "", err
   183  	}
   184  	return toBuild, nil
   185  }
   186  
   187  // doltVersion prints dolt version of binary
   188  func doltVersion(ctx context.Context, dir, command string) error {
   189  	doltVersion := ExecCommand(ctx, command, "version")
   190  	doltVersion.Stderr = os.Stderr
   191  	doltVersion.Stdout = os.Stdout
   192  	doltVersion.Dir = dir
   193  	return doltVersion.Run()
   194  }