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