blob: 3f57f9c3286a4536b14b917a5f14826a77560f7e [file] [log] [blame]
Jiri Simsad7616c92015-03-24 23:44:30 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Matt Rosencrantzbca49812015-03-01 21:32:54 -08005// The following enables go generate to generate the doc.go file.
Jiri Simsa32f76fb2015-04-07 15:39:23 -07006//go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go .
Matt Rosencrantzbca49812015-03-01 21:32:54 -08007
8package main
9
10import (
11 "bytes"
12 "fmt"
13 "io/ioutil"
14 "log"
15 "os"
16 "path/filepath"
17 "strings"
18
19 "v.io/v23/vdlroot/vdltool"
20 "v.io/x/lib/cmdline"
21 "v.io/x/lib/textutil"
22 "v.io/x/ref/lib/vdl/build"
23 "v.io/x/ref/lib/vdl/codegen/golang"
24 "v.io/x/ref/lib/vdl/codegen/java"
25 "v.io/x/ref/lib/vdl/codegen/javascript"
26 "v.io/x/ref/lib/vdl/compile"
27 "v.io/x/ref/lib/vdl/vdlutil"
28)
29
30func init() {
31 log.SetFlags(log.Lshortfile | log.Ltime | log.Lmicroseconds)
32}
33
34func main() {
35 os.Exit(cmdVDL.Main())
36}
37
38func checkErrors(errs *vdlutil.Errors) error {
39 if errs.IsEmpty() {
40 return nil
41 }
42 return fmt.Errorf(`
43%s (run with "vdl -v" for verbose logging or "vdl help" for help)`, errs)
44}
45
46// runHelper returns a function that generates a sorted list of transitive
47// targets, and calls the supplied run function.
48func runHelper(run func(targets []*build.Package, env *compile.Env)) func(cmd *cmdline.Command, args []string) error {
49 return func(cmd *cmdline.Command, args []string) error {
50 if flagVerbose {
51 vdlutil.SetVerbose()
52 }
53 if len(args) == 0 {
54 // If the user doesn't specify any targets, the cwd is implied.
55 args = append(args, ".")
56 }
57 env := compile.NewEnv(flagMaxErrors)
58 env.DisallowPathQualifiers()
59 mode := build.UnknownPathIsError
60 if flagIgnoreUnknown {
61 mode = build.UnknownPathIsIgnored
62 }
63 var opts build.Opts
64 opts.Extensions = strings.Split(flagExts, ",")
65 opts.VDLConfigName = flagVDLConfig
66 targets := build.TransitivePackages(args, mode, opts, env.Errors)
67 if err := checkErrors(env.Errors); err != nil {
68 return err
69 }
70 run(targets, env)
71 return checkErrors(env.Errors)
72 }
73}
74
75var topicPackages = cmdline.Topic{
76 Name: "packages",
77 Short: "Description of package lists",
78 Long: `
79Most vdl commands apply to a list of packages:
80
81 vdl command <packages>
82
83<packages> are a list of packages to process, similar to the standard go tool.
84In its simplest form each package is an import path; e.g.
85 "v.io/x/ref/lib/vdl"
86
87A package that is an absolute path or that begins with a . or .. element is
88interpreted as a file system path, and denotes the package in that directory.
89
90A package is a pattern if it includes one or more "..." wildcards, each of which
91can match any string, including the empty string and strings containing
92slashes. Such a pattern expands to all packages found in VDLPATH with names
93matching the pattern. As a special-case, x/... matches x as well as x's
94subdirectories.
95
96The special-case "all" is a synonym for "...", and denotes all packages found
97in VDLPATH.
98
99Import path elements and file names are not allowed to begin with "." or "_";
100such paths are ignored in wildcard matches, and return errors if specified
101explicitly.
102
103 Run "vdl help vdlpath" to see docs on VDLPATH.
104 Run "go help packages" to see the standard go package docs.
105`,
106}
107
108var topicVdlPath = cmdline.Topic{
109 Name: "vdlpath",
110 Short: "Description of VDLPATH environment variable",
111 Long: `
112The VDLPATH environment variable is used to resolve import statements.
113It must be set to compile and generate vdl packages.
114
115The format is a colon-separated list of directories, where each directory must
116have a "src/" directory that holds vdl source code. The path below 'src'
117determines the import path. If VDLPATH specifies multiple directories, imports
118are resolved by picking the first directory with a matching import name.
119
120An example:
121
122 VDPATH=/home/user/vdlA:/home/user/vdlB
123
124 /home/user/vdlA/
125 src/
126 foo/ (import "foo" refers here)
127 foo1.vdl
128 /home/user/vdlB/
129 src/
130 foo/ (this package is ignored)
131 foo2.vdl
132 bar/
133 baz/ (import "bar/baz" refers here)
134 baz.vdl
135`,
136}
137
138var topicVdlRoot = cmdline.Topic{
139 Name: "vdlroot",
140 Short: "Description of VDLROOT environment variable",
141 Long: `
142The VDLROOT environment variable is similar to VDLPATH, but instead of pointing
143to multiple user source directories, it points at a single source directory
144containing the standard vdl packages.
145
146Setting VDLROOT is optional.
147
Jiri Simsa32f76fb2015-04-07 15:39:23 -0700148If VDLROOT is empty, we try to construct it out of the V23_ROOT environment
149variable. It is an error if both VDLROOT and V23_ROOT are empty.
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800150`,
151}
152
153var topicVdlConfig = cmdline.Topic{
154 Name: "vdl.config",
155 Short: "Description of vdl.config files",
156 Long: `
157Each vdl source package P may contain an optional file "vdl.config" within the P
158directory. This file specifies additional configuration for the vdl tool.
159
160The format of this file is described by the vdltool.Config type in the "vdltool"
161standard package, located at VDLROOT/vdltool/config.vdl.
162
163If the file does not exist, we use the zero value of vdl.Config.
164`,
165}
166
167const pkgArgName = "<packages>"
168const pkgArgLong = `
169<packages> are a list of packages to process, similar to the standard go tool.
170For more information, run "vdl help packages".
171`
172
173var cmdCompile = &cmdline.Command{
174 Run: runHelper(runCompile),
175 Name: "compile",
176 Short: "Compile packages and dependencies, but don't generate code",
177 Long: `
178Compile compiles packages and their transitive dependencies, but does not
179generate code. This is useful to sanity-check that your VDL files are valid.
180`,
181 ArgsName: pkgArgName,
182 ArgsLong: pkgArgLong,
183}
184
185var cmdGenerate = &cmdline.Command{
186 Run: runHelper(runGenerate),
187 Name: "generate",
188 Short: "Compile packages and dependencies, and generate code",
189 Long: `
190Generate compiles packages and their transitive dependencies, and generates code
191in the specified languages.
192`,
193 ArgsName: pkgArgName,
194 ArgsLong: pkgArgLong,
195}
196
197var cmdAudit = &cmdline.Command{
198 Run: runHelper(runAudit),
199 Name: "audit",
200 Short: "Check if any packages are stale and need generation",
201 Long: `
202Audit runs the same logic as generate, but doesn't write out generated files.
203Returns a 0 exit code if all packages are up-to-date, otherwise returns a
204non-0 exit code indicating some packages need generation.
205`,
206 ArgsName: pkgArgName,
207 ArgsLong: pkgArgLong,
208}
209
210var cmdList = &cmdline.Command{
211 Run: runHelper(runList),
212 Name: "list",
213 Short: "List package and dependency info in transitive order",
214 Long: `
215List returns information about packages and their transitive dependencies, in
216transitive order. This is the same order the generate and compile commands use
217for processing. If "vdl list A" is run and A depends on B, which depends on C,
218the returned order will be C, B, A. If multiple packages are specified the
219ordering is over all combined dependencies.
220
221Reminder: cyclic dependencies between packages are not allowed. Cyclic
222dependencies between VDL files within the same package are also not allowed.
223This is more strict than regular Go; it makes it easier to generate code for
224other languages like C++.
225`,
226 ArgsName: pkgArgName,
227 ArgsLong: pkgArgLong,
228}
229
Bogdan Capritadc81f5f2015-03-18 10:08:03 -0700230var genLangAll = genLangs(vdltool.GenLanguageAll[:])
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800231
232type genLangs []vdltool.GenLanguage
233
234func (gls genLangs) String() string {
235 var ret string
236 for i, gl := range gls {
237 if i > 0 {
238 ret += ","
239 }
240 ret += gl.String()
241 }
242 return ret
243}
244
245func (gls *genLangs) Set(value string) error {
246 // If the flag is repeated on the cmdline it is overridden. Duplicates within
247 // the comma separated list are ignored, and retain their original ordering.
248 *gls = genLangs{}
249 seen := make(map[vdltool.GenLanguage]bool)
250 for _, str := range strings.Split(value, ",") {
251 gl, err := vdltool.GenLanguageFromString(str)
252 if err != nil {
253 return err
254 }
255 if !seen[gl] {
256 seen[gl] = true
257 *gls = append(*gls, gl)
258 }
259 }
260 return nil
261}
262
263// genOutDir has three modes:
264// 1) If dir is non-empty, we use it as the out dir.
265// 2) If rules is non-empty, we translate using the xlate rules.
266// 3) If everything is empty, we generate in-place.
267type genOutDir struct {
268 dir string
269 rules xlateRules
270}
271
272// xlateSrcDst specifies a translation rule, where src must match the suffix of
273// the path just before the package path, and dst is the replacement for src.
274// If dst is the special string "SKIP" we'll skip generation of packages
275// matching the src.
276type xlateSrcDst struct {
277 src, dst string
278}
279
280// xlateRules specifies a collection of translation rules.
281type xlateRules []xlateSrcDst
282
283func (x *xlateRules) String() (ret string) {
284 for _, srcdst := range *x {
285 if len(ret) > 0 {
286 ret += ","
287 }
288 ret += srcdst.src + "->" + srcdst.dst
289 }
290 return
291}
292
293func (x *xlateRules) Set(value string) error {
294 for _, rule := range strings.Split(value, ",") {
295 srcdst := strings.Split(rule, "->")
296 if len(srcdst) != 2 {
297 return fmt.Errorf("invalid out dir xlate rule %q (not src->dst format)", rule)
298 }
299 *x = append(*x, xlateSrcDst{srcdst[0], srcdst[1]})
300 }
301 return nil
302}
303
304func (x *genOutDir) String() string {
305 if x.dir != "" {
306 return x.dir
307 }
308 return x.rules.String()
309}
310
311func (x *genOutDir) Set(value string) error {
312 if strings.Contains(value, "->") {
313 x.dir = ""
314 return x.rules.Set(value)
315 }
316 x.dir = value
317 return nil
318}
319
320var (
321 // Common flags for the tool itself, applicable to all commands.
322 flagVerbose bool
323 flagMaxErrors int
324 flagExts string
325 flagVDLConfig string
326 flagIgnoreUnknown bool
327
328 // Options for each command.
329 optCompileStatus bool
330 optGenStatus bool
331 optGenGoOutDir = genOutDir{}
332 optGenJavaOutDir = genOutDir{
333 rules: xlateRules{
334 {"go/src", "java/src/vdl/java"},
335 },
336 }
337 optGenJavascriptOutDir = genOutDir{
338 rules: xlateRules{
339 {"release/go/src", "release/javascript/core/src"},
340 {"roadmap/go/src", "release/javascript/core/src"},
341 {"third_party/go/src", "SKIP"},
342 {"tools/go/src", "SKIP"},
343 // TODO(toddw): Skip vdlroot javascript generation for now.
344 {"release/go/src/v.io/v23/vdlroot", "SKIP"},
345 },
346 }
347 optGenJavaOutPkg = xlateRules{
348 {"v.io", "io/v"},
349 }
350 optPathToJSCore string
351 // TODO(bjornick): Add javascript to the default gen langs.
352 optGenLangs = genLangs{vdltool.GenLanguageGo, vdltool.GenLanguageJava}
353)
354
355// Root returns the root command for the VDL tool.
356var cmdVDL = &cmdline.Command{
357 Name: "vdl",
Todd Wang6ed3b6c2015-04-08 14:37:04 -0700358 Short: "manages Vanadium Definition Language source code",
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800359 Long: `
Todd Wang6ed3b6c2015-04-08 14:37:04 -0700360Command vdl manages Vanadium Definition Language source code. It's similar to
361the go tool used for managing Go source code.
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800362`,
363 Children: []*cmdline.Command{cmdGenerate, cmdCompile, cmdAudit, cmdList},
364 Topics: []cmdline.Topic{topicPackages, topicVdlPath, topicVdlRoot, topicVdlConfig},
365}
366
367func init() {
368 // Common flags for the tool itself, applicable to all commands.
369 cmdVDL.Flags.BoolVar(&flagVerbose, "v", false, "Turn on verbose logging.")
Suharsh Sivakumar6901a2e2015-04-02 11:39:19 -0700370 cmdVDL.Flags.IntVar(&flagMaxErrors, "max-errors", -1, "Stop processing after this many errors, or -1 for unlimited.")
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800371 cmdVDL.Flags.StringVar(&flagExts, "exts", ".vdl", "Comma-separated list of valid VDL file name extensions.")
372 cmdVDL.Flags.StringVar(&flagVDLConfig, "vdl.config", "vdl.config", "Basename of the optional per-package config file.")
373 cmdVDL.Flags.BoolVar(&flagIgnoreUnknown, "ignore_unknown", false, "Ignore unknown packages provided on the command line.")
374
375 // Options for compile.
376 cmdCompile.Flags.BoolVar(&optCompileStatus, "status", true, "Show package names while we compile")
377
378 // Options for generate.
379 cmdGenerate.Flags.Var(&optGenLangs, "lang", "Comma-separated list of languages to generate, currently supporting "+genLangAll.String())
380 cmdGenerate.Flags.BoolVar(&optGenStatus, "status", true, "Show package names as they are updated")
381 // TODO(toddw): Move out_dir configuration into vdl.config, and provide a
382 // generic override mechanism for vdl.config.
Suharsh Sivakumar6901a2e2015-04-02 11:39:19 -0700383 cmdGenerate.Flags.Var(&optGenGoOutDir, "go-out-dir", `
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800384Go output directory. There are three modes:
385 "" : Generate output in-place in the source tree
386 "dir" : Generate output rooted at dir
387 "src->dst[,s2->d2...]" : Generate output using translation rules
388Assume your source tree is organized as follows:
389 VDLPATH=/home/vdl
Suharsh Sivakumar8646ba62015-03-18 15:22:28 -0700390 /home/vdl/src/test_base/base1.vdl
391 /home/vdl/src/test_base/base2.vdl
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800392Here's example output under the different modes:
Suharsh Sivakumar6901a2e2015-04-02 11:39:19 -0700393 --go-out-dir=""
Suharsh Sivakumar8646ba62015-03-18 15:22:28 -0700394 /home/vdl/src/test_base/base1.vdl.go
395 /home/vdl/src/test_base/base2.vdl.go
Suharsh Sivakumar6901a2e2015-04-02 11:39:19 -0700396 --go-out-dir="/tmp/foo"
Suharsh Sivakumar8646ba62015-03-18 15:22:28 -0700397 /tmp/foo/test_base/base1.vdl.go
398 /tmp/foo/test_base/base2.vdl.go
Suharsh Sivakumar6901a2e2015-04-02 11:39:19 -0700399 --go-out-dir="vdl/src->foo/bar/src"
Suharsh Sivakumar8646ba62015-03-18 15:22:28 -0700400 /home/foo/bar/src/test_base/base1.vdl.go
401 /home/foo/bar/src/test_base/base2.vdl.go
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800402When the src->dst form is used, src must match the suffix of the path just
403before the package path, and dst is the replacement for src. Use commas to
404separate multiple rules; the first rule matching src is used. The special dst
405SKIP indicates matching packages are skipped.`)
Suharsh Sivakumar6901a2e2015-04-02 11:39:19 -0700406 cmdGenerate.Flags.Var(&optGenJavaOutDir, "java-out-dir",
407 "Same semantics as --go-out-dir but applies to java code generation.")
408 cmdGenerate.Flags.Var(&optGenJavaOutPkg, "java-out-pkg", `
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800409Java output package translation rules. Must be of the form:
410 "src->dst[,s2->d2...]"
411If a VDL package has a prefix src, the prefix will be replaced with dst. Use
412commas to separate multiple rules; the first rule matching src is used, and if
413there are no matching rules, the package remains unchanged. The special dst
414SKIP indicates matching packages are skipped.`)
Suharsh Sivakumar6901a2e2015-04-02 11:39:19 -0700415 cmdGenerate.Flags.Var(&optGenJavascriptOutDir, "js-out-dir",
416 "Same semantics as --go-out-dir but applies to js code generation.")
417 cmdGenerate.Flags.StringVar(&optPathToJSCore, "js-relative-path-to-core", "",
418 "If set, this is the relative path from js-out-dir to the root of the JS core")
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800419
420 // Options for audit are identical to generate.
421 cmdAudit.Flags = cmdGenerate.Flags
422}
423
424func runCompile(targets []*build.Package, env *compile.Env) {
425 for _, target := range targets {
426 pkg := build.BuildPackage(target, env)
427 if pkg != nil && optCompileStatus {
428 fmt.Println(pkg.Path)
429 }
430 }
431}
432
433func runGenerate(targets []*build.Package, env *compile.Env) {
434 gen(false, targets, env)
435}
436
437func runAudit(targets []*build.Package, env *compile.Env) {
438 if gen(true, targets, env) && env.Errors.IsEmpty() {
439 // Some packages are stale, and there were no errors; return an arbitrary
440 // non-0 exit code. Errors are handled in runHelper, as usual.
441 os.Exit(10)
442 }
443}
444
445func shouldGenerate(config vdltool.Config, lang vdltool.GenLanguage) bool {
446 // If config.GenLanguages is empty, all languages are allowed to be generated.
447 _, ok := config.GenLanguages[lang]
448 return len(config.GenLanguages) == 0 || ok
449}
450
451// gen generates the given targets with env. If audit is true, only checks
452// whether any packages are stale; otherwise files will actually be written out.
453// Returns true if any packages are stale.
454func gen(audit bool, targets []*build.Package, env *compile.Env) bool {
455 anychanged := false
456 for _, target := range targets {
457 pkg := build.BuildPackage(target, env)
458 if pkg == nil {
459 // Stop at the first package that fails to compile.
460 if env.Errors.IsEmpty() {
461 env.Errors.Errorf("%s: internal error (compiled into nil package)", target.Path)
462 }
463 return true
464 }
465 // TODO(toddw): Skip code generation if the semantic contents of the
466 // generated file haven't changed.
467 pkgchanged := false
468 for _, gl := range optGenLangs {
469 switch gl {
470 case vdltool.GenLanguageGo:
471 if !shouldGenerate(pkg.Config, vdltool.GenLanguageGo) {
472 continue
473 }
474 dir, err := xlateOutDir(target.Dir, target.GenPath, optGenGoOutDir, pkg.GenPath)
Suharsh Sivakumar6901a2e2015-04-02 11:39:19 -0700475 if handleErrorOrSkip("--go-out-dir", err, env) {
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800476 continue
477 }
478 for _, file := range pkg.Files {
479 data := golang.Generate(file, env)
480 if writeFile(audit, data, dir, file.BaseName+".go", env) {
481 pkgchanged = true
482 }
483 }
484 case vdltool.GenLanguageJava:
485 if !shouldGenerate(pkg.Config, vdltool.GenLanguageJava) {
486 continue
487 }
488 pkgPath, err := xlatePkgPath(pkg.GenPath, optGenJavaOutPkg)
Suharsh Sivakumar6901a2e2015-04-02 11:39:19 -0700489 if handleErrorOrSkip("--java-out-pkg", err, env) {
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800490 continue
491 }
492 dir, err := xlateOutDir(target.Dir, target.GenPath, optGenJavaOutDir, pkgPath)
Suharsh Sivakumar6901a2e2015-04-02 11:39:19 -0700493 if handleErrorOrSkip("--java-out-dir", err, env) {
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800494 continue
495 }
496 java.SetPkgPathXlator(func(pkgPath string) string {
497 result, _ := xlatePkgPath(pkgPath, optGenJavaOutPkg)
498 return result
499 })
500 for _, file := range java.Generate(pkg, env) {
501 fileDir := filepath.Join(dir, file.Dir)
502 if writeFile(audit, file.Data, fileDir, file.Name, env) {
503 pkgchanged = true
504 }
505 }
506 case vdltool.GenLanguageJavascript:
507 if !shouldGenerate(pkg.Config, vdltool.GenLanguageJavascript) {
508 continue
509 }
510 dir, err := xlateOutDir(target.Dir, target.GenPath, optGenJavascriptOutDir, pkg.GenPath)
Suharsh Sivakumar6901a2e2015-04-02 11:39:19 -0700511 if handleErrorOrSkip("--js-out-dir", err, env) {
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800512 continue
513 }
514 path := func(importPath string) string {
515 prefix := filepath.Clean(target.Dir[0 : len(target.Dir)-len(target.GenPath)])
516 pkgDir := filepath.Join(prefix, filepath.FromSlash(importPath))
517 fullDir, err := xlateOutDir(pkgDir, importPath, optGenJavascriptOutDir, importPath)
518 if err != nil {
519 panic(err)
520 }
521 cleanPath, err := filepath.Rel(dir, fullDir)
522 if err != nil {
523 panic(err)
524 }
525 return cleanPath
526 }
527 data := javascript.Generate(pkg, env, path, optPathToJSCore)
528 if writeFile(audit, data, dir, "index.js", env) {
529 pkgchanged = true
530 }
531 default:
532 env.Errors.Errorf("Generating code for language %v isn't supported", gl)
533 }
534 }
535 if pkgchanged {
536 anychanged = true
537 if optGenStatus {
538 fmt.Println(pkg.Path)
539 }
540 }
541 }
542 return anychanged
543}
544
545// writeFile writes data into the standard location for file, using the given
546// suffix. Errors are reported via env. Returns true iff the file doesn't
547// already exist with the given data.
548func writeFile(audit bool, data []byte, dirName, baseName string, env *compile.Env) bool {
549 dstName := filepath.Join(dirName, baseName)
550 // Don't change anything if old and new are the same.
551 if oldData, err := ioutil.ReadFile(dstName); err == nil && bytes.Equal(oldData, data) {
552 return false
553 }
554 if !audit {
555 // Create containing directory, if it doesn't already exist.
556 if err := os.MkdirAll(dirName, os.FileMode(0777)); err != nil {
557 env.Errors.Errorf("Couldn't create directory %s: %v", dirName, err)
558 return true
559 }
560 if err := ioutil.WriteFile(dstName, data, os.FileMode(0666)); err != nil {
561 env.Errors.Errorf("Couldn't write file %s: %v", dstName, err)
562 return true
563 }
564 }
565 return true
566}
567
568func handleErrorOrSkip(prefix string, err error, env *compile.Env) bool {
569 if err != nil {
570 if err != errSkip {
571 env.Errors.Errorf("%s error: %v", prefix, err)
572 }
573 return true
574 }
575 return false
576}
577
578var errSkip = fmt.Errorf("SKIP")
579
580func xlateOutDir(dir, path string, outdir genOutDir, outPkgPath string) (string, error) {
581 path = filepath.FromSlash(path)
582 outPkgPath = filepath.FromSlash(outPkgPath)
583 // Strip package path from the directory.
584 if !strings.HasSuffix(dir, path) {
585 return "", fmt.Errorf("package dir %q doesn't end with package path %q", dir, path)
586 }
587 dir = filepath.Clean(dir[:len(dir)-len(path)])
588
589 switch {
590 case outdir.dir != "":
591 return filepath.Join(outdir.dir, outPkgPath), nil
592 case len(outdir.rules) == 0:
593 return filepath.Join(dir, outPkgPath), nil
594 }
595 // Try translation rules in order.
596 for _, xlate := range outdir.rules {
597 d := dir
598 if !strings.HasSuffix(d, xlate.src) {
599 continue
600 }
601 if xlate.dst == "SKIP" {
602 return "", errSkip
603 }
604 d = filepath.Clean(d[:len(d)-len(xlate.src)])
605 return filepath.Join(d, xlate.dst, outPkgPath), nil
606 }
607 return "", fmt.Errorf("package prefix %q doesn't match translation rules %q", dir, outdir)
608}
609
610func xlatePkgPath(pkgPath string, rules xlateRules) (string, error) {
611 for _, xlate := range rules {
612 if !strings.HasPrefix(pkgPath, xlate.src) {
613 continue
614 }
615 if xlate.dst == "SKIP" {
616 return pkgPath, errSkip
617 }
618 return xlate.dst + pkgPath[len(xlate.src):], nil
619 }
620 return pkgPath, nil
621}
622
623func runList(targets []*build.Package, env *compile.Env) {
624 for tx, target := range targets {
625 num := fmt.Sprintf("%d", tx)
626 fmt.Printf("%s %s\n", num, strings.Repeat("=", termWidth()-len(num)-1))
627 fmt.Printf("Name: %v\n", target.Name)
628 fmt.Printf("Config: %+v\n", target.Config)
629 fmt.Printf("Path: %v\n", target.Path)
630 fmt.Printf("GenPath: %v\n", target.GenPath)
631 fmt.Printf("Dir: %v\n", target.Dir)
632 if len(target.BaseFileNames) > 0 {
633 fmt.Print("Files:\n")
634 for _, file := range target.BaseFileNames {
635 fmt.Printf(" %v\n", file)
636 }
637 }
638 }
639}
640
641func termWidth() int {
642 if _, width, err := textutil.TerminalSize(); err == nil && width > 0 {
643 return width
644 }
645 return 80 // have a reasonable default
646}