| // Copyright (C) 2015 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package maker |
| |
| import ( |
| "flag" |
| "fmt" |
| "log" |
| "os" |
| "runtime" |
| "sort" |
| "strings" |
| ) |
| |
| const ( |
| // Default is the name of the entity that is built if none are supplied on the |
| // command line. |
| Default = "default" |
| ) |
| |
| var ( |
| prepares = []func(){} |
| ) |
| |
| // Register a new graph building function with the maker system. |
| // The function will be invoked during Run to add entities and steps to the |
| // build graph. |
| func Register(f func()) { |
| prepares = append(prepares, f) |
| } |
| |
| type stringSetFlag []string |
| |
| func (f *stringSetFlag) String() string { return strings.Join(f.Strings(), ":") } |
| func (f *stringSetFlag) Strings() []string { return ([]string)(*f) } |
| |
| func (f *stringSetFlag) Set(value string) error { |
| *f = append(*f, value) |
| return nil |
| } |
| |
| // Run should be invoked once from main. |
| // It parses the command line, builds the graph, and then performs the required |
| // action. |
| func Run() { |
| // Get the configuration |
| verbose := flag.Int("v", 1, "Verbose mode") |
| do := flag.String("do", "make", "The action to perform, one of make, show or clean.") |
| early := flag.Bool("early", false, "Stops the build at the first error, also disables parallel builds.") |
| threads := flag.Int("threads", runtime.NumCPU(), "Set number of OS threads to use. 0 disables parallel builds.") |
| var disables stringSetFlag |
| flag.Var(&disables, "disable", "Disable a specific node") |
| flag.Parse() |
| if *threads > 0 { |
| runtime.GOMAXPROCS(*threads) |
| } else { |
| Config.DisableParallel = true |
| } |
| if *early { |
| Config.StopOnError = true |
| Config.DisableParallel = true |
| } |
| Config.Verbose = *verbose |
| // Build the entity graph |
| for _, f := range prepares { |
| f() |
| } |
| // Force disabled status from the command line |
| for _, d := range disables.Strings() { |
| if s := Creator(d); s != nil { |
| s.Disable() |
| } |
| } |
| // Prepare the active path |
| targets := flag.Args() |
| meta := List("") |
| for _, match := range targets { |
| e := FindPathEntity(match) |
| if e != nil { |
| if Creator(e) != nil { |
| meta.DependsOn(e) |
| } |
| } else { |
| // not an exact entry, so fuzzy search time |
| entities := FindEntities(match) |
| if len(entities) == 0 { |
| log.Fatalf("no entities match for %q", match) |
| } |
| for _, e := range entities { |
| if Creator(e) != nil { |
| meta.DependsOn(e) |
| } |
| } |
| } |
| } |
| // Perform the requested action |
| switch *do { |
| case "make": |
| if len(meta.inputs) == 0 { |
| meta.DependsOn(Default) |
| } |
| meta.start() |
| <-meta.done |
| if Errors.Failed() { |
| fmt.Printf("Failed: %s\n", Errors.Last()) |
| os.Exit(1) |
| } else { |
| fmt.Printf("Succeeded\n") |
| } |
| case "show": |
| if len(meta.inputs) == 0 { |
| fmt.Printf("targets available are:\n") |
| strings := sort.StringSlice{} |
| for _, e := range entities { |
| if IsVirtual(e) { |
| strings = append(strings, e.Name()) |
| } |
| } |
| strings.Sort() |
| for _, n := range strings { |
| fmt.Printf(" %s\n", n) |
| } |
| } else { |
| fmt.Printf("active dependancy graph is:\n") |
| dumper{}.dump(meta, nil) |
| } |
| case "clean": |
| log.Fatalf("Clean not yet supported") |
| default: |
| log.Fatalf("Unknown action %q", *do) |
| } |
| } |
| |
| type dumper map[*Step]struct{} |
| |
| func (d dumper) dump(s *Step, seen []*Step) { |
| if s == nil { |
| fmt.Println() |
| return |
| } |
| fmt.Printf(" [%d]", len(s.inputs)) |
| if s.disabled { |
| fmt.Println(" - disabled") |
| return |
| } |
| if _, done := d[s]; done { |
| fmt.Println(" - already seen") |
| for i := range seen { |
| if seen[i] == s { |
| err := "Error: Cyclic dependency chain found:\n" |
| for i := range seen { |
| err += fmt.Sprintf(" [%d]: %v\n", i, seen[i]) |
| } |
| panic(err) |
| } |
| } |
| return |
| } |
| fmt.Println() |
| d[s] = struct{}{} |
| for i, e := range s.inputs { |
| for i := 0; i < len(seen); i++ { |
| fmt.Print(" ") |
| } |
| fmt.Printf("(%d) %s", i+1, e) |
| d.dump(Creator(e), append(seen, s)) |
| } |
| } |