github.com/bazelbuild/bazel-gazelle@v0.36.1-0.20240520142334-61b277ba6fed/cmd/autogazelle/autogazelle.go (about)

     1  /* Copyright 2018 The Bazel Authors. All rights reserved.
     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  
    16  // autogazelle is a program that tracks changes in a workspace and runs
    17  // gazelle to incorporate those changes into Bazel build files.
    18  //
    19  // autogazelle has two components: a client and a server. The server
    20  // watches for file system changes within the workspace and builds a
    21  // set of build files that need to be updated. The server listens on a
    22  // UNIX socket. When it accepts a connection, it runs gazelle in modified
    23  // directories and closes the connection without transmitting anything.
    24  // The client simply connects to the server and waits for the connection
    25  // to be closed.
    26  //
    27  // autogazelle is intended to be invoked by autogazelle.bash as a bazel
    28  // wrapper script. It requires the BUILD_WORKSPACE_DIRECTORY environment
    29  // variable to be set to the workspace root directory and BAZEL_REAL to
    30  // be set to the local of the real bazel binary.
    31  package main
    32  
    33  import (
    34  	"errors"
    35  	"flag"
    36  	"fmt"
    37  	"io"
    38  	"log"
    39  	"os"
    40  	"os/exec"
    41  	"path/filepath"
    42  	"strings"
    43  	"time"
    44  )
    45  
    46  var (
    47  	programName = filepath.Base(os.Args[0])
    48  
    49  	isServer      = flag.Bool("server", false, "whether this process acts as the server")
    50  	gazelleLabel  = flag.String("gazelle", "", "label for script that autogazelle should invoke with 'bazel run'")
    51  	serverTimeout = flag.Duration("timeout", 3600*time.Second, "time in seconds the server will listen for a client before quitting")
    52  	socketPath    = flag.String("socket", "tools/autogazelle.socket", "path to the UNIX socket where the server will listen, relative to the workspace root")
    53  	logPath       = flag.String("log", "tools/autogazelle.log", "path to the server's log file, relative to the workspace root")
    54  )
    55  
    56  func main() {
    57  	log.SetPrefix(programName + ": ")
    58  	log.SetFlags(log.Ldate | log.Ltime)
    59  	flag.Parse()
    60  	if err := run(); err != nil {
    61  		log.Fatal(err)
    62  	}
    63  }
    64  
    65  func run() error {
    66  	if *gazelleLabel == "" {
    67  		return errors.New("-gazelle not set")
    68  	}
    69  
    70  	workspaceDir, ok := os.LookupEnv("BUILD_WORKSPACE_DIRECTORY")
    71  	if !ok {
    72  		return errors.New("BUILD_WORKSPACE_DIRECTORY not set")
    73  	}
    74  	if err := os.Chdir(workspaceDir); err != nil {
    75  		return err
    76  	}
    77  
    78  	if _, ok := os.LookupEnv("BAZEL_REAL"); !ok {
    79  		return errors.New("BAZEL_REAL not set")
    80  	}
    81  
    82  	if *isServer {
    83  		return runServer()
    84  	} else {
    85  		return runClient()
    86  	}
    87  }
    88  
    89  type mode int
    90  
    91  const (
    92  	fullMode mode = iota
    93  	fastMode
    94  )
    95  
    96  // runGazelle invokes gazelle with "bazel run". In fullMode, gazelle will
    97  // run in the entire repository. In fastMode, gazelle will only run
    98  // in the given directories.
    99  func runGazelle(mode mode, dirs []string) error {
   100  	if mode == fastMode && len(dirs) == 0 {
   101  		return nil
   102  	}
   103  
   104  	args := []string{os.Getenv("BAZEL_REAL"), "run", *gazelleLabel, "--", "-index=false"}
   105  	if mode == fastMode {
   106  		args = append(args, "-r=false")
   107  		args = append(args, dirs...)
   108  	}
   109  
   110  	cmd := exec.Command(args[0], args[1:]...)
   111  	cmd.Stdout = os.Stdout
   112  	cmd.Stderr = os.Stderr
   113  	log.Printf("running gazelle: %s\n", strings.Join(cmd.Args, " "))
   114  	return cmd.Run()
   115  }
   116  
   117  // restoreBuildFilesInRepo copies BUILD.in and BUILD.bazel.in files and
   118  // copies them to BUILD and BUILD.bazel.
   119  func restoreBuildFilesInRepo() {
   120  	err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
   121  		if err != nil {
   122  			log.Print(err)
   123  			return nil
   124  		}
   125  		restoreBuildFilesInDir(path)
   126  		return nil
   127  	})
   128  	if err != nil {
   129  		log.Print(err)
   130  	}
   131  }
   132  
   133  func restoreBuildFilesInDir(dir string) {
   134  	for _, base := range []string{"BUILD.bazel", "BUILD"} {
   135  		inPath := filepath.Join(dir, base+".in")
   136  		_, err := os.Stat(inPath)
   137  		if err != nil {
   138  			continue
   139  		}
   140  		if err := restoreFile(filepath.Join(dir, base+".in"), filepath.Join(dir, base)); err != nil {
   141  			log.Print(err)
   142  		}
   143  	}
   144  }
   145  
   146  func restoreFile(src, dest string) (err error) {
   147  	r, err := os.Open(src)
   148  	if err != nil {
   149  		return err
   150  	}
   151  	defer r.Close()
   152  
   153  	w, err := os.Create(dest)
   154  	if err != nil {
   155  		return err
   156  	}
   157  	defer func() {
   158  		if cerr := w.Close(); err == nil && cerr != nil {
   159  			err = cerr
   160  		}
   161  	}()
   162  
   163  	_, err = fmt.Fprintf(w, `# This file was generated from %s
   164  # by %s
   165  # DO NOT EDIT
   166  
   167  `, filepath.Base(src), programName)
   168  	if err != nil {
   169  		return err
   170  	}
   171  
   172  	_, err = io.Copy(w, r)
   173  	return err
   174  }