• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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