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 }