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 }