github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/dev/devcam/devcam.go (about)

     1  /*
     2  Copyright 2013 The Camlistore Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8       http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"flag"
    21  	"fmt"
    22  	"io"
    23  	"log"
    24  	"os"
    25  	"os/exec"
    26  	"os/signal"
    27  	pathpkg "path"
    28  	"path/filepath"
    29  	"strconv"
    30  	"strings"
    31  	"syscall"
    32  
    33  	"camlistore.org/pkg/cmdmain"
    34  )
    35  
    36  var (
    37  	noBuild  = flag.Bool("nobuild", false, "do not rebuild anything")
    38  	race     = flag.Bool("race", false, "build with race detector")
    39  	quiet, _ = strconv.ParseBool(os.Getenv("CAMLI_QUIET"))
    40  	// Whether to build the subcommand with sqlite support. This only
    41  	// concerns the server subcommand, which sets it to serverCmd.sqlite.
    42  	withSqlite bool
    43  )
    44  
    45  // The path to the Camlistore source tree. Any devcam command
    46  // should be run from there.
    47  var camliSrcRoot string
    48  
    49  // sysExec is set to syscall.Exec on platforms that support it.
    50  var sysExec func(argv0 string, argv []string, envv []string) (err error)
    51  
    52  // runExec execs bin. If the platform doesn't support exec, it runs it and waits
    53  // for it to finish.
    54  func runExec(bin string, args []string, env *Env) error {
    55  	if sysExec != nil {
    56  		sysExec(bin, append([]string{filepath.Base(bin)}, args...), env.Flat())
    57  	}
    58  
    59  	cmd := exec.Command(bin, args...)
    60  	cmd.Env = env.Flat()
    61  	cmd.Stdout = os.Stdout
    62  	cmd.Stderr = os.Stderr
    63  	if err := cmd.Start(); err != nil {
    64  		return fmt.Errorf("Could not run %v: %v", bin, err)
    65  	}
    66  	go handleSignals(cmd.Process)
    67  	return cmd.Wait()
    68  }
    69  
    70  // cpDir copies the contents of src dir into dst dir.
    71  // filter is a list of file suffixes to skip. ex: ".go"
    72  func cpDir(src, dst string, filter []string) error {
    73  	return filepath.Walk(src, func(fullpath string, fi os.FileInfo, err error) error {
    74  		if err != nil {
    75  			return err
    76  		}
    77  		for _, suffix := range filter {
    78  			if strings.HasSuffix(fi.Name(), suffix) {
    79  				return nil
    80  			}
    81  		}
    82  		suffix, err := filepath.Rel(src, fullpath)
    83  		if err != nil {
    84  			return fmt.Errorf("Failed to find Rel(%q, %q): %v", src, fullpath, err)
    85  		}
    86  		if fi.IsDir() {
    87  			return nil
    88  		}
    89  		return cpFile(fullpath, filepath.Join(dst, suffix))
    90  	})
    91  }
    92  
    93  func cpFile(src, dst string) error {
    94  	sfi, err := os.Stat(src)
    95  	if err != nil {
    96  		return err
    97  	}
    98  	if !sfi.Mode().IsRegular() {
    99  		return fmt.Errorf("cpFile can't deal with non-regular file %s", src)
   100  	}
   101  
   102  	dstDir := filepath.Dir(dst)
   103  	if err := os.MkdirAll(dstDir, 0755); err != nil {
   104  		return err
   105  	}
   106  
   107  	df, err := os.Create(dst)
   108  	if err != nil {
   109  		return err
   110  	}
   111  	sf, err := os.Open(src)
   112  	if err != nil {
   113  		return err
   114  	}
   115  	defer sf.Close()
   116  
   117  	n, err := io.Copy(df, sf)
   118  	if err == nil && n != sfi.Size() {
   119  		err = fmt.Errorf("copied wrong size for %s -> %s: copied %d; want %d", src, dst, n, sfi.Size())
   120  	}
   121  	cerr := df.Close()
   122  	if err == nil {
   123  		err = cerr
   124  	}
   125  	return err
   126  }
   127  
   128  func handleSignals(camliProc *os.Process) {
   129  	c := make(chan os.Signal, 1)
   130  	signal.Notify(c, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)
   131  	for {
   132  		sig := <-c
   133  		sysSig, ok := sig.(syscall.Signal)
   134  		if !ok {
   135  			log.Fatal("Not a unix signal")
   136  		}
   137  		switch sysSig {
   138  		case syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT:
   139  			log.Printf("Received %v signal, terminating.", sig)
   140  			err := camliProc.Kill()
   141  			if err != nil {
   142  				log.Fatalf("Failed to kill child: %v ", err)
   143  			}
   144  		default:
   145  			log.Fatal("Received another signal, should not happen.")
   146  		}
   147  	}
   148  }
   149  
   150  func checkCamliSrcRoot() {
   151  	args := flag.Args()
   152  	if len(args) > 0 && args[0] == "review" {
   153  		// exception for devcam review, which does its own check.
   154  		return
   155  	}
   156  	if _, err := os.Stat("make.go"); err != nil {
   157  		if !os.IsNotExist(err) {
   158  			log.Fatalf("Could not stat make.go: %v", err)
   159  		}
   160  		log.Fatal("./make.go not found; devcam needs to be run from the Camlistore source tree root.")
   161  	}
   162  	cwd, err := os.Getwd()
   163  	if err != nil {
   164  		log.Fatal(err)
   165  	}
   166  	camliSrcRoot = cwd
   167  }
   168  
   169  // Build builds the camlistore command at the given path from the source tree root.
   170  func build(path string) error {
   171  	if v, _ := strconv.ParseBool(os.Getenv("CAMLI_FAST_DEV")); v {
   172  		// Demo mode. See dev/demo.sh.
   173  		return nil
   174  	}
   175  	_, cmdName := filepath.Split(path)
   176  	target := pathpkg.Join("camlistore.org", filepath.ToSlash(path))
   177  	binPath := filepath.Join("bin", cmdName)
   178  	var modtime int64
   179  	fi, err := os.Stat(binPath)
   180  	if err != nil {
   181  		if !os.IsNotExist(err) {
   182  			return fmt.Errorf("Could not stat %v: %v", binPath, err)
   183  		}
   184  	} else {
   185  		modtime = fi.ModTime().Unix()
   186  	}
   187  	args := []string{
   188  		"run", "make.go",
   189  		"--quiet",
   190  		"--race=" + strconv.FormatBool(*race),
   191  		"--embed_static=false",
   192  		"--sqlite=" + strconv.FormatBool(withSqlite),
   193  		fmt.Sprintf("--if_mods_since=%d", modtime),
   194  		"--targets=" + target,
   195  	}
   196  	cmd := exec.Command("go", args...)
   197  	cmd.Stdout = os.Stdout
   198  	cmd.Stderr = os.Stderr
   199  	if err := cmd.Run(); err != nil {
   200  		return fmt.Errorf("Error building %v: %v", target, err)
   201  	}
   202  	return nil
   203  }
   204  
   205  func main() {
   206  	cmdmain.CheckCwd = checkCamliSrcRoot
   207  	// TODO(mpl): usage error is not really correct for devcam.
   208  	// See if I can reimplement it while still using cmdmain.Main().
   209  	cmdmain.Main()
   210  }