// Copyright 2022 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package report import ( "context" "fmt" "os" "path/filepath" "strings" "sync" "tools/treble/build/report/app" ) // // Repo and project related functions // type project struct { Name string // Name GitProj *app.GitProject // Git project data } var unknownProject = &project{Name: "unknown", GitProj: &app.GitProject{}} // Convert repo project to project with source files and revision // information func resolveProject(ctx context.Context, repoProj *app.RepoProject, remote *app.RepoRemote, proj ProjectDependencies, getFiles bool, upstreamBranch string) *project { path := repoProj.Path if path == "" { path = repoProj.Name } gitDir := "" if strings.HasPrefix(path, "overlays/") { // Assume two levels of overlay path (overlay/XYZ) path = strings.Join(strings.Split(path, "/")[2:], "/") // The overlays .git symbolic links are not mapped correctly // into the jails. Resolve them here, inside the nsjail the // absolute path for all git repos will be in the form of // /src/.git/ symlink, _ := os.Readlink(filepath.Join(path, ".git")) parts := strings.Split(symlink, "/") repostart := 0 for ; repostart < len(parts); repostart++ { if parts[repostart] != ".." { if repostart > 1 { repostart-- parts[repostart] = "/src" } break } } gitDir = filepath.Join(parts[repostart:]...) } gitProj, err := proj.Project(ctx, path, gitDir, remote.Name, repoProj.Revision) if err != nil { return nil } out := &project{Name: repoProj.Name, GitProj: gitProj} if getFiles { _ = proj.PopulateFiles(ctx, gitProj, upstreamBranch) } return out } // Get the build file for a given filename, this is a two step lookup. // First find the project associated with the file via the file cache, // then resolve the file via the project found. // // Most files will be relative paths from the repo workspace func lookupProjectFile(ctx context.Context, rtx *Context, filename string) (*project, *app.GitTreeObj) { if proj, exists := rtx.Info.FileCache[filename]; exists { repoName := (filename)[len(proj.GitProj.RepoDir)+1:] if gitObj, exists := proj.GitProj.Files[repoName]; exists { return proj, gitObj } return proj, nil } else { // Try resolving any symlinks if realpath, err := filepath.EvalSymlinks(filename); err == nil { if realpath != filename { return lookupProjectFile(ctx, rtx, realpath) } } if strings.HasPrefix(filename, rtx.RepoBase) { // Some dependencies pick up the full path try stripping out relpath := (filename)[len(rtx.RepoBase):] return lookupProjectFile(ctx, rtx, relpath) } } return unknownProject, &app.GitTreeObj{Filename: filename, Sha: ""} } // Create a mapping of projects from the input source manifest func resolveProjectMap(ctx context.Context, rtx *Context, manifestFile string, getFiles bool, upstreamBranch string) *ProjectInfo { // Parse the manifest file manifest, err := rtx.Repo.Manifest(manifestFile) if err != nil { return nil } info := &ProjectInfo{} // Create map of remotes remotes := make(map[string]*app.RepoRemote) var defRemotePtr *app.RepoRemote for i, _ := range manifest.Remotes { remotes[manifest.Remotes[i].Name] = &manifest.Remotes[i] } defRemotePtr, exists := remotes[manifest.Default.Remote] if !exists { fmt.Printf("Failed to find default remote") } info.FileCache = make(map[string]*project) info.ProjMap = make(map[string]*project) var wg sync.WaitGroup projChan := make(chan *project) repoChan := make(chan *app.RepoProject) for i := 0; i < rtx.WorkerCount; i++ { wg.Add(1) go func() { for repoProj := range repoChan { remotePtr := defRemotePtr if manifest.Projects[i].Remote != nil { remotePtr = remotes[*manifest.Projects[i].Remote] } proj := resolveProject(ctx, repoProj, remotePtr, rtx.Project, getFiles, upstreamBranch) if proj != nil { projChan <- proj } else { projChan <- &project{Name: repoProj.Name} } } wg.Done() }() } go func() { wg.Wait() close(projChan) }() go func() { for i, _ := range manifest.Projects { repoChan <- &manifest.Projects[i] } close(repoChan) }() for r := range projChan { if r.GitProj != nil { info.ProjMap[r.Name] = r if len(r.GitProj.Files) > 0 { for n := range r.GitProj.Files { info.FileCache[filepath.Join(r.GitProj.RepoDir, n)] = r } } } else { fmt.Printf("Failed to resolve %s\n", r.Name) } } return info }