| // Copyright 2022 Google LLC |
| // |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package exporter |
| |
| import ( |
| "fmt" |
| "io" |
| |
| "go.skia.org/infra/go/skerr" |
| "go.skia.org/infra/go/util" |
| "go.skia.org/skia/bazel/exporter/build_proto/build" |
| ) |
| |
| // cmakeWorkspace represents the entire state of a CMake project. |
| type cmakeWorkspace struct { |
| // Map Bazel rule names to cmakeRule instances. Holding pointer |
| // values as the rules are mutable. |
| rules map[string]*cmakeRule |
| } |
| |
| // writeState tracks the state of an in-progress write of the workspace. |
| type writeState struct { |
| writtenRules []string // All cmakeRule's written during write. |
| } |
| |
| // newCMakeWorkspace will create a new CMake workspace object. |
| func newCMakeWorkspace() *cmakeWorkspace { |
| return &cmakeWorkspace{rules: map[string]*cmakeRule{}} |
| } |
| |
| // Determine if a rule has been written. |
| func (s *writeState) isRuleWritten(ruleName string) bool { |
| return util.In(ruleName, s.writtenRules) |
| } |
| |
| // setRuleWritten will mark the rule as having been written. |
| func (s *writeState) setRuleWritten(ruleName string) { |
| s.writtenRules = append(s.writtenRules, ruleName) |
| } |
| |
| // getRule will return the CMake wrapper for a Bazel rule given the |
| // rule name. Will return nil if there is no corresponding rule. |
| func (w *cmakeWorkspace) getRule(name string) *cmakeRule { |
| return w.rules[name] |
| } |
| |
| // createRule will create (if necessary) a new CMake rule object |
| // for the given rule name. |
| func (w *cmakeWorkspace) createRule(rule *build.Rule) *cmakeRule { |
| if r := w.getRule(rule.GetName()); r != nil { |
| return r |
| } |
| r := newCMakeRule(rule) |
| w.rules[rule.GetName()] = r |
| return r |
| } |
| |
| // writeRule will write the given rule to the writer. |
| // It will first write all dependent rules so that they appear |
| // in the CMake project file before the rule that depends on them. |
| func (w *cmakeWorkspace) writeRule(writer io.Writer, r *cmakeRule, state *writeState) (int, error) { |
| nb := 0 |
| if !r.hasSrcs() { |
| return 0, nil |
| } |
| // First write all dependencies because CMake does not support forward references. |
| for _, name := range r.deps { |
| dep := w.getRule(name) |
| if dep == nil { |
| return 0, skerr.Fmt(`cannot find rule %q`, name) |
| } |
| n, err := w.writeRule(writer, dep, state) |
| if err != nil { |
| return nb, skerr.Wrap(err) |
| } |
| nb += n |
| } |
| if state.isRuleWritten(r.getName()) { |
| return nb, nil |
| } |
| num, err := fmt.Fprintln(writer) |
| if err != nil { |
| return nb, skerr.Wrap(err) |
| } |
| nb += num |
| num, err = r.write(writer) |
| if err != nil { |
| return nb, skerr.Wrap(err) |
| } |
| state.setRuleWritten(r.getName()) |
| nb += num |
| return nb, nil |
| } |
| |
| // Write this workspace using the given writer. |
| func (w *cmakeWorkspace) write(writer io.Writer) (int, error) { |
| var state writeState |
| nb := 0 |
| for _, r := range w.rules { |
| num, err := w.writeRule(writer, r, &state) |
| if err != nil { |
| return nb, skerr.Wrap(err) |
| } |
| nb += num |
| } |
| return nb, nil |
| } |