xref: /aosp_15_r20/build/soong/android/all_teams.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1package android
2
3import (
4	"path"
5	"path/filepath"
6
7	"android/soong/android/team_proto"
8
9	"google.golang.org/protobuf/proto"
10)
11
12const ownershipDirectory = "ownership"
13const allTeamsFile = "all_teams.pb"
14
15func AllTeamsFactory() Singleton {
16	return &allTeamsSingleton{}
17}
18
19func init() {
20	registerAllTeamBuildComponents(InitRegistrationContext)
21}
22
23func registerAllTeamBuildComponents(ctx RegistrationContext) {
24	ctx.RegisterParallelSingletonType("all_teams", AllTeamsFactory)
25}
26
27// For each module, list the team or the bpFile the module is defined in.
28type moduleTeamAndTestInfo struct {
29	// Name field from bp file for the team
30	teamName string
31	// Blueprint file the module is located in.
32	bpFile string
33	// Is this module only used by tests.
34	testOnly bool
35	// Is this a directly testable target by running the module directly
36	// or via tradefed.
37	topLevelTestTarget bool
38	// String name indicating the module, like `java_library` for reporting.
39	kind string
40}
41
42type allTeamsSingleton struct {
43	// Path where the collected metadata is stored after successful validation.
44	outputPath OutputPath
45
46	// Map of all package modules we visit during GenerateBuildActions
47	packages map[string]packageProperties
48	// Map of all team modules we visit during GenerateBuildActions
49	teams map[string]teamProperties
50	// Keeps track of team information or bp file for each module we visit.
51	teams_for_mods map[string]moduleTeamAndTestInfo
52}
53
54// See if there is a package module for the given bpFilePath with a team defined, if so return the team.
55// If not ascend up to the parent directory and do the same.
56func (t *allTeamsSingleton) lookupDefaultTeam(bpFilePath string) (teamProperties, bool) {
57	// return the Default_team listed in the package if is there.
58	if p, ok := t.packages[bpFilePath]; ok {
59		if defaultTeam := p.Default_team; defaultTeam != nil {
60			return t.teams[*defaultTeam], true
61		}
62	}
63	// Strip a directory and go up.
64	// Does android/paths.go basePath,SourcePath help?
65	current, base := filepath.Split(bpFilePath)
66	current = filepath.Clean(current) // removes trailing slash, convert "" -> "."
67	parent, _ := filepath.Split(current)
68	if current == "." {
69		return teamProperties{}, false
70	}
71	return t.lookupDefaultTeam(filepath.Join(parent, base))
72}
73
74// Visit all modules and collect all teams and use WriteFileRuleVerbatim
75// to write it out.
76func (t *allTeamsSingleton) GenerateBuildActions(ctx SingletonContext) {
77	t.packages = make(map[string]packageProperties)
78	t.teams = make(map[string]teamProperties)
79	t.teams_for_mods = make(map[string]moduleTeamAndTestInfo)
80
81	ctx.VisitAllModules(func(module Module) {
82		bpFile := ctx.BlueprintFile(module)
83
84		// Package Modules and Team Modules are stored in a map so we can look them up by name for
85		// modules without a team.
86		if pack, ok := module.(*packageModule); ok {
87			// Packages don't have names, use the blueprint file as the key. we can't get qualifiedModuleId in t context.
88			pkgKey := bpFile
89			t.packages[pkgKey] = pack.properties
90			return
91		}
92		if team, ok := module.(*teamModule); ok {
93			t.teams[team.Name()] = team.properties
94			return
95		}
96
97		testModInfo := TestModuleInformation{}
98		if tmi, ok := OtherModuleProvider(ctx, module, TestOnlyProviderKey); ok {
99			testModInfo = tmi
100		}
101
102		// Some modules, like java_test_host don't set the provider when the module isn't enabled:
103		//                                                test_only, top_level
104		//     AVFHostTestCases{os:linux_glibc,arch:common} {true true}
105		//     AVFHostTestCases{os:windows,arch:common} {false false}
106		// Generally variant information of true override false or unset.
107		if testModInfo.TestOnly == false {
108			if prevValue, exists := t.teams_for_mods[module.Name()]; exists {
109				if prevValue.testOnly == true {
110					return
111				}
112			}
113		}
114		entry := moduleTeamAndTestInfo{
115			bpFile:             bpFile,
116			testOnly:           testModInfo.TestOnly,
117			topLevelTestTarget: testModInfo.TopLevelTarget,
118			kind:               ctx.ModuleType(module),
119			teamName:           module.base().Team(),
120		}
121		t.teams_for_mods[module.Name()] = entry
122
123	})
124
125	// Visit all modules again and lookup the team name in the package or parent package if the team
126	// isn't assignged at the module level.
127	allTeams := t.lookupTeamForAllModules()
128
129	t.outputPath = PathForOutput(ctx, ownershipDirectory, allTeamsFile)
130	data, err := proto.Marshal(allTeams)
131	if err != nil {
132		ctx.Errorf("Unable to marshal team data. %s", err)
133	}
134
135	WriteFileRuleVerbatim(ctx, t.outputPath, string(data))
136	ctx.Phony("all_teams", t.outputPath)
137}
138
139func (t *allTeamsSingleton) MakeVars(ctx MakeVarsContext) {
140	ctx.DistForGoal("all_teams", t.outputPath)
141}
142
143// Visit every (non-package, non-team) module and write out a proto containing
144// either the declared team data for that module or the package default team data for that module.
145func (t *allTeamsSingleton) lookupTeamForAllModules() *team_proto.AllTeams {
146	teamsProto := make([]*team_proto.Team, len(t.teams_for_mods))
147	for i, moduleName := range SortedKeys(t.teams_for_mods) {
148		m, _ := t.teams_for_mods[moduleName]
149		teamName := m.teamName
150		var teamProperties teamProperties
151		found := false
152		if teamName != "" {
153			teamProperties, found = t.teams[teamName]
154		} else {
155			teamProperties, found = t.lookupDefaultTeam(m.bpFile)
156		}
157		// Deal with one blueprint file including another by looking up the default
158		// in the main Android.bp rather than one listed with "build = [My.bp]"
159		if !found {
160			teamProperties, found = t.lookupDefaultTeam(path.Join(path.Dir(m.bpFile), "Android.bp"))
161		}
162
163		trendy_team_id := ""
164		if found {
165			trendy_team_id = *teamProperties.Trendy_team_id
166		}
167
168		teamData := new(team_proto.Team)
169		*teamData = team_proto.Team{
170			TargetName:     proto.String(moduleName),
171			Path:           proto.String(m.bpFile),
172			TestOnly:       proto.Bool(m.testOnly),
173			TopLevelTarget: proto.Bool(m.topLevelTestTarget),
174			Kind:           proto.String(m.kind),
175		}
176		if trendy_team_id != "" {
177			teamData.TrendyTeamId = proto.String(trendy_team_id)
178		} else {
179			// Clients rely on the TrendyTeamId optional field not being set.
180		}
181		teamsProto[i] = teamData
182	}
183	return &team_proto.AllTeams{Teams: teamsProto}
184}
185