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