1// Copyright 2022 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package report 16 17import ( 18 "context" 19 "errors" 20 "fmt" 21 "io/fs" 22 "path/filepath" 23 24 "tools/treble/build/report/app" 25) 26 27// Find all binary executables under the given directory along with the number 28// of symlinks 29// 30func binaryExecutables(ctx context.Context, dir string, recursive bool) ([]string, int, error) { 31 var files []string 32 numSymLinks := 0 33 err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { 34 if err != nil { 35 return err 36 } 37 if !d.IsDir() { 38 if info, err := d.Info(); err == nil { 39 if info.Mode()&0111 != 0 { 40 files = append(files, path) 41 } 42 if d.Type()&fs.ModeSymlink != 0 { 43 numSymLinks++ 44 } 45 } 46 } else { 47 if !recursive { 48 if path != dir { 49 return filepath.SkipDir 50 } 51 } 52 } 53 return nil 54 }) 55 56 return files, numSymLinks, err 57} 58 59// Resolve the manifest 60func (rtx *Context) ResolveProjectMap(ctx context.Context, manifest string, upstreamBranch string) { 61 if rtx.Info == nil { 62 rtx.Info = resolveProjectMap(ctx, rtx, manifest, true, upstreamBranch) 63 } 64} 65 66// Find host tools 67func ResolveHostTools(ctx context.Context, hostToolPath string) (*app.HostReport, error) { 68 out := &app.HostReport{Path: hostToolPath} 69 out.Targets, out.SymLinks, _ = binaryExecutables(ctx, hostToolPath, true) 70 return out, nil 71} 72 73// Run reports 74 75// 76// Run report request 77// 78// Setup routines to: 79// - resolve the manifest projects 80// - resolve build queries 81// 82// Once the manifest projects have been resolved the build 83// queries can be fully resolved 84// 85func RunReport(ctx context.Context, rtx *Context, req *app.ReportRequest) (*app.Report, error) { 86 inChan, targetCh := targetResolvers(ctx, rtx) 87 go func() { 88 for i, _ := range req.Targets { 89 inChan <- req.Targets[i] 90 } 91 close(inChan) 92 }() 93 94 // Resolve the build inputs into build target projects 95 buildTargetChan := resolveBuildInputs(ctx, rtx, targetCh) 96 97 out := &app.Report{Targets: make(map[string]*app.BuildTarget)} 98 for bt := range buildTargetChan { 99 out.Targets[bt.Name] = bt 100 } 101 102 return out, nil 103} 104 105// Resolve commit into git commit info 106func ResolveCommit(ctx context.Context, rtx *Context, commit *app.ProjectCommit) (*app.GitCommit, []string, error) { 107 if proj, exists := rtx.Info.ProjMap[commit.Project]; exists { 108 info, err := rtx.Project.CommitInfo(ctx, proj.GitProj, commit.Revision) 109 files := []string{} 110 if err == nil { 111 for _, f := range info.Files { 112 if f.Type != app.GitFileRemoved { 113 files = append(files, filepath.Join(proj.GitProj.RepoDir, f.Filename)) 114 } 115 } 116 } 117 return info, files, err 118 } 119 return nil, nil, errors.New(fmt.Sprintf("Unknown project %s", commit.Project)) 120 121} 122 123// Run query report based on the input request. 124// 125// For each input file query the target and 126// create a set of the inputs and outputs associated 127// with all the input files. 128// 129// 130func RunQuery(ctx context.Context, rtx *Context, req *app.QueryRequest) (*app.QueryResponse, error) { 131 inChan, queryCh := queryResolvers(ctx, rtx) 132 133 go func() { 134 // Convert source files to outputs 135 for _, target := range req.Files { 136 inChan <- target 137 } 138 close(inChan) 139 }() 140 141 inFiles := make(map[string]bool) 142 outFiles := make(map[string]bool) 143 unknownSrcFiles := make(map[string]bool) 144 for result := range queryCh { 145 if result.error { 146 unknownSrcFiles[result.source] = true 147 } else { 148 for _, outFile := range result.query.Outputs { 149 outFiles[outFile] = true 150 } 151 for _, inFile := range result.query.Inputs { 152 inFiles[inFile] = true 153 } 154 155 } 156 } 157 158 out := &app.QueryResponse{} 159 for k, _ := range outFiles { 160 out.OutputFiles = append(out.OutputFiles, k) 161 } 162 for k, _ := range inFiles { 163 out.InputFiles = append(out.InputFiles, k) 164 } 165 for k, _ := range unknownSrcFiles { 166 out.UnknownFiles = append(out.UnknownFiles, k) 167 } 168 169 return out, nil 170} 171 172// Get paths 173func RunPaths(ctx context.Context, rtx *Context, target string, singlePath bool, files []string) []*app.BuildPath { 174 out := []*app.BuildPath{} 175 inChan, pathCh := pathsResolvers(ctx, rtx, target, singlePath) 176 // Convert source files to outputs 177 go func() { 178 for _, f := range files { 179 inChan <- f 180 } 181 close(inChan) 182 }() 183 184 for result := range pathCh { 185 if !result.error { 186 out = append(out, result.path) 187 } 188 } 189 return out 190 191} 192