1// Copyright 2017 The Wuffs Authors. 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// https://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 17// wuffs is a tool for managing Wuffs source code. 18package main 19 20import ( 21 "bytes" 22 "flag" 23 "fmt" 24 "io/ioutil" 25 "os" 26 "path/filepath" 27 "sort" 28 "strings" 29 30 "github.com/google/wuffs/lang/wuffsroot" 31) 32 33var commands = []struct { 34 name string 35 do func(wuffsRoot string, args []string) error 36}{ 37 {"bench", doBench}, 38 {"gen", doGen}, 39 {"genlib", doGenlib}, 40 {"test", doTest}, 41} 42 43func usage() { 44 fmt.Fprintf(os.Stderr, `Wuffs is a tool for managing Wuffs source code. 45 46Usage: 47 48 wuffs command [arguments] 49 50The commands are: 51 52 bench benchmark packages 53 gen generate code for packages and dependencies 54 genlib generate software libraries 55 test test packages 56`) 57} 58 59func main() { 60 if err := main1(); err != nil { 61 os.Stderr.WriteString(err.Error() + "\n") 62 os.Exit(1) 63 } 64} 65 66func main1() error { 67 flag.Usage = usage 68 flag.Parse() 69 70 wuffsRoot, err := wuffsroot.Value() 71 if err != nil { 72 return err 73 } 74 if args := flag.Args(); len(args) > 0 { 75 for _, c := range commands { 76 if args[0] == c.name { 77 return c.do(wuffsRoot, args[1:]) 78 } 79 } 80 } 81 usage() 82 os.Exit(1) 83 return nil 84} 85 86func findFiles(qualDirname string, suffix string) (filenames []string, err error) { 87 filenames, err = findFiles1(nil, qualDirname, suffix) 88 if err != nil { 89 return nil, err 90 } 91 sort.Strings(filenames) 92 return filenames, nil 93} 94 95func findFiles1(dstQF []string, qualDirname string, suffix string) (qualFilenames []string, err error) { 96 dstQF, relDirnames, err := appendDir(dstQF, qualDirname, suffix, true) 97 if err != nil { 98 return nil, err 99 } 100 for _, d := range relDirnames { 101 dstQF, err = findFiles1(dstQF, filepath.Join(qualDirname, d), suffix) 102 if err != nil { 103 return nil, err 104 } 105 } 106 return dstQF, nil 107} 108 109func listDir(qualDirname string, suffix string, returnSubdirs bool) (qualFilenames []string, relDirnames []string, err error) { 110 qualFilenames, relDirnames, err = appendDir(nil, qualDirname, suffix, returnSubdirs) 111 if err != nil { 112 return nil, nil, err 113 } 114 sort.Strings(qualFilenames) 115 sort.Strings(relDirnames) 116 return qualFilenames, relDirnames, nil 117} 118 119func appendDir(dstQF []string, qualDirname string, suffix string, returnSubdirs bool) (qualFilenames []string, relDirnames []string, err error) { 120 f, err := os.Open(qualDirname) 121 if err != nil { 122 return nil, nil, err 123 } 124 defer f.Close() 125 126 infos, err := f.Readdir(-1) 127 if err != nil { 128 return nil, nil, err 129 } 130 for _, o := range infos { 131 name := o.Name() 132 if o.IsDir() { 133 if returnSubdirs { 134 relDirnames = append(relDirnames, name) 135 } 136 } else if strings.HasSuffix(name, suffix) { 137 dstQF = append(dstQF, filepath.Join(qualDirname, name)) 138 } 139 } 140 141 return dstQF, relDirnames, nil 142} 143 144func writeFile(filename string, contents []byte) error { 145 if existing, err := ioutil.ReadFile(filename); err == nil && bytes.Equal(existing, contents) { 146 fmt.Println("gen unchanged: ", filename) 147 return nil 148 } 149 if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil { 150 return err 151 } 152 if err := ioutil.WriteFile(filename, contents, 0644); err != nil { 153 return err 154 } 155 fmt.Println("gen wrote: ", filename) 156 return nil 157} 158 159const ( 160 langsDefault = "c" 161 langsUsage = `comma-separated list of target languages (file extensions), e.g. "c,go,rs"` 162 163 skipgenDefault = false 164 skipgenUsage = `whether to skip automatically generating code when testing` 165 166 skipgendepsDefault = false 167 skipgendepsUsage = `whether to skip automatically generating packages' dependencies` 168) 169 170func parseLangs(commaSeparated string) ([]string, error) { 171 ret := []string(nil) 172 for _, s := range strings.Split(commaSeparated, ",") { 173 if !validName(s) { 174 return nil, fmt.Errorf(`invalid lang %q, not in [a-z0-9]+`, s) 175 } 176 ret = append(ret, s) 177 } 178 return ret, nil 179} 180 181func validName(s string) bool { 182 if len(s) == 0 { 183 return false 184 } 185 for _, c := range s { 186 if (c < '0' || '9' < c) && (c < 'a' || 'z' < c) { 187 return false 188 } 189 } 190 return true 191} 192