• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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	"fmt"
20	"os"
21	"path/filepath"
22	"strings"
23	"sync"
24
25	"tools/treble/build/report/app"
26)
27
28//
29// Repo and project related functions
30//
31type project struct {
32	Name    string          // Name
33	GitProj *app.GitProject // Git project data
34}
35
36var unknownProject = &project{Name: "unknown", GitProj: &app.GitProject{}}
37
38// Convert repo project to project with source files and revision
39// information
40func resolveProject(ctx context.Context, repoProj *app.RepoProject, remote *app.RepoRemote, proj ProjectDependencies, getFiles bool, upstreamBranch string) *project {
41
42	path := repoProj.Path
43	if path == "" {
44		path = repoProj.Name
45	}
46	gitDir := ""
47	if strings.HasPrefix(path, "overlays/") {
48		// Assume two levels of overlay path (overlay/XYZ)
49		path = strings.Join(strings.Split(path, "/")[2:], "/")
50		// The overlays .git symbolic links are not mapped correctly
51		// into the jails.   Resolve them here, inside the nsjail the
52		// absolute path for all git repos will be in the form of
53		// /src/.git/
54		symlink, _ := os.Readlink(filepath.Join(path, ".git"))
55		parts := strings.Split(symlink, "/")
56		repostart := 0
57		for ; repostart < len(parts); repostart++ {
58			if parts[repostart] != ".." {
59				if repostart > 1 {
60					repostart--
61					parts[repostart] = "/src"
62				}
63				break
64			}
65		}
66		gitDir = filepath.Join(parts[repostart:]...)
67
68	}
69	gitProj, err := proj.Project(ctx, path, gitDir, remote.Name, repoProj.Revision)
70	if err != nil {
71		return nil
72	}
73	out := &project{Name: repoProj.Name, GitProj: gitProj}
74	if getFiles {
75		_ = proj.PopulateFiles(ctx, gitProj, upstreamBranch)
76	}
77	return out
78}
79
80// Get the build file for a given filename, this is a two step lookup.
81// First find the project associated with the file via the file cache,
82// then resolve the file via the project found.
83//
84// Most files will be relative paths from the repo workspace
85func lookupProjectFile(ctx context.Context, rtx *Context, filename string) (*project, *app.GitTreeObj) {
86	if proj, exists := rtx.Info.FileCache[filename]; exists {
87		repoName := (filename)[len(proj.GitProj.RepoDir)+1:]
88		if gitObj, exists := proj.GitProj.Files[repoName]; exists {
89			return proj, gitObj
90		}
91		return proj, nil
92	} else {
93		// Try resolving any symlinks
94		if realpath, err := filepath.EvalSymlinks(filename); err == nil {
95			if realpath != filename {
96				return lookupProjectFile(ctx, rtx, realpath)
97			}
98		}
99
100		if strings.HasPrefix(filename, rtx.RepoBase) {
101			// Some dependencies pick up the full path try stripping out
102			relpath := (filename)[len(rtx.RepoBase):]
103			return lookupProjectFile(ctx, rtx, relpath)
104		}
105	}
106	return unknownProject, &app.GitTreeObj{Filename: filename, Sha: ""}
107}
108
109// Create a mapping of projects from the input source manifest
110func resolveProjectMap(ctx context.Context, rtx *Context, manifestFile string, getFiles bool, upstreamBranch string) *ProjectInfo {
111	// Parse the manifest file
112	manifest, err := rtx.Repo.Manifest(manifestFile)
113	if err != nil {
114		return nil
115	}
116	info := &ProjectInfo{}
117	// Create map of remotes
118	remotes := make(map[string]*app.RepoRemote)
119	var defRemotePtr *app.RepoRemote
120	for i, _ := range manifest.Remotes {
121		remotes[manifest.Remotes[i].Name] = &manifest.Remotes[i]
122	}
123
124	defRemotePtr, exists := remotes[manifest.Default.Remote]
125	if !exists {
126		fmt.Printf("Failed to find default remote")
127	}
128	info.FileCache = make(map[string]*project)
129	info.ProjMap = make(map[string]*project)
130
131	var wg sync.WaitGroup
132	projChan := make(chan *project)
133	repoChan := make(chan *app.RepoProject)
134
135	for i := 0; i < rtx.WorkerCount; i++ {
136		wg.Add(1)
137		go func() {
138			for repoProj := range repoChan {
139				remotePtr := defRemotePtr
140				if manifest.Projects[i].Remote != nil {
141					remotePtr = remotes[*manifest.Projects[i].Remote]
142				}
143				proj := resolveProject(ctx, repoProj, remotePtr, rtx.Project, getFiles, upstreamBranch)
144				if proj != nil {
145					projChan <- proj
146				} else {
147					projChan <- &project{Name: repoProj.Name}
148				}
149			}
150			wg.Done()
151		}()
152	}
153	go func() {
154		wg.Wait()
155		close(projChan)
156	}()
157	go func() {
158		for i, _ := range manifest.Projects {
159			repoChan <- &manifest.Projects[i]
160		}
161		close(repoChan)
162	}()
163	for r := range projChan {
164		if r.GitProj != nil {
165			info.ProjMap[r.Name] = r
166			if len(r.GitProj.Files) > 0 {
167				for n := range r.GitProj.Files {
168					info.FileCache[filepath.Join(r.GitProj.RepoDir, n)] = r
169				}
170
171			}
172
173		} else {
174			fmt.Printf("Failed to resolve %s\n", r.Name)
175		}
176	}
177	return info
178}
179