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