1// Copyright 2022 Google LLC 2// 3// Use of this source code is governed by a BSD-style license that can be 4// found in the LICENSE file. 5 6package exporter 7 8import ( 9 "fmt" 10 "io" 11 12 "go.skia.org/infra/go/skerr" 13 "go.skia.org/infra/go/util" 14 "go.skia.org/skia/bazel/exporter/build_proto/build" 15) 16 17// cmakeWorkspace represents the entire state of a CMake project. 18type cmakeWorkspace struct { 19 // Map Bazel rule names to cmakeRule instances. Holding pointer 20 // values as the rules are mutable. 21 rules map[string]*cmakeRule 22} 23 24// writeState tracks the state of an in-progress write of the workspace. 25type writeState struct { 26 writtenRules []string // All cmakeRule's written during write. 27} 28 29// newCMakeWorkspace will create a new CMake workspace object. 30func newCMakeWorkspace() *cmakeWorkspace { 31 return &cmakeWorkspace{rules: map[string]*cmakeRule{}} 32} 33 34// Determine if a rule has been written. 35func (s *writeState) isRuleWritten(ruleName string) bool { 36 return util.In(ruleName, s.writtenRules) 37} 38 39// setRuleWritten will mark the rule as having been written. 40func (s *writeState) setRuleWritten(ruleName string) { 41 s.writtenRules = append(s.writtenRules, ruleName) 42} 43 44// getRule will return the CMake wrapper for a Bazel rule given the 45// rule name. Will return nil if there is no corresponding rule. 46func (w *cmakeWorkspace) getRule(name string) *cmakeRule { 47 return w.rules[name] 48} 49 50// createRule will create (if necessary) a new CMake rule object 51// for the given rule name. 52func (w *cmakeWorkspace) createRule(rule *build.Rule) *cmakeRule { 53 if r := w.getRule(rule.GetName()); r != nil { 54 return r 55 } 56 r := newCMakeRule(rule) 57 w.rules[rule.GetName()] = r 58 return r 59} 60 61// writeRule will write the given rule to the writer. 62// It will first write all dependent rules so that they appear 63// in the CMake project file before the rule that depends on them. 64func (w *cmakeWorkspace) writeRule(writer io.Writer, r *cmakeRule, state *writeState) (int, error) { 65 nb := 0 66 if !r.hasSrcs() { 67 return 0, nil 68 } 69 // First write all dependencies because CMake does not support forward references. 70 for _, name := range r.deps { 71 dep := w.getRule(name) 72 if dep == nil { 73 return 0, skerr.Fmt(`cannot find rule %q`, name) 74 } 75 n, err := w.writeRule(writer, dep, state) 76 if err != nil { 77 return nb, skerr.Wrap(err) 78 } 79 nb += n 80 } 81 if state.isRuleWritten(r.getName()) { 82 return nb, nil 83 } 84 num, err := fmt.Fprintln(writer) 85 if err != nil { 86 return nb, skerr.Wrap(err) 87 } 88 nb += num 89 num, err = r.write(writer) 90 if err != nil { 91 return nb, skerr.Wrap(err) 92 } 93 state.setRuleWritten(r.getName()) 94 nb += num 95 return nb, nil 96} 97 98// Write this workspace using the given writer. 99func (w *cmakeWorkspace) write(writer io.Writer) (int, error) { 100 var state writeState 101 nb := 0 102 for _, r := range w.rules { 103 num, err := w.writeRule(writer, r, &state) 104 if err != nil { 105 return nb, skerr.Wrap(err) 106 } 107 nb += num 108 } 109 return nb, nil 110} 111