aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Seymour <rseymour@google.com>2022-06-22 19:43:54 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2022-06-22 19:43:54 +0000
commit19b570ff5c93bb111b69b89edeccb7a178c3aea8 (patch)
tree4b7ce172d8a870940c0cfa3ce25eb6ac03e5873b
parentc572ee9baa080550ade3fd27451951aadce8e76c (diff)
parent08841703410cf054c5f45b2f0d04771ab3790a0e (diff)
downloadtreble-19b570ff5c93bb111b69b89edeccb7a178c3aea8.tar.gz
Use paths to determine build targets for treble_build am: ca63627bbe am: 54176d5cbb am: 0884170341
Original change: https://android-review.googlesource.com/c/platform/tools/treble/+/2132556 Change-Id: If9adf77844bc02c9cbc05433d68875b1996a8ca1 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--build/report/app/build.go27
-rw-r--r--build/report/app/git.go41
-rw-r--r--build/report/app/report.go28
-rw-r--r--build/report/cmd/Android.bp6
-rw-r--r--build/report/cmd/host.go75
-rw-r--r--build/report/cmd/main.go262
-rw-r--r--build/report/cmd/outputs.go88
-rw-r--r--build/report/cmd/paths.go64
-rw-r--r--build/report/cmd/query.go44
-rw-r--r--build/report/cmd/report.go122
-rw-r--r--build/report/local/git.go91
-rw-r--r--build/report/local/git_test.go37
-rw-r--r--build/report/local/ninja.go47
-rw-r--r--build/report/local/ninja_test.go28
-rw-r--r--build/report/report/build.go83
-rw-r--r--build/report/report/dependencies.go4
-rw-r--r--build/report/report/projects.go166
-rw-r--r--build/report/report/report_test.go57
-rw-r--r--build/report/report/run.go96
-rw-r--r--build/report/report/types.go20
20 files changed, 832 insertions, 554 deletions
diff --git a/build/report/app/build.go b/build/report/app/build.go
index fffeb95..8b78e23 100644
--- a/build/report/app/build.go
+++ b/build/report/app/build.go
@@ -39,26 +39,12 @@ type BuildPath struct {
Paths []string `json:paths"`
}
-// Build file
-type BuildFile struct {
- Name string `json:"name"` // Source file name
- Revision string `json:"revision"` // SHA revision
-}
-
-// Build project
-type BuildProject struct {
- Path string `json:"path"` // Project path
- Name string `json:"name"` // Project name
- Revision string `json:"revision"` // SHA revision of project
- Files []BuildFile `json:"files"` // Project files
-}
-
// Build target
type BuildTarget struct {
- Name string `json:"name"` // Target name
- Steps int `json:"build_steps"` // Number of steps to build target
- FileCount int `json:"files"` // Number of input files for a target
- Projects map[string]*BuildProject `json:"projects"` // Inputs projects/files of a target
+ Name string `json:"name"` // Target name
+ Steps int `json:"build_steps"` // Number of steps to build target
+ FileCount int `json:"files"` // Number of input files for a target
+ Projects map[string]*GitProject `json:"projects"` // Inputs projects/files of a target
}
// Build command result
@@ -67,3 +53,8 @@ type BuildCmdResult struct {
Output []string `json:"output"`
Success bool `json:"success"`
}
+
+// Build dependencies
+type BuildDeps struct {
+ Targets map[string][]string `json:"targets"`
+}
diff --git a/build/report/app/git.go b/build/report/app/git.go
index 9ebed29..7cd21a4 100644
--- a/build/report/app/git.go
+++ b/build/report/app/git.go
@@ -14,22 +14,31 @@
package app
+// GIT diff
+type GitDiff struct {
+ AddedLines int `json:"added_lines"`
+ DeletedLines int `json:"deleted_lines"`
+ BinaryDiff bool `json:"binary_diff"`
+}
+
// GIT tree object (files,dirs...)
type GitTreeObj struct {
- Permissions string `json:"permissions"`
- Type string `json:"type"`
- Sha string `json:"sha"`
- Filename string `json:"filename"`
+ Permissions string `json:"permissions"`
+ Type string `json:"type"`
+ Sha string `json:"sha"`
+ Filename string `json:"filename"`
+ BranchDiff *GitDiff `json:"branch_diff"`
}
// GitProject
type GitProject struct {
- WorkDir string `json:"working_dir"` // Working directory
- GitDir string `json:"git_dir"` // GIT directory
- Remote string `json:"remote"` // Remote Name
- RemoteUrl string `json:"remote_url"` // Remote URL
- Revision string `json:"revision"` // Revision (SHA)
- Files []GitTreeObj `json:"files"` // Files within the project
+ RepoDir string `json:"repo_dir"` // Relative directory within repo
+ WorkDir string `json:"working_dir"` // Working directory
+ GitDir string `json:"git_dir"` // GIT directory
+ Remote string `json:"remote"` // Remote Name
+ RemoteUrl string `json:"remote_url"` // Remote URL
+ Revision string `json:"revision"` // Revision (SHA)
+ Files map[string]*GitTreeObj `json:"files"` // Files within the project
}
type GitCommitFileType int
@@ -50,3 +59,15 @@ type GitCommit struct {
Sha string `json:"sha"`
Files []GitCommitFile `json:"files"`
}
+
+func (t GitCommitFileType) String() string {
+ switch t {
+ case GitFileModified:
+ return "M"
+ case GitFileAdded:
+ return "A"
+ case GitFileRemoved:
+ return "R"
+ }
+ return ""
+}
diff --git a/build/report/app/report.go b/build/report/app/report.go
index afe86ec..bc9c7ab 100644
--- a/build/report/app/report.go
+++ b/build/report/app/report.go
@@ -16,23 +16,19 @@ package app
// Report request structure
type ReportRequest struct {
- IncludeHostTools bool `json:"include_host_tools"` // Get target information for all host tools found
- HostToolPath string `json:"host_tool_path"` // Location of output host tools
- ManifestFile string `json:"manifest"` // Repo manifest file
- Targets []string `json:"targets"` // Targets
-}
-
-// Host tool report response data
-type HostReport struct {
- Path string `json:"path"` // Path to find host tools
- SymLinks int `json:"sym_links"` // Number of symlinks found
- Targets []*BuildTarget `json:"targets"` // Build targets for tools found
+ Targets []string `json:"targets"` // Targets
}
// Report response data
type Report struct {
- Host *HostReport `json:"host"` // Host response
- Targets []*BuildTarget `json:"targets"` // Build target data
+ Targets map[string]*BuildTarget `json:"targets"` // Build target data
+}
+
+// Host tool report response data
+type HostReport struct {
+ Path string `json:"path"` // Path to find host tools
+ SymLinks int `json:"sym_links"` // Number of symlinks found
+ Targets []string `json:"targets"` // Target for tools found
}
// Project level commit
@@ -41,12 +37,6 @@ type ProjectCommit struct {
Revision string `json:"revision"` // Revision
}
-// Project level commits
-type ProjectCommits struct {
- ManifestFile string `json:"manifest"` // Repo manifest file
- Commits []ProjectCommit `json:"commits"` // Commits to resolve
-}
-
// Query request
type QueryRequest struct {
Files []string `json:"files"` // Files to resolve
diff --git a/build/report/cmd/Android.bp b/build/report/cmd/Android.bp
index 9b53fe9..ec83692 100644
--- a/build/report/cmd/Android.bp
+++ b/build/report/cmd/Android.bp
@@ -24,10 +24,10 @@ package {
blueprint_go_binary {
name: "treble_build",
srcs: [
+ "host.go",
"main.go",
- "outputs.go",
- "report.go",
-
+ "paths.go",
+ "query.go",
],
deps: [
"treble_report_app",
diff --git a/build/report/cmd/host.go b/build/report/cmd/host.go
new file mode 100644
index 0000000..0b8e65f
--- /dev/null
+++ b/build/report/cmd/host.go
@@ -0,0 +1,75 @@
+// 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 main
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "path/filepath"
+
+ "tools/treble/build/report/report"
+)
+
+type hostReport struct {
+ toolPath string
+}
+
+// Determine host tools
+func (h *hostReport) Run(ctx context.Context, rtx *report.Context, rsp *response) error {
+ var err error
+ rsp.Host, err = report.ResolveHostTools(ctx, h.toolPath)
+ if err != nil {
+ return err
+ }
+ rsp.Targets = append(rsp.Targets, rsp.Host.Targets...)
+ return nil
+}
+
+func (h *hostReport) PrintText(w io.Writer, rsp *response, verbose bool) {
+ if rsp.Host != nil {
+ // Get the unique number of inputs
+ hostSourceFileMap := make(map[string]bool)
+ hostSourceProjectMap := make(map[string]bool)
+
+ for _, t := range rsp.Host.Targets {
+ // Find target in report
+ if bt, exists := rsp.Report.Targets[t]; exists {
+ for name, proj := range bt.Projects {
+ hostSourceProjectMap[name] = true
+ for f := range proj.Files {
+ hostSourceFileMap[filepath.Join(name, f)] = true
+ }
+ }
+ // Remove the target from being printed
+ delete(rsp.Report.Targets, t)
+ }
+ }
+
+ fmt.Fprintln(w, " Host Tools")
+ fmt.Fprintf(w, " %-20s : %s\n", "Directory", rsp.Host.Path)
+ fmt.Fprintf(w, " %-20s : %d\n", "Tools", len(rsp.Host.Targets))
+ fmt.Fprintf(w, " %-20s : %d\n", "Prebuilts", rsp.Host.SymLinks)
+ fmt.Fprintf(w, " %-20s : %d\n", "Inputs", len(hostSourceFileMap))
+ fmt.Fprintf(w, " %-20s : %d\n", "Projects", len(hostSourceProjectMap))
+
+ if verbose {
+ for proj, _ := range hostSourceProjectMap {
+ fmt.Fprintf(w, " %s\n", proj)
+ }
+ }
+ }
+
+}
diff --git a/build/report/cmd/main.go b/build/report/cmd/main.go
index 2d704b5..0264ff3 100644
--- a/build/report/cmd/main.go
+++ b/build/report/cmd/main.go
@@ -15,11 +15,15 @@
package main
import (
+ "bufio"
"context"
+ "encoding/json"
"errors"
"flag"
"fmt"
+ "io"
"log"
+ "os"
"runtime"
"strings"
@@ -28,6 +32,14 @@ import (
"tools/treble/build/report/report"
)
+type Build interface {
+ Build(ctx context.Context, target string) *app.BuildCmdResult
+}
+
+type tool interface {
+ Run(ctx context.Context, rtx *report.Context, rsp *response) error
+ PrintText(w io.Writer, rsp *response, verbose bool)
+}
type repoFlags []app.ProjectCommit
func (r *repoFlags) Set(value string) error {
@@ -53,63 +65,233 @@ func (r *repoFlags) String() string {
var (
// Common flags
- ninjaDbPtr = flag.String("ninja", local.DefNinjaDb(), "Set the .ninja file to use when building metrics")
- ninjaExcPtr = flag.String("ninja_cmd", local.DefNinjaExc(), "Set the ninja executable")
- manifestPtr = flag.String("manifest", local.DefManifest(), "Set the location of the manifest file")
- repoBasePtr = flag.String("repo_base", local.DefRepoBase(), "Set the repo base directory")
- workerCountPtr = flag.Int("worker_count", runtime.NumCPU(), "Number of worker routines")
-
- reportFlags = flag.NewFlagSet("report", flag.ExitOnError)
- outputsFlags = flag.NewFlagSet("outputs", flag.ExitOnError)
+ ninjaDbPtr = flag.String("ninja", local.DefNinjaDb(), "Set the .ninja file to use when building metrics")
+ ninjaExcPtr = flag.String("ninja_cmd", local.DefNinjaExc(), "Set the ninja executable")
+ manifestPtr = flag.String("manifest", local.DefManifest(), "Set the location of the manifest file")
+ upstreamPtr = flag.String("upstream", "", "Upstream branch to compare files against")
+ repoBasePtr = flag.String("repo_base", local.DefRepoBase(), "Set the repo base directory")
+ workerCountPtr = flag.Int("worker_count", runtime.NumCPU(), "Number of worker routines")
+ buildWorkerCountPtr = flag.Int("build_worker_count", local.MaxNinjaCliWorkers, "Number of build worker routines")
+ buildPtr = flag.Bool("build", false, "Build targets")
+ jsonPtr = flag.Bool("json", false, "Print json data")
+ verbosePtr = flag.Bool("v", false, "Print verbose text data")
+ outputPtr = flag.String("o", "", "Output to file")
+
+ hostFlags = flag.NewFlagSet("host", flag.ExitOnError)
+ queryFlags = flag.NewFlagSet("query", flag.ExitOnError)
+ pathsFlags = flag.NewFlagSet("paths", flag.ExitOnError)
)
+type commit struct {
+ Project app.ProjectCommit `json:"project"`
+ Commit *app.GitCommit `json:"commit"`
+}
+
+// Use one structure for output for now
+type response struct {
+ Commits []commit `json:"commits,omitempty"`
+ Inputs []string `json:"files,omitempty"`
+ BuildFiles []*app.BuildCmdResult `json:"build_files,omitempty"`
+ Targets []string `json:"targets,omitempty"`
+ Report *app.Report `json:"report,omitempty"`
+
+ // Subcommand data
+ Query *app.QueryResponse `json:"query,omitempty"`
+ Paths []*app.BuildPath `json:"build_paths,omitempty"`
+ Host *app.HostReport `json:"host,omitempty"`
+}
+
func main() {
ctx := context.Background()
- flag.Parse()
+ rsp := &response{}
- subCmds := strings.Join([]string{"report", "outputs"}, " ")
+ flag.Parse()
subArgs := flag.Args()
if len(subArgs) < 1 {
- log.Fatalf("Expected a sub-command. Possible sub-commands %s", subCmds)
+ // Nothing to do
+ return
}
+ defBuildTarget := "droid"
log.SetFlags(log.LstdFlags | log.Llongfile)
ninja := local.NewNinjaCli(*ninjaExcPtr, *ninjaDbPtr)
rtx := &report.Context{
- RepoBase: *repoBasePtr,
- Repo: &report.RepoMan{},
- Build: ninja,
- Project: local.NewGitCli(),
- WorkerCount: *workerCountPtr}
+ RepoBase: *repoBasePtr,
+ Repo: &report.RepoMan{},
+ Build: ninja,
+ Project: local.NewGitCli(),
+ WorkerCount: *workerCountPtr,
+ BuildWorkerCount: *buildWorkerCountPtr,
+ }
+
+ var subcommand tool
+ var commits repoFlags
switch subArgs[0] {
- case "report":
- incHostToolPtr := reportFlags.Bool("host", false, "Include host tool metrics")
- hostToolPathPtr := reportFlags.String("hostbin", local.DefHostBinPath(), "Set the output directory for host tools")
- jsonPtr := reportFlags.Bool("json", false, "Print json data")
- verbosePtr := reportFlags.Bool("v", false, "Print verbose data")
- outputPtr := reportFlags.String("o", "", "Output to file")
-
- reportFlags.Parse(subArgs[1:])
- reportExc(ctx, rtx,
- &reportArgs{incHostTools: *incHostToolPtr, hostToolPath: *hostToolPathPtr,
- manifest: *manifestPtr, jsonOut: *jsonPtr, verbose: *verbosePtr,
- outFile: *outputPtr},
- reportFlags.Args())
-
- case "outputs":
- var commits repoFlags
- outputsFlags.Var(&commits, "repo", "Repo:SHA to build")
- buildPtr := outputsFlags.Bool("build", false, "Build outputs")
- outputPtr := outputsFlags.String("o", "", "Output to file")
- outputsFlags.Parse(subArgs[1:])
- outputsExc(ctx, rtx,
- &outputArgs{manifest: *manifestPtr, build: *buildPtr, builder: ninja, outputFile: *outputPtr},
- commits, outputsFlags.Args())
+ case "host":
+ hostToolPathPtr := hostFlags.String("hostbin", local.DefHostBinPath(), "Set the output directory for host tools")
+ hostFlags.Parse(subArgs[1:])
+
+ subcommand = &hostReport{toolPath: *hostToolPathPtr}
+ rsp.Targets = hostFlags.Args()
+
+ case "query":
+ queryFlags.Var(&commits, "repo", "Repo:SHA to query")
+ queryFlags.Parse(subArgs[1:])
+ subcommand = &queryReport{}
+ rsp.Targets = queryFlags.Args()
+
+ case "paths":
+ pathsFlags.Var(&commits, "repo", "Repo:SHA to build")
+ singlePathPtr := pathsFlags.Bool("1", false, "Get single path to output target")
+ pathsFlags.Parse(subArgs[1:])
+
+ subcommand = &pathsReport{build_target: defBuildTarget, single: *singlePathPtr}
+
+ rsp.Targets = pathsFlags.Args()
default:
- log.Fatalf("Unknown sub-command <%s>. Possible sub-commands %s", subCmds)
+ rsp.Targets = subArgs
+ }
+
+ rtx.ResolveProjectMap(ctx, *manifestPtr, *upstreamPtr)
+ // Resolve any commits
+ if len(commits) > 0 {
+ log.Printf("Resolving %s", commits.String())
+ for _, c := range commits {
+ commit := commit{Project: c}
+ info, files, err := report.ResolveCommit(ctx, rtx, &c)
+ if err != nil {
+ log.Fatalf("Failed to resolve commit %s:%s", c.Project, c.Revision)
+ }
+ commit.Commit = info
+ rsp.Commits = append(rsp.Commits, commit)
+
+ // Add files to list of inputs
+ rsp.Inputs = append(rsp.Inputs, files...)
+ }
+ }
+
+ // Run any sub tools
+ if subcommand != nil {
+ if err := subcommand.Run(ctx, rtx, rsp); err != nil {
+ log.Fatal(err)
+ }
+ }
+
+ buildErrors := 0
+ if *buildPtr {
+ for _, t := range rsp.Targets {
+ log.Printf("Building %s\n", t)
+ res := ninja.Build(ctx, t)
+ log.Printf("%s\n", res.Output)
+ if res.Success != true {
+ buildErrors++
+ }
+ rsp.BuildFiles = append(rsp.BuildFiles, res)
+ }
+ }
+
+ // Generate report
+ var err error
+ log.Printf("Generating report for targets %s", rsp.Targets)
+ req := &app.ReportRequest{Targets: rsp.Targets}
+ rsp.Report, err = report.RunReport(ctx, rtx, req)
+ if err != nil {
+ log.Fatal(fmt.Sprintf("Report failure <%s>", err))
+ }
+
+ if *jsonPtr {
+ b, _ := json.MarshalIndent(rsp, "", "\t")
+ if *outputPtr == "" {
+ os.Stdout.Write(b)
+ } else {
+ os.WriteFile(*outputPtr, b, 0644)
+ }
+ } else {
+ if *outputPtr == "" {
+ printTextReport(os.Stdout, subcommand, rsp, *verbosePtr)
+ } else {
+ file, err := os.Create(*outputPtr)
+ if err != nil {
+ log.Fatalf("Failed to create output file %s (%s)", *outputPtr, err)
+ }
+ w := bufio.NewWriter(file)
+ printTextReport(w, subcommand, rsp, *verbosePtr)
+ w.Flush()
+ }
+
+ }
+
+ if buildErrors > 0 {
+ log.Fatal(fmt.Sprintf("Failed to build %d targets", buildErrors))
+ }
+}
+
+func printTextReport(w io.Writer, subcommand tool, rsp *response, verbose bool) {
+ fmt.Fprintln(w, "Metric Report")
+ if subcommand != nil {
+ subcommand.PrintText(w, rsp, verbose)
+ }
+
+ if len(rsp.Commits) > 0 {
+ fmt.Fprintln(w, "")
+ fmt.Fprintln(w, " Commit Results")
+ for _, c := range rsp.Commits {
+ fmt.Fprintf(w, " %-120s : %s\n", c.Project.Project, c.Project.Revision)
+ fmt.Fprintf(w, " SHA : %s\n", c.Commit.Sha)
+ fmt.Fprintf(w, " Files : \n")
+ for _, f := range c.Commit.Files {
+ fmt.Fprintf(w, " %s %s\n", f.Type.String(), f.Filename)
+ }
+ }
+ }
+ if len(rsp.BuildFiles) > 0 {
+ fmt.Fprintln(w, "")
+ fmt.Fprintln(w, " Build Files")
+ for _, b := range rsp.BuildFiles {
+ fmt.Fprintf(w, " %-120s : %t \n", b.Name, b.Success)
+ }
+ }
+
+ targetPrint := func(target *app.BuildTarget) {
+ fmt.Fprintf(w, " %-20s : %s\n", "Name", target.Name)
+ fmt.Fprintf(w, " %-20s : %d\n", "Build Steps", target.Steps)
+ fmt.Fprintf(w, " %-20s \n", "Inputs")
+ fmt.Fprintf(w, " %-20s : %d\n", "Files", target.FileCount)
+ fmt.Fprintf(w, " %-20s : %d\n", "Projects", len(target.Projects))
+ fmt.Fprintln(w)
+ for name, proj := range target.Projects {
+ forkCount := 0
+ for _, file := range proj.Files {
+ if file.BranchDiff != nil {
+ forkCount++
+ }
+ }
+ fmt.Fprintf(w, " %-120s : %d ", name, len(proj.Files))
+ if forkCount != 0 {
+ fmt.Fprintf(w, " (%d)\n", forkCount)
+ } else {
+ fmt.Fprintf(w, " \n")
+ }
+
+ if verbose {
+ for _, file := range proj.Files {
+ var fork string
+ if file.BranchDiff != nil {
+ fork = fmt.Sprintf("(%d+ %d-)", file.BranchDiff.AddedLines, file.BranchDiff.DeletedLines)
+ }
+ fmt.Fprintf(w, " %-20s %s\n", fork, file.Filename)
+ }
+
+ }
+ }
+
+ }
+ fmt.Fprintln(w, " Targets")
+ for _, t := range rsp.Report.Targets {
+ targetPrint(t)
}
}
diff --git a/build/report/cmd/outputs.go b/build/report/cmd/outputs.go
deleted file mode 100644
index 86d2dd6..0000000
--- a/build/report/cmd/outputs.go
+++ /dev/null
@@ -1,88 +0,0 @@
-// 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 main
-
-import (
- "context"
- "encoding/json"
- "log"
- "os"
-
- "tools/treble/build/report/app"
- "tools/treble/build/report/report"
-)
-
-type response struct {
- Commits *app.ProjectCommits `json:"commits"`
- Query *app.QueryRequest `json:"query"`
- QueryRsp *app.QueryResponse `json:"query_result"`
- BuildFiles []*app.BuildCmdResult `json:"build_files"`
-}
-
-type Build interface {
- Build(ctx context.Context, target string) *app.BuildCmdResult
-}
-type outputArgs struct {
- manifest string
- build bool
- builder Build
- outputFile string
-}
-
-func outputsExc(ctx context.Context, rtx *report.Context, args *outputArgs, commits repoFlags, files []string) {
- ret := response{}
- if len(commits) > 0 {
- log.Printf("Resolving %s", commits.String())
- ret.Commits = &app.ProjectCommits{ManifestFile: *manifestPtr, Commits: commits}
- commitFiles, err := report.ResolveCommits(ctx, rtx, ret.Commits)
- if err != nil {
- log.Fatalf("Failed to resolve commits %s", commits.String())
- }
- files = append(files, commitFiles...)
- }
-
- log.Printf("Querying files %s \n", files)
- ret.Query = &app.QueryRequest{Files: files}
- var err error
- ret.QueryRsp, err = report.RunQuery(ctx, rtx, ret.Query)
- if err != nil {
- log.Fatalf("Failed to query outputs %s\n", err)
- }
- error := false
- if args.build {
- log.Println("Filtering output files")
- buildFiles := report.RunPathFilter(ctx, rtx, "droid", ret.QueryRsp.OutputFiles)
- for _, f := range buildFiles {
- log.Printf("Building %s\n", f)
- res := args.builder.Build(ctx, f)
- log.Printf("%s\n", res.Output)
- if res.Success != true {
- error = true
- }
- ret.BuildFiles = append(ret.BuildFiles, res)
- }
- }
-
- b, _ := json.MarshalIndent(ret, "", "\t")
- if args.outputFile == "" {
- os.Stdout.Write(b)
- } else {
- os.WriteFile(args.outputFile, b, 0644)
- }
-
- if error {
- log.Fatal("Failed to build outputs")
- }
-}
diff --git a/build/report/cmd/paths.go b/build/report/cmd/paths.go
new file mode 100644
index 0000000..4c28d4f
--- /dev/null
+++ b/build/report/cmd/paths.go
@@ -0,0 +1,64 @@
+// 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 main
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "log"
+
+ "tools/treble/build/report/report"
+)
+
+type pathsReport struct {
+ build_target string // Target used to filter build request
+ single bool // Get single path
+}
+
+func (p pathsReport) Run(ctx context.Context, rtx *report.Context, rsp *response) error {
+
+ log.Printf("Resolving paths for %s (single : %v)\n", rsp.Inputs, p.single)
+ rsp.Paths = report.RunPaths(ctx, rtx, p.build_target, p.single, rsp.Inputs)
+
+ // The path is returned in an array in the form [build_target, path_stop1,...,path_stopN,source_file]
+ // Choose the closest build target (path_stopN) to the source file to build to reduce the amount that
+ // is built.
+ const buildPathIndex = 2
+ build_targets := make(map[string]bool)
+ for _, path := range rsp.Paths {
+ // Default to build closest build target
+ if len(path.Paths) > buildPathIndex {
+ build_targets[path.Paths[len(path.Paths)-buildPathIndex]] = true
+ }
+ }
+ for b := range build_targets {
+ rsp.Targets = append(rsp.Targets, b)
+ }
+
+ return nil
+}
+
+func (h *pathsReport) PrintText(w io.Writer, rsp *response, verbose bool) {
+ if len(rsp.Paths) > 0 {
+ fmt.Fprintln(w, " Paths")
+ for _, p := range rsp.Paths {
+ // Provide path from target to dependency with the
+ // path length, since target and dependency are in the
+ // path subtract them out from length
+ fmt.Fprintf(w, " %s..(%d)..%-s\n", p.Target, len(p.Paths)-2, p.Dependency)
+ }
+ }
+}
diff --git a/build/report/cmd/query.go b/build/report/cmd/query.go
new file mode 100644
index 0000000..e4c7f0f
--- /dev/null
+++ b/build/report/cmd/query.go
@@ -0,0 +1,44 @@
+// 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 main
+
+import (
+ "context"
+ "io"
+ "log"
+
+ "tools/treble/build/report/app"
+ "tools/treble/build/report/report"
+)
+
+// Command arguments
+type queryReport struct {
+}
+
+// Run query
+func (o queryReport) Run(ctx context.Context, rtx *report.Context, rsp *response) error {
+ var err error
+ log.Printf("Querying files %s\n", rsp.Inputs)
+ req := &app.QueryRequest{Files: rsp.Inputs}
+ rsp.Query, err = report.RunQuery(ctx, rtx, req)
+ if err != nil {
+ return err
+ }
+
+ return nil
+
+}
+func (h *queryReport) PrintText(w io.Writer, rsp *response, verbose bool) {
+}
diff --git a/build/report/cmd/report.go b/build/report/cmd/report.go
deleted file mode 100644
index be50814..0000000
--- a/build/report/cmd/report.go
+++ /dev/null
@@ -1,122 +0,0 @@
-// 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 main
-
-import (
- "bufio"
- "context"
- "encoding/json"
- "fmt"
- "io"
- "log"
- "os"
- "path/filepath"
-
- "tools/treble/build/report/app"
- "tools/treble/build/report/report"
-)
-
-type reportArgs struct {
- incHostTools bool
- hostToolPath string
- manifest string
- jsonOut bool
- verbose bool
- outFile string
-}
-
-func reportExc(ctx context.Context, rtx *report.Context, args *reportArgs, targets []string) {
- req := &app.ReportRequest{IncludeHostTools: args.incHostTools,
- HostToolPath: args.hostToolPath,
- ManifestFile: args.manifest,
- Targets: targets}
-
- out, err := report.RunReport(ctx, rtx, req)
-
- if err != nil {
- log.Fatalf("Failed to run report : %s\n", err)
- }
- if args.jsonOut {
- b, _ := json.MarshalIndent(out, "", "\t")
- if args.outFile == "" {
- os.Stdout.Write(b)
- } else {
- os.WriteFile(args.outFile, b, 0644)
- }
- } else {
- if args.outFile == "" {
- printTextReport(os.Stdout, out, args.verbose)
- } else {
- file, err := os.Create(args.outFile)
- if err != nil {
- log.Fatalf("Failed to create output file %s (%s)", args.outFile, err)
- }
- w := bufio.NewWriter(file)
- printTextReport(w, out, args.verbose)
- }
- }
-
-}
-
-func printTextReport(w io.Writer, report *app.Report, verbose bool) {
- fmt.Fprintln(w, "Metric Report")
- if report.Host != nil {
- // Get the unique number of inputs
- hostSourceFileMap := make(map[string]bool)
- hostSourceProjectMap := make(map[string]bool)
- for i, _ := range report.Host.Targets {
- for j, _ := range report.Host.Targets[i].Projects {
- hostSourceProjectMap[report.Host.Targets[i].Projects[j].Name] = true
- for k, _ := range report.Host.Targets[i].Projects[j].Files {
- hostSourceFileMap[filepath.Join(report.Host.Targets[i].Projects[j].Path,
- report.Host.Targets[i].Projects[j].Files[k].Name)] = true
- }
- }
- }
- fmt.Fprintln(w, " Host Tools")
- fmt.Fprintf(w, " %-20s : %s\n", "Directory", report.Host.Path)
- fmt.Fprintf(w, " %-20s : %d\n", "Tools", len(report.Host.Targets))
- fmt.Fprintf(w, " %-20s : %d\n", "Prebuilts", report.Host.SymLinks)
- fmt.Fprintf(w, " %-20s : %d\n", "Inputs", len(hostSourceFileMap))
- fmt.Fprintf(w, " %-20s : %d\n", "Projects", len(hostSourceProjectMap))
-
- if verbose {
- for proj, _ := range hostSourceProjectMap {
- fmt.Fprintf(w, " %s\n", proj)
- }
- }
- }
- if len(report.Targets) != 0 {
- fmt.Fprintln(w, " Targets")
- for i, _ := range report.Targets {
- fmt.Fprintf(w, " %-20s : %s\n", "Name", report.Targets[i].Name)
- fmt.Fprintf(w, " %-20s : %d\n", "Build Steps", report.Targets[i].Steps)
- fmt.Fprintf(w, " %-20s \n", "Inputs")
- fmt.Fprintf(w, " %-20s : %d\n", "Files", report.Targets[i].FileCount)
- fmt.Fprintf(w, " %-20s : %d\n", "Projects", len(report.Targets[i].Projects))
- fmt.Fprintln(w)
- for _, proj := range report.Targets[i].Projects {
- fmt.Fprintf(w, " %-120s : %d\n", proj.Name, len(proj.Files))
- if verbose {
- for _, file := range proj.Files {
- fmt.Fprintf(w, " %-20s : %s\n", file.Revision, file.Name)
- }
- }
- }
-
- }
-
- }
-}
diff --git a/build/report/local/git.go b/build/report/local/git.go
index 2a7dc04..2793042 100644
--- a/build/report/local/git.go
+++ b/build/report/local/git.go
@@ -24,6 +24,7 @@ import (
"context"
"errors"
"fmt"
+ "strconv"
"strings"
"time"
@@ -32,10 +33,11 @@ import (
// Separate out the executable to allow tests to override the results
type gitExec interface {
- ProjectInfo(ctx context.Context, gitDir string, workDir string) (out *bytes.Buffer, err error)
- RemoteUrl(ctx context.Context, gitDir string, workDir string, remote string) (*bytes.Buffer, error)
- Tree(ctx context.Context, gitDir string, workDir string, revision string) (*bytes.Buffer, error)
- CommitInfo(ctx context.Context, gitDir string, workDir string, revision string) (*bytes.Buffer, error)
+ ProjectInfo(ctx context.Context, gitDir, workDir string) (out *bytes.Buffer, err error)
+ RemoteUrl(ctx context.Context, gitDir, workDir, remote string) (*bytes.Buffer, error)
+ Tree(ctx context.Context, gitDir, workDir, revision string) (*bytes.Buffer, error)
+ CommitInfo(ctx context.Context, gitDir, workDir, revision string) (*bytes.Buffer, error)
+ DiffBranches(ctx context.Context, gitDir, workDir, upstream, sha string) (*bytes.Buffer, error)
}
type gitCli struct {
@@ -43,7 +45,7 @@ type gitCli struct {
}
// Create GIT project based on input parameters
-func (cli gitCli) Project(ctx context.Context, path string, gitDir string, remote string, revision string, getFiles bool) (*app.GitProject, error) {
+func (cli gitCli) Project(ctx context.Context, path, gitDir, remote, revision string) (*app.GitProject, error) {
workDir := path
// Set defaults
if remote == "" {
@@ -64,11 +66,13 @@ func (cli gitCli) Project(ctx context.Context, path string, gitDir string, remot
}
}
// Create project to use to run commands
- out := &app.GitProject{WorkDir: workDir,
+ out := &app.GitProject{
+ RepoDir: path,
+ WorkDir: workDir,
GitDir: gitDir,
Remote: remote,
Revision: revision,
- Files: []app.GitTreeObj{}}
+ Files: make(map[string]*app.GitTreeObj)}
// Remote URL
if raw, err := cli.git.RemoteUrl(ctx, gitDir, workDir, remote); err == nil {
@@ -78,18 +82,33 @@ func (cli gitCli) Project(ctx context.Context, path string, gitDir string, remot
}
}
- // all files in repo
- if getFiles {
- if raw, err := cli.git.Tree(ctx, gitDir, workDir, revision); err == nil {
- lsFiles, err := parseLsTree(raw)
- if err == nil {
- out.Files = *lsFiles
+ return out, nil
+}
+
+// Get all files in the repository if, upstream branch is provided mark which files differ from upstream
+func (cli gitCli) PopulateFiles(ctx context.Context, proj *app.GitProject, upstream string) error {
+ if raw, err := cli.git.Tree(ctx, proj.GitDir, proj.WorkDir, proj.Revision); err == nil {
+ lsFiles, err := parseLsTree(raw)
+ if err == nil {
+ for _, file := range lsFiles {
+ proj.Files[file.Filename] = file
+ }
+ }
+ if upstream != "" {
+
+ if diff, err := cli.git.DiffBranches(ctx, proj.GitDir, proj.WorkDir, upstream, proj.Revision); err == nil {
+ if diffFiles, err := parseBranchDiff(diff); err == nil {
+ for f, d := range diffFiles {
+ if file, exists := proj.Files[f]; exists {
+ file.BranchDiff = d
+ }
+ }
+ }
}
}
}
- return out, nil
-
+ return nil
}
// Get the commit information associated with the input sha
@@ -128,15 +147,36 @@ func parseRemoteUrl(data *bytes.Buffer) (url string, err error) {
}
// parse ls-tree
-func parseLsTree(data *bytes.Buffer) (*[]app.GitTreeObj, error) {
- out := &[]app.GitTreeObj{}
+func parseLsTree(data *bytes.Buffer) ([]*app.GitTreeObj, error) {
+ out := []*app.GitTreeObj{}
s := bufio.NewScanner(data)
for s.Scan() {
- obj := app.GitTreeObj{}
+ obj := &app.GitTreeObj{}
// TODO
// Filename could contain a <space> as quotepath is turned off, truncating the name here
fmt.Sscanf(s.Text(), "%s %s %s %s", &obj.Permissions, &obj.Type, &obj.Sha, &obj.Filename)
- *out = append(*out, obj)
+ out = append(out, obj)
+ }
+ return out, nil
+}
+
+// parse branch diff (diff --num-stat)
+func parseBranchDiff(data *bytes.Buffer) (map[string]*app.GitDiff, error) {
+ out := make(map[string]*app.GitDiff)
+ s := bufio.NewScanner(data)
+ for s.Scan() {
+ d := &app.GitDiff{}
+ var fname, added, deleted string
+ _, err := fmt.Sscanf(s.Text(), "%s %s %s", &added, &deleted, &fname)
+ if err == nil {
+ if added == "-" || deleted == "-" {
+ d.BinaryDiff = true
+ } else {
+ d.AddedLines, _ = strconv.Atoi(added)
+ d.DeletedLines, _ = strconv.Atoi(deleted)
+ }
+ }
+ out[fname] = d
}
return out, nil
}
@@ -184,21 +224,24 @@ func (git *gitCmd) runDirCmd(ctx context.Context, gitDir string, workDir string,
return out, nil
}
-func (git *gitCmd) ProjectInfo(ctx context.Context, gitDir string, workDir string) (*bytes.Buffer, error) {
+func (git *gitCmd) ProjectInfo(ctx context.Context, gitDir, workDir string) (*bytes.Buffer, error) {
return git.runDirCmd(ctx, gitDir, workDir, []string{"rev-parse", "--show-toplevel", "HEAD"})
}
-func (git *gitCmd) RemoteUrl(ctx context.Context, gitDir string, workDir string, remote string) (*bytes.Buffer, error) {
+func (git *gitCmd) RemoteUrl(ctx context.Context, gitDir, workDir, remote string) (*bytes.Buffer, error) {
return git.runDirCmd(ctx, gitDir, workDir, []string{"remote", "get-url", remote})
}
-func (git *gitCmd) Tree(ctx context.Context, gitDir string, workDir string, revision string) (*bytes.Buffer, error) {
+func (git *gitCmd) Tree(ctx context.Context, gitDir, workDir, revision string) (*bytes.Buffer, error) {
cmdArgs := []string{"-c", "core.quotepath=off", "ls-tree", "--full-name", revision, "-r", "-t"}
return git.runDirCmd(ctx, gitDir, workDir, cmdArgs)
}
-func (git *gitCmd) CommitInfo(ctx context.Context, gitDir string, workDir string, sha string) (*bytes.Buffer, error) {
+func (git *gitCmd) CommitInfo(ctx context.Context, gitDir, workDir, sha string) (*bytes.Buffer, error) {
cmdArgs := []string{"diff-tree", "-r", "-m", "--name-status", "--root", sha}
return git.runDirCmd(ctx, gitDir, workDir, cmdArgs)
}
-
+func (git *gitCmd) DiffBranches(ctx context.Context, gitDir, workDir, upstream, sha string) (*bytes.Buffer, error) {
+ cmdArgs := []string{"diff", "--numstat", fmt.Sprintf("%s...%s", upstream, sha)}
+ return git.runDirCmd(ctx, gitDir, workDir, cmdArgs)
+}
func NewGitCli() *gitCli {
cli := &gitCli{git: &gitCmd{cmd: "git", timeout: 100000 * time.Millisecond}}
return cli
diff --git a/build/report/local/git_test.go b/build/report/local/git_test.go
index 332f80f..d5345ae 100644
--- a/build/report/local/git_test.go
+++ b/build/report/local/git_test.go
@@ -29,24 +29,28 @@ type TestCmd struct {
text string
}
type gitTestCli struct {
- revParse *TestCmd
- remoteUrl *TestCmd
- tree *TestCmd
- commit *TestCmd
+ revParse *TestCmd
+ remoteUrl *TestCmd
+ tree *TestCmd
+ commit *TestCmd
+ diffBranch *TestCmd
}
-func (g *gitTestCli) ProjectInfo(ctx context.Context, gitDir string, workDir string) (*bytes.Buffer, error) {
+func (g *gitTestCli) ProjectInfo(ctx context.Context, gitDir, workDir string) (*bytes.Buffer, error) {
return bytes.NewBufferString(g.revParse.text), g.revParse.err
}
-func (g *gitTestCli) RemoteUrl(ctx context.Context, gitDir string, workDir string, remote string) (*bytes.Buffer, error) {
+func (g *gitTestCli) RemoteUrl(ctx context.Context, gitDir, workDir, remote string) (*bytes.Buffer, error) {
return bytes.NewBufferString(g.remoteUrl.text), g.remoteUrl.err
}
-func (g *gitTestCli) Tree(ctx context.Context, gitDir string, workDir string, revision string) (*bytes.Buffer, error) {
+func (g *gitTestCli) Tree(ctx context.Context, gitDir, workDir, revision string) (*bytes.Buffer, error) {
return bytes.NewBufferString(g.tree.text), g.tree.err
}
-func (g *gitTestCli) CommitInfo(ctx context.Context, gitDir string, workDir string, sha string) (*bytes.Buffer, error) {
+func (g *gitTestCli) CommitInfo(ctx context.Context, gitDir, workDir, sha string) (*bytes.Buffer, error) {
return bytes.NewBufferString(g.commit.text), g.tree.err
}
+func (g *gitTestCli) DiffBranches(ctx context.Context, gitDir, workDir, upstream, sha string) (*bytes.Buffer, error) {
+ return bytes.NewBufferString(g.diffBranch.text), g.tree.err
+}
func Test_git(t *testing.T) {
@@ -82,12 +86,14 @@ func Test_git(t *testing.T) {
revCmd: &TestCmd{text: "/abs/path/to/work/dir\nsha_revision\n", err: nil},
remoteCmd: &TestCmd{text: "http://url/workdir", err: nil},
treeCmd: &TestCmd{text: "", err: nil},
- res: &app.GitProject{WorkDir: "/abs/path/to/work/dir",
+ res: &app.GitProject{
+ RepoDir: "work/dir",
+ WorkDir: "/abs/path/to/work/dir",
GitDir: ".git",
Remote: "origin",
RemoteUrl: "http://url/workdir",
Revision: "sha_revision",
- Files: []app.GitTreeObj{}},
+ Files: make(map[string]*app.GitTreeObj)},
},
// Test empty commit
commit: commitTest{
@@ -106,12 +112,14 @@ func Test_git(t *testing.T) {
revCmd: &TestCmd{text: "/abs/path/to/work/dir\nsha_revision\n", err: nil},
remoteCmd: &TestCmd{text: "http://url/workdir", err: nil},
treeCmd: &TestCmd{text: "100644 blob 0000000000000000000000000000000000000001 file.1\n", err: nil},
- res: &app.GitProject{WorkDir: "/abs/path/to/work/dir",
+ res: &app.GitProject{
+ RepoDir: "work/dir",
+ WorkDir: "/abs/path/to/work/dir",
GitDir: ".git",
Remote: "origin",
RemoteUrl: "http://url/workdir",
Revision: "sha_revision",
- Files: []app.GitTreeObj{{Permissions: "100644", Type: "blob",
+ Files: map[string]*app.GitTreeObj{"file.1": &app.GitTreeObj{Permissions: "100644", Type: "blob",
Sha: "0000000000000000000000000000000000000001", Filename: "file.1"}}},
},
commit: commitTest{
@@ -136,10 +144,13 @@ func Test_git(t *testing.T) {
commit: test.commit.cmd,
}}
- proj, err := git.Project(nil, test.path, test.gitDir, test.remote, test.revision, test.getFiles)
+ proj, err := git.Project(nil, test.path, test.gitDir, test.remote, test.revision)
if err != nil {
t.Fatal("Failed to parse project")
}
+ if test.getFiles {
+ _ = git.PopulateFiles(nil, proj, "")
+ }
if !reflect.DeepEqual(*proj, *test.project.res) {
t.Errorf("Project = %+v; want %+v", *proj, *test.project.res)
}
diff --git a/build/report/local/ninja.go b/build/report/local/ninja.go
index 3684c3b..012bcc7 100644
--- a/build/report/local/ninja.go
+++ b/build/report/local/ninja.go
@@ -26,6 +26,9 @@ import (
"tools/treble/build/report/app"
)
+// Performance degrades running multiple CLIs
+const MaxNinjaCliWorkers = 4
+
// Separate out the executable to allow tests to override the results
type ninjaExec interface {
Command(ctx context.Context, target string) (*bytes.Buffer, error)
@@ -33,6 +36,7 @@ type ninjaExec interface {
Query(ctx context.Context, target string) (*bytes.Buffer, error)
Path(ctx context.Context, target string, dependency string) (*bytes.Buffer, error)
Paths(ctx context.Context, target string, dependency string) (*bytes.Buffer, error)
+ Deps(ctx context.Context) (*bytes.Buffer, error)
Build(ctx context.Context, target string) (*bytes.Buffer, error)
}
@@ -124,6 +128,33 @@ func parseBuild(target string, data *bytes.Buffer, success bool) *app.BuildCmdRe
return out
}
+// parse deps command
+func parseDeps(data *bytes.Buffer) (*app.BuildDeps, error) {
+ out := &app.BuildDeps{Targets: make(map[string][]string)}
+ s := bufio.NewScanner(data)
+ curTarget := ""
+ var deps []string
+ for s.Scan() {
+ line := strings.TrimSpace(s.Text())
+ // Check if it's a new target
+ tokens := strings.Split(line, ":")
+ if len(tokens) > 1 {
+ if curTarget != "" {
+ out.Targets[curTarget] = deps
+ }
+ deps = []string{}
+ curTarget = tokens[0]
+ } else if line != "" {
+ deps = append(deps, line)
+ }
+
+ }
+ if curTarget != "" {
+ out.Targets[curTarget] = deps
+ }
+ return out, nil
+}
+
//
// Command line interface to ninja binary.
//
@@ -135,6 +166,7 @@ func parseBuild(target string, data *bytes.Buffer, success bool) *app.BuildCmdRe
// Query() -t query
// Path() -t path
// Paths() -t paths
+// Deps() -t deps
//
//
@@ -172,6 +204,10 @@ func (n *ninjaCmd) Path(ctx context.Context, target string, dependency string) (
func (n *ninjaCmd) Paths(ctx context.Context, target string, dependency string) (*bytes.Buffer, error) {
return n.runTool(ctx, "paths", []string{target, dependency})
}
+func (n *ninjaCmd) Deps(ctx context.Context) (*bytes.Buffer, error) {
+ return n.runTool(ctx, "deps", []string{})
+}
+
func (n *ninjaCmd) Build(ctx context.Context, target string) (*bytes.Buffer, error) {
args := append([]string{
@@ -234,6 +270,15 @@ func (cli *ninjaCli) Paths(ctx context.Context, target string, dependency string
return parsePaths(target, dependency, raw)
}
+// ninja -t deps
+func (cli *ninjaCli) Deps(ctx context.Context) (*app.BuildDeps, error) {
+ raw, err := cli.n.Deps(ctx)
+ if err != nil {
+ return nil, err
+ }
+ return parseDeps(raw)
+}
+
// Build given target
func (cli *ninjaCli) Build(ctx context.Context, target string) *app.BuildCmdResult {
raw, err := cli.n.Build(ctx, target)
@@ -242,5 +287,5 @@ func (cli *ninjaCli) Build(ctx context.Context, target string) *app.BuildCmdResu
}
func NewNinjaCli(cmd string, db string) *ninjaCli {
cli := &ninjaCli{n: &ninjaCmd{cmd: cmd, db: db, timeout: 100000 * time.Millisecond, buildTimeout: 300000 * time.Millisecond}}
- return (cli)
+ return cli
}
diff --git a/build/report/local/ninja_test.go b/build/report/local/ninja_test.go
index 3941e4e..40e2043 100644
--- a/build/report/local/ninja_test.go
+++ b/build/report/local/ninja_test.go
@@ -29,6 +29,7 @@ type ninjaTest struct {
query *TestCmd
path *TestCmd
paths *TestCmd
+ deps *TestCmd
build *TestCmd
}
@@ -47,6 +48,9 @@ func (n *ninjaTest) Path(ctx context.Context, target string, dependency string)
func (n *ninjaTest) Paths(ctx context.Context, target string, dependency string) (*bytes.Buffer, error) {
return bytes.NewBufferString(n.paths.text), n.paths.err
}
+func (n *ninjaTest) Deps(ctx context.Context) (*bytes.Buffer, error) {
+ return bytes.NewBufferString(n.deps.text), n.deps.err
+}
func (n *ninjaTest) Build(ctx context.Context, target string) (*bytes.Buffer, error) {
return bytes.NewBufferString(n.build.text), n.build.err
}
@@ -72,6 +76,10 @@ func Test_ninja(t *testing.T) {
cmd *TestCmd
res []*app.BuildPath
}
+ type depsTest struct {
+ cmd *TestCmd
+ res *app.BuildDeps
+ }
type buildTest struct {
cmd *TestCmd
res *app.BuildCmdResult
@@ -84,6 +92,7 @@ func Test_ninja(t *testing.T) {
input inputTest
path pathTest
paths pathsTest
+ deps depsTest
build buildTest
}{
{
@@ -111,6 +120,14 @@ func Test_ninja(t *testing.T) {
&app.BuildPath{Target: "test", Dependency: "dependency", Paths: []string{"test", "mid4", "dependency"}},
},
},
+ deps: depsTest{
+ cmd: &TestCmd{text: "some/build/library.so: #deps1\n dependentFile1.S\n dependentFile2.S\nsome/build/library2.so: #deps1\n dependentFile1.S\n dependentFile3.S\n"},
+ res: &app.BuildDeps{Targets: map[string][]string{
+ "some/build/library.so": []string{"dependentFile1.S", "dependentFile2.S"},
+ "some/build/library2.so": []string{"dependentFile1.S", "dependentFile3.S"},
+ },
+ },
+ },
build: buildTest{
cmd: &TestCmd{text: "", err: nil},
res: &app.BuildCmdResult{Name: "test", Output: []string{}, Success: true}},
@@ -124,6 +141,7 @@ func Test_ninja(t *testing.T) {
input: test.input.cmd,
path: test.path.cmd,
paths: test.paths.cmd,
+ deps: test.deps.cmd,
build: test.build.cmd,
}
n := &ninjaCli{n: exec}
@@ -177,6 +195,16 @@ func Test_ninja(t *testing.T) {
}
}
+ if test.deps.cmd != nil {
+ if res, err := n.Deps(nil); err != nil {
+ t.Errorf("Deps error %s", err)
+ } else {
+ if !reflect.DeepEqual(res, test.deps.res) {
+ t.Errorf("Deps result %v; want %v", res, test.deps.res)
+ }
+ }
+
+ }
if test.build.cmd != nil {
res := n.Build(nil, test.target)
if !reflect.DeepEqual(*res, *test.build.res) {
diff --git a/build/report/report/build.go b/build/report/report/build.go
index aedd8b4..cc0c9bf 100644
--- a/build/report/report/build.go
+++ b/build/report/report/build.go
@@ -16,6 +16,7 @@ package report
import (
"context"
+ "fmt"
"sync"
"tools/treble/build/report/app"
@@ -41,24 +42,28 @@ type buildPathData struct {
//
// create build target from using repo data
//
-func createBuildTarget(ctx context.Context, repo *repo, buildTarget *buildTargetData) *app.BuildTarget {
-
+func createBuildTarget(ctx context.Context, rtx *Context, buildTarget *buildTargetData) *app.BuildTarget {
out := &app.BuildTarget{Name: buildTarget.input.Target,
Steps: buildTarget.buildSteps,
- Projects: make(map[string]*app.BuildProject),
+ Projects: make(map[string]*app.GitProject),
FileCount: len(buildTarget.input.Files),
}
- for i, _ := range buildTarget.input.Files {
- proj, buildFile := lookupProjectFile(ctx, repo, &buildTarget.input.Files[i])
+ for _, f := range buildTarget.input.Files {
+ proj, buildFile := lookupProjectFile(ctx, rtx, f)
if buildFile != nil {
if buildProj, exists := out.Projects[proj.Name]; exists {
- buildProj.Files = append(buildProj.Files, *buildFile)
+ buildProj.Files[buildFile.Filename] = buildFile
} else {
out.Projects[proj.Name] =
- &app.BuildProject{Path: proj.RepoPath,
- Name: proj.Name, Revision: proj.GitProj.Revision,
- Files: []app.BuildFile{*buildFile}}
+ &app.GitProject{
+ RepoDir: proj.GitProj.RepoDir,
+ WorkDir: proj.GitProj.WorkDir,
+ GitDir: proj.GitProj.GitDir,
+ Remote: proj.GitProj.Remote,
+ RemoteUrl: proj.GitProj.RemoteUrl,
+ Revision: proj.GitProj.Revision,
+ Files: map[string]*app.GitTreeObj{buildFile.Filename: buildFile}}
}
}
}
@@ -70,7 +75,7 @@ func targetResolvers(ctx context.Context, rtx *Context) (chan string, chan *buil
var wg sync.WaitGroup
inChan := make(chan string)
outChan := make(chan *buildTargetData)
- for i := 0; i < rtx.WorkerCount; i++ {
+ for i := 0; i < rtx.BuildWorkerCount; i++ {
wg.Add(1)
go func() {
for targetName := range inChan {
@@ -80,7 +85,11 @@ func targetResolvers(ctx context.Context, rtx *Context) (chan string, chan *buil
buildSteps = len(cmds.Cmds)
}
input, err := rtx.Build.Input(ctx, targetName)
- outChan <- &buildTargetData{input: input, buildSteps: buildSteps, error: err != nil}
+ if input == nil {
+ fmt.Printf("Failed to get input %s (%s)\n", targetName, err)
+ } else {
+ outChan <- &buildTargetData{input: input, buildSteps: buildSteps, error: err != nil}
+ }
}
wg.Done()
}()
@@ -95,14 +104,14 @@ func targetResolvers(ctx context.Context, rtx *Context) (chan string, chan *buil
//
// Setup routines to resolve build input targets to BuildTarget
-func resolveBuildInputs(ctx context.Context, rtx *Context, repo *repo, inChan chan *buildTargetData) chan *app.BuildTarget {
+func resolveBuildInputs(ctx context.Context, rtx *Context, inChan chan *buildTargetData) chan *app.BuildTarget {
var wg sync.WaitGroup
outChan := make(chan *app.BuildTarget)
- for i := 0; i < rtx.WorkerCount; i++ {
+ for i := 0; i < rtx.BuildWorkerCount; i++ {
wg.Add(1)
go func() {
for buildTarget := range inChan {
- outChan <- createBuildTarget(ctx, repo, buildTarget)
+ outChan <- createBuildTarget(ctx, rtx, buildTarget)
}
wg.Done()
}()
@@ -119,7 +128,7 @@ func queryResolvers(ctx context.Context, rtx *Context) (chan string, chan *build
var wg sync.WaitGroup
inChan := make(chan string)
outChan := make(chan *buildSourceData)
- for i := 0; i < rtx.WorkerCount; i++ {
+ for i := 0; i < rtx.BuildWorkerCount; i++ {
wg.Add(1)
go func() {
for srcName := range inChan {
@@ -137,45 +146,27 @@ func queryResolvers(ctx context.Context, rtx *Context) (chan string, chan *build
return inChan, outChan
}
-// Setup routines to resolve path
-func pathResolvers(ctx context.Context, rtx *Context, target string) (chan string, chan *buildPathData) {
- var wg sync.WaitGroup
- inChan := make(chan string)
- outChan := make(chan *buildPathData)
- for i := 0; i < rtx.WorkerCount; i++ {
- wg.Add(1)
- go func() {
- for dep := range inChan {
- path, err := rtx.Build.Path(ctx, target, dep)
- outChan <- &buildPathData{filename: dep, path: path, error: err != nil}
- }
- wg.Done()
- }()
- }
- go func() {
- wg.Wait()
- close(outChan)
- }()
-
- return inChan, outChan
-}
-
// Setup routines to resolve paths
-func pathsResolvers(ctx context.Context, rtx *Context, target string) (chan string, chan *buildPathData) {
+func pathsResolvers(ctx context.Context, rtx *Context, target string, singlePath bool) (chan string, chan *buildPathData) {
var wg sync.WaitGroup
inChan := make(chan string)
outChan := make(chan *buildPathData)
- for i := 0; i < rtx.WorkerCount; i++ {
+ for i := 0; i < rtx.BuildWorkerCount; i++ {
wg.Add(1)
go func() {
for dep := range inChan {
- paths, err := rtx.Build.Paths(ctx, target, dep)
- if err != nil {
- outChan <- &buildPathData{filename: dep, path: nil, error: true}
+ if singlePath {
+ path, err := rtx.Build.Path(ctx, target, dep)
+ outChan <- &buildPathData{filename: dep, path: path, error: err != nil}
} else {
- for _, path := range paths {
-
- outChan <- &buildPathData{filename: dep, path: path, error: false}
+ paths, err := rtx.Build.Paths(ctx, target, dep)
+ if err != nil {
+ outChan <- &buildPathData{filename: dep, path: nil, error: true}
+ } else {
+ for _, path := range paths {
+
+ outChan <- &buildPathData{filename: dep, path: path, error: false}
+ }
}
}
}
diff --git a/build/report/report/dependencies.go b/build/report/report/dependencies.go
index 34b84d4..f2b21b5 100644
--- a/build/report/report/dependencies.go
+++ b/build/report/report/dependencies.go
@@ -26,10 +26,12 @@ type BuildDependencies interface {
Query(ctx context.Context, target string) (*app.BuildQuery, error)
Path(ctx context.Context, target string, dependency string) (*app.BuildPath, error)
Paths(ctx context.Context, target string, dependency string) ([]*app.BuildPath, error)
+ Deps(ctx context.Context) (*app.BuildDeps, error)
}
type ProjectDependencies interface {
- Project(ctx context.Context, path string, gitDir string, remote string, revision string, getFiles bool) (*app.GitProject, error)
+ Project(ctx context.Context, path string, gitDir string, remote string, revision string) (*app.GitProject, error)
+ PopulateFiles(ctx context.Context, proj *app.GitProject, upstream string) error
CommitInfo(ctx context.Context, proj *app.GitProject, sha string) (*app.GitCommit, error)
}
diff --git a/build/report/report/projects.go b/build/report/report/projects.go
index d5b2347..eaec5bf 100644
--- a/build/report/report/projects.go
+++ b/build/report/report/projects.go
@@ -20,6 +20,7 @@ import (
"os"
"path/filepath"
"strings"
+ "sync"
"tools/treble/build/report/app"
)
@@ -28,60 +29,15 @@ import (
// Repo and project related functions
//
type project struct {
- Name string // Name
- RepoPath string // Path in repo
- GitProj *app.GitProject // Git project data
- ObjMap map[string]app.GitTreeObj // Mapping of filename to git tree object
+ Name string // Name
+ GitProj *app.GitProject // Git project data
}
-var unknownProject = &project{Name: "unknown", RepoPath: "unknown", GitProj: &app.GitProject{}}
-
-// Repo containing a map of projects, this also contains a
-// map between a source file and the project it belongs to
-// allowing a quicker lookup of source file to project
-type repo struct {
- RepoBase string // Absolute path to repo base
- ProjMap map[string]*project // Map project name to project
- FileCache map[string]*project // map source files to project
-}
-
-// Create a mapping of projects from the input source manifest
-func createProjectMap(ctx context.Context, manifest *app.RepoManifest, repoBase string, proj ProjectDependencies, getFiles bool) *repo {
- if !strings.HasSuffix(repoBase, "/") {
- repoBase += "/"
- }
- repo := &repo{RepoBase: repoBase}
- // 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")
- }
- repo.FileCache = make(map[string]*project)
- repo.ProjMap = make(map[string]*project)
- for i, _ := range manifest.Projects {
-
- remotePtr := defRemotePtr
- if manifest.Projects[i].Remote != nil {
- remotePtr = remotes[*manifest.Projects[i].Remote]
- }
- proj := resolveProject(ctx, &manifest.Projects[i], remotePtr, proj, getFiles, &repo.FileCache)
- if proj != nil {
- // Add the remote information
- repo.ProjMap[proj.Name] = proj
- }
- }
- return repo
-}
+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, fileCache *map[string]*project) *project {
+func resolveProject(ctx context.Context, repoProj *app.RepoProject, remote *app.RepoRemote, proj ProjectDependencies, getFiles bool, upstreamBranch string) *project {
path := repoProj.Path
if path == "" {
@@ -110,17 +66,13 @@ func resolveProject(ctx context.Context, repoProj *app.RepoProject, remote *app.
gitDir = filepath.Join(parts[repostart:]...)
}
- gitProj, err := proj.Project(ctx, path, gitDir, remote.Name, repoProj.Revision, getFiles)
+ gitProj, err := proj.Project(ctx, path, gitDir, remote.Name, repoProj.Revision)
if err != nil {
return nil
}
- out := &project{Name: repoProj.Name, RepoPath: path, GitProj: gitProj}
- if len(gitProj.Files) > 0 {
- out.ObjMap = make(map[string]app.GitTreeObj)
- for _, obj := range gitProj.Files {
- (*fileCache)[filepath.Join(path, obj.Filename)] = out
- out.ObjMap[obj.Filename] = obj
- }
+ out := &project{Name: repoProj.Name, GitProj: gitProj}
+ if getFiles {
+ _ = proj.PopulateFiles(ctx, gitProj, upstreamBranch)
}
return out
}
@@ -130,43 +82,97 @@ func resolveProject(ctx context.Context, repoProj *app.RepoProject, remote *app.
// then resolve the file via the project found.
//
// Most files will be relative paths from the repo workspace
-func lookupProjectFile(ctx context.Context, repo *repo, filename *string) (*project, *app.BuildFile) {
- if proj, exists := repo.FileCache[*filename]; exists {
- repoName := (*filename)[len(proj.RepoPath)+1:]
- if gitObj, exists := proj.ObjMap[repoName]; exists {
- return proj, &app.BuildFile{Name: gitObj.Filename, Revision: gitObj.Sha}
+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, repo, &realpath)
+ if realpath, err := filepath.EvalSymlinks(filename); err == nil {
+ if realpath != filename {
+ return lookupProjectFile(ctx, rtx, realpath)
}
}
- if strings.HasPrefix(*filename, repo.RepoBase) {
+ if strings.HasPrefix(filename, rtx.RepoBase) {
// Some dependencies pick up the full path try stripping out
- relpath := (*filename)[len(repo.RepoBase)+1:]
- return lookupProjectFile(ctx, repo, &relpath)
+ relpath := (filename)[len(rtx.RepoBase):]
+ return lookupProjectFile(ctx, rtx, relpath)
}
}
- return unknownProject, &app.BuildFile{Name: *filename, Revision: ""}
+ 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) chan *repo {
- outChan := make(chan *repo)
+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() {
- defer close(outChan)
- // Parse the manifest file
- xmlRepo, err := rtx.Repo.Manifest(manifestFile)
- if err != nil {
- return
+ for i, _ := range manifest.Projects {
+ repoChan <- &manifest.Projects[i]
}
- // Convert manifest into projects with source files
- repo := createProjectMap(ctx, xmlRepo, rtx.RepoBase, rtx.Project, getFiles)
- outChan <- repo
+ close(repoChan)
}()
- return outChan
+ 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
}
diff --git a/build/report/report/report_test.go b/build/report/report/report_test.go
index d259fa7..7aa3891 100644
--- a/build/report/report/report_test.go
+++ b/build/report/report/report_test.go
@@ -35,6 +35,7 @@ type reportTest struct {
projects map[string]*app.GitProject
commits map[*app.GitProject]map[string]*app.GitCommit
+ deps *app.BuildDeps
projectCommits map[string]int
}
@@ -81,7 +82,10 @@ func (r *reportTest) Paths(ctx context.Context, target string, dependency string
return r.multipaths[target][dependency], nil
}
-func (r *reportTest) Project(ctx context.Context, path string, gitDir string, remote string, revision string, getFiles bool) (*app.GitProject, error) {
+func (r *reportTest) Deps(ctx context.Context) (*app.BuildDeps, error) {
+ return r.deps, nil
+}
+func (r *reportTest) Project(ctx context.Context, path string, gitDir string, remote string, revision string) (*app.GitProject, error) {
var err error
out := r.projects[path]
if out == nil {
@@ -89,7 +93,9 @@ func (r *reportTest) Project(ctx context.Context, path string, gitDir string, re
}
return out, err
}
-
+func (r *reportTest) PopulateFiles(ctx context.Context, proj *app.GitProject, upstream string) error {
+ return nil
+}
func (r *reportTest) CommitInfo(ctx context.Context, proj *app.GitProject, sha string) (*app.GitCommit, error) {
var err error
out := r.commits[proj][sha]
@@ -113,6 +119,22 @@ func projName(i int) string {
return "proj." + strconv.Itoa(i)
}
+func fileName(i int) (filename string, sha string) {
+ iString := strconv.Itoa(i)
+ return "source." + iString, "sha." + iString
+}
+func createFile(i int) *app.GitTreeObj {
+ fname, sha := fileName(i)
+ return &app.GitTreeObj{Permissions: "100644", Type: "blob", Filename: fname, Sha: sha}
+}
+func createProject(name string) *app.GitProject {
+ return &app.GitProject{
+ RepoDir: name, WorkDir: name, GitDir: ".git", Remote: "origin",
+ RemoteUrl: "origin_url", Revision: name + "_sha",
+ Files: make(map[string]*app.GitTreeObj)}
+
+}
+
// Create basic test data for given inputs
func createTest(projCount int, fileCount int) *reportTest {
test := &reportTest{
@@ -132,14 +154,11 @@ func createTest(projCount int, fileCount int) *reportTest {
for i := 0; i <= projCount; i++ {
name := projName(i)
- proj := &app.GitProject{
- WorkDir: name, GitDir: ".git", Remote: "origin",
- RemoteUrl: "origin_url", Revision: name + "_sha"}
+ proj := createProject(name)
for i := 0; i <= fileCount; i++ {
- iString := strconv.Itoa(i)
- treeObj := app.GitTreeObj{Permissions: "100644", Type: "blob", Filename: "source." + iString, Sha: iString}
- proj.Files = append(proj.Files, treeObj)
+ treeObj := createFile(i)
+ proj.Files[treeObj.Filename] = treeObj
}
test.projects[name] = proj
@@ -194,14 +213,14 @@ func Test_report(t *testing.T) {
var targets []string
// Build expected output while creating the targets
- var resTargets []*app.BuildTarget
+ resTargets := make(map[string]*app.BuildTarget)
for _, target := range targetDefs {
res := &app.BuildTarget{Name: target.name,
Steps: target.cmds,
FileCount: len(target.inputFiles),
- Projects: make(map[string]*app.BuildProject),
+ Projects: make(map[string]*app.GitProject),
}
// Add files to the build target
@@ -209,17 +228,16 @@ func Test_report(t *testing.T) {
for _, in := range target.inputFiles {
// Get project by name
pName := projName(in.proj)
+ bf := createFile(in.file)
p := test.projects[pName]
- bf := &app.BuildFile{Name: p.Files[in.file].Filename,
- Revision: p.Files[in.file].Sha}
inputFiles = append(inputFiles,
- fmt.Sprintf("%s/%s", p.WorkDir, bf.Name))
+ fmt.Sprintf("%s/%s", p.WorkDir, bf.Filename))
if _, exists := res.Projects[pName]; !exists {
- res.Projects[pName] = &app.BuildProject{Path: pName, Name: pName, Revision: p.Revision}
+ res.Projects[pName] = createProject(pName)
}
- res.Projects[pName].Files = append(res.Projects[pName].Files, *bf)
+ res.Projects[pName].Files[bf.Filename] = bf
}
// Create test data
@@ -231,17 +249,18 @@ func Test_report(t *testing.T) {
Outputs: createStrings("target.out.", target.outputTargets)}
targets = append(targets, target.name)
- resTargets = append(resTargets, res)
+ resTargets[res.Name] = res
}
- rtx := &Context{RepoBase: "/src", Repo: test, Build: test, Project: test, WorkerCount: 1}
- req := &app.ReportRequest{ManifestFile: "test_file", Targets: targets}
+ rtx := &Context{RepoBase: "/src", Repo: test, Build: test, Project: test, WorkerCount: 1, BuildWorkerCount: 1}
+ rtx.ResolveProjectMap(nil, "test_file", "")
+ req := &app.ReportRequest{Targets: targets}
rsp, err := RunReport(nil, rtx, req)
if err != nil {
t.Errorf("Failed to run report for request %+v", req)
} else {
if !reflect.DeepEqual(rsp.Targets, resTargets) {
- t.Errorf("Target not expected")
+ t.Errorf("Got targets %+v, expected %+v", rsp.Targets, resTargets)
}
}
}
diff --git a/build/report/report/run.go b/build/report/report/run.go
index e480df6..fd03088 100644
--- a/build/report/report/run.go
+++ b/build/report/report/run.go
@@ -56,6 +56,20 @@ func binaryExecutables(ctx context.Context, dir string, recursive bool) ([]strin
return files, numSymLinks, err
}
+// Resolve the manifest
+func (rtx *Context) ResolveProjectMap(ctx context.Context, manifest string, upstreamBranch string) {
+ if rtx.Info == nil {
+ rtx.Info = resolveProjectMap(ctx, rtx, manifest, true, upstreamBranch)
+ }
+}
+
+// Find host tools
+func ResolveHostTools(ctx context.Context, hostToolPath string) (*app.HostReport, error) {
+ out := &app.HostReport{Path: hostToolPath}
+ out.Targets, out.SymLinks, _ = binaryExecutables(ctx, hostToolPath, true)
+ return out, nil
+}
+
// Run reports
//
@@ -69,69 +83,40 @@ func binaryExecutables(ctx context.Context, dir string, recursive bool) ([]strin
// queries can be fully resolved
//
func RunReport(ctx context.Context, rtx *Context, req *app.ReportRequest) (*app.Report, error) {
-
- repoCh := resolveProjectMap(ctx, rtx, req.ManifestFile, true)
inChan, targetCh := targetResolvers(ctx, rtx)
- hostTargetSymLinks := 0
- hostTargetMap := make(map[string]bool)
go func() {
for i, _ := range req.Targets {
inChan <- req.Targets[i]
}
-
- if req.IncludeHostTools {
- hostTargets, symLinks, _ := binaryExecutables(ctx, req.HostToolPath, true)
- hostTargetSymLinks = symLinks
- for i, _ := range hostTargets {
- inChan <- hostTargets[i]
- hostTargetMap[hostTargets[i]] = true
- }
- }
close(inChan)
}()
- // Wait for repo projects to be resolved
- repo := <-repoCh
// Resolve the build inputs into build target projects
- buildTargetChan := resolveBuildInputs(ctx, rtx, repo, targetCh)
+ buildTargetChan := resolveBuildInputs(ctx, rtx, targetCh)
- out := &app.Report{}
- if req.IncludeHostTools {
- out.Host = &app.HostReport{Path: req.HostToolPath, SymLinks: hostTargetSymLinks}
- }
+ out := &app.Report{Targets: make(map[string]*app.BuildTarget)}
for bt := range buildTargetChan {
- if _, exists := hostTargetMap[bt.Name]; exists {
- out.Host.Targets = append(out.Host.Targets, bt)
- } else {
- out.Targets = append(out.Targets, bt)
- }
+ out.Targets[bt.Name] = bt
}
return out, nil
}
-// Resolve set of commits into set of files
-func ResolveCommits(ctx context.Context, rtx *Context, req *app.ProjectCommits) ([]string, error) {
- // Resolve project map, don't need the repo files here
- repo := <-resolveProjectMap(ctx, rtx, req.ManifestFile, false)
-
- files := []string{}
- // Resolve any commits
- for _, commit := range req.Commits {
- if proj, exists := repo.ProjMap[commit.Project]; exists {
- info, err := rtx.Project.CommitInfo(ctx, proj.GitProj, commit.Revision)
- if err == nil {
- for _, f := range info.Files {
- if f.Type != app.GitFileRemoved {
- files = append(files, filepath.Join(proj.RepoPath, f.Filename))
- }
+// Resolve commit into git commit info
+func ResolveCommit(ctx context.Context, rtx *Context, commit *app.ProjectCommit) (*app.GitCommit, []string, error) {
+ if proj, exists := rtx.Info.ProjMap[commit.Project]; exists {
+ info, err := rtx.Project.CommitInfo(ctx, proj.GitProj, commit.Revision)
+ files := []string{}
+ if err == nil {
+ for _, f := range info.Files {
+ if f.Type != app.GitFileRemoved {
+ files = append(files, filepath.Join(proj.GitProj.RepoDir, f.Filename))
}
}
- } else {
- return nil, errors.New(fmt.Sprintf("Failed to find commit %s:%s", commit.Project, commit.Revision))
}
+ return info, files, err
}
- return files, nil
+ return nil, nil, errors.New(fmt.Sprintf("Unknown project %s", commit.Project))
}
@@ -184,29 +169,10 @@ func RunQuery(ctx context.Context, rtx *Context, req *app.QueryRequest) (*app.Qu
return out, nil
}
-// Check if path exists between target and outputs provided, return outputs that have a
-// path to target. Only return valid paths via the output any errors are dropped
-func RunPathFilter(ctx context.Context, rtx *Context, target string, outputs []string) []string {
- var filter []string
- inChan, pathCh := pathResolvers(ctx, rtx, target)
- // Convert source files to outputs
- go func() {
- for _, out := range outputs {
- inChan <- out
- }
- close(inChan)
- }()
- for result := range pathCh {
- if !result.error {
- filter = append(filter, result.filename)
- }
- }
- return filter
-}
-
-func RunPaths(ctx context.Context, rtx *Context, target string, files []string) []*app.BuildPath {
+// Get paths
+func RunPaths(ctx context.Context, rtx *Context, target string, singlePath bool, files []string) []*app.BuildPath {
out := []*app.BuildPath{}
- inChan, pathCh := pathsResolvers(ctx, rtx, target)
+ inChan, pathCh := pathsResolvers(ctx, rtx, target, singlePath)
// Convert source files to outputs
go func() {
for _, f := range files {
diff --git a/build/report/report/types.go b/build/report/report/types.go
index 707864b..8610d43 100644
--- a/build/report/report/types.go
+++ b/build/report/report/types.go
@@ -25,11 +25,21 @@ func (r *RepoMan) Manifest(filename string) (*app.RepoManifest, error) {
return app.ParseXml(filename)
}
+// Project information containing a map of projects, this also contains a
+// map between a source file and the project it belongs to
+// allowing a quicker lookup of source file to project
+type ProjectInfo struct {
+ ProjMap map[string]*project // Map project name to project
+ FileCache map[string]*project // Map source files to project
+}
+
// Report context
type Context struct {
- RepoBase string // Absolute repo base directory
- Repo RepoDependencies // Repo interface
- Build BuildDependencies // Build interface
- Project ProjectDependencies // Project interface
- WorkerCount int // Number of worker threads
+ RepoBase string // Absolute path to repo base
+ Repo RepoDependencies // Repo interface
+ Build BuildDependencies // Build interface
+ Project ProjectDependencies // Project interface
+ WorkerCount int // Number of worker threads
+ BuildWorkerCount int // Number of build worker threads
+ Info *ProjectInfo // Project information
}