github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/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  	if _, err := os.Stat("make.go"); err != nil {
   152  		if !os.IsNotExist(err) {
   153  			log.Fatalf("Could not stat make.go: %v", err)
   154  		}
   155  		log.Fatal("./make.go not found; devcam needs to be run from the Camlistore source tree root.")
   156  	}
   157  	cwd, err := os.Getwd()
   158  	if err != nil {
   159  		log.Fatal(err)
   160  	}
   161  	camliSrcRoot = cwd
   162  }
   163  
   164  // Build builds the camlistore command at the given path from the source tree root.
   165  func build(path string) error {
   166  	if v, _ := strconv.ParseBool(os.Getenv("CAMLI_FAST_DEV")); v {
   167  		// Demo mode. See dev/demo.sh.
   168  		return nil
   169  	}
   170  	_, cmdName := filepath.Split(path)
   171  	target := pathpkg.Join("camlistore.org", filepath.ToSlash(path))
   172  	binPath := filepath.Join("bin", cmdName)
   173  	var modtime int64
   174  	fi, err := os.Stat(binPath)
   175  	if err != nil {
   176  		if !os.IsNotExist(err) {
   177  			return fmt.Errorf("Could not stat %v: %v", binPath, err)
   178  		}
   179  	} else {
   180  		modtime = fi.ModTime().Unix()
   181  	}
   182  	args := []string{
   183  		"run", "make.go",
   184  		"--quiet",
   185  		"--race=" + strconv.FormatBool(*race),
   186  		"--embed_static=false",
   187  		"--sqlite=" + strconv.FormatBool(withSqlite),
   188  		fmt.Sprintf("--if_mods_since=%d", modtime),
   189  		"--targets=" + target,
   190  	}
   191  	cmd := exec.Command("go", args...)
   192  	cmd.Stdout = os.Stdout
   193  	cmd.Stderr = os.Stderr
   194  	if err := cmd.Run(); err != nil {
   195  		return fmt.Errorf("Error building %v: %v", target, err)
   196  	}
   197  	return nil
   198  }
   199  
   200  func main() {
   201  	checkCamliSrcRoot()
   202  	// TODO(mpl): usage error is not really correct for devcam.
   203  	// See if I can reimplement it while still using cmdmain.Main().
   204  	cmdmain.Main()
   205  }