blob: 5e9229ac5d9c50030f2636cce5b95aa8c30818b4 [file] [log] [blame]
// 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
}