xref: /aosp_15_r20/external/bazelbuild-rules_go/go/tools/gopackagesdriver/bazel.go (revision 9bb1b549b6a84214c53be0924760be030e66b93a)
1*9bb1b549SSpandan Das// Copyright 2021 The Bazel Authors. All rights reserved.
2*9bb1b549SSpandan Das//
3*9bb1b549SSpandan Das// Licensed under the Apache License, Version 2.0 (the "License");
4*9bb1b549SSpandan Das// you may not use this file except in compliance with the License.
5*9bb1b549SSpandan Das// You may obtain a copy of the License at
6*9bb1b549SSpandan Das//
7*9bb1b549SSpandan Das//    http://www.apache.org/licenses/LICENSE-2.0
8*9bb1b549SSpandan Das//
9*9bb1b549SSpandan Das// Unless required by applicable law or agreed to in writing, software
10*9bb1b549SSpandan Das// distributed under the License is distributed on an "AS IS" BASIS,
11*9bb1b549SSpandan Das// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*9bb1b549SSpandan Das// See the License for the specific language governing permissions and
13*9bb1b549SSpandan Das// limitations under the License.
14*9bb1b549SSpandan Das
15*9bb1b549SSpandan Daspackage main
16*9bb1b549SSpandan Das
17*9bb1b549SSpandan Dasimport (
18*9bb1b549SSpandan Das	"bufio"
19*9bb1b549SSpandan Das	"bytes"
20*9bb1b549SSpandan Das	"context"
21*9bb1b549SSpandan Das	"encoding/json"
22*9bb1b549SSpandan Das	"errors"
23*9bb1b549SSpandan Das	"fmt"
24*9bb1b549SSpandan Das	"io/ioutil"
25*9bb1b549SSpandan Das	"net/url"
26*9bb1b549SSpandan Das	"os"
27*9bb1b549SSpandan Das	"os/exec"
28*9bb1b549SSpandan Das	"path/filepath"
29*9bb1b549SSpandan Das	"strings"
30*9bb1b549SSpandan Das)
31*9bb1b549SSpandan Das
32*9bb1b549SSpandan Dasconst (
33*9bb1b549SSpandan Das	toolTag = "gopackagesdriver"
34*9bb1b549SSpandan Das)
35*9bb1b549SSpandan Das
36*9bb1b549SSpandan Dastype Bazel struct {
37*9bb1b549SSpandan Das	bazelBin          string
38*9bb1b549SSpandan Das	workspaceRoot     string
39*9bb1b549SSpandan Das	bazelStartupFlags []string
40*9bb1b549SSpandan Das	info              map[string]string
41*9bb1b549SSpandan Das}
42*9bb1b549SSpandan Das
43*9bb1b549SSpandan Das// Minimal BEP structs to access the build outputs
44*9bb1b549SSpandan Dastype BEPNamedSet struct {
45*9bb1b549SSpandan Das	NamedSetOfFiles *struct {
46*9bb1b549SSpandan Das		Files []struct {
47*9bb1b549SSpandan Das			Name string `json:"name"`
48*9bb1b549SSpandan Das			URI  string `json:"uri"`
49*9bb1b549SSpandan Das		} `json:"files"`
50*9bb1b549SSpandan Das	} `json:"namedSetOfFiles"`
51*9bb1b549SSpandan Das}
52*9bb1b549SSpandan Das
53*9bb1b549SSpandan Dasfunc NewBazel(ctx context.Context, bazelBin, workspaceRoot string, bazelStartupFlags []string) (*Bazel, error) {
54*9bb1b549SSpandan Das	b := &Bazel{
55*9bb1b549SSpandan Das		bazelBin:          bazelBin,
56*9bb1b549SSpandan Das		workspaceRoot:     workspaceRoot,
57*9bb1b549SSpandan Das		bazelStartupFlags: bazelStartupFlags,
58*9bb1b549SSpandan Das	}
59*9bb1b549SSpandan Das	if err := b.fillInfo(ctx); err != nil {
60*9bb1b549SSpandan Das		return nil, fmt.Errorf("unable to query bazel info: %w", err)
61*9bb1b549SSpandan Das	}
62*9bb1b549SSpandan Das	return b, nil
63*9bb1b549SSpandan Das}
64*9bb1b549SSpandan Das
65*9bb1b549SSpandan Dasfunc (b *Bazel) fillInfo(ctx context.Context) error {
66*9bb1b549SSpandan Das	b.info = map[string]string{}
67*9bb1b549SSpandan Das	output, err := b.run(ctx, "info")
68*9bb1b549SSpandan Das	if err != nil {
69*9bb1b549SSpandan Das		return err
70*9bb1b549SSpandan Das	}
71*9bb1b549SSpandan Das	scanner := bufio.NewScanner(bytes.NewBufferString(output))
72*9bb1b549SSpandan Das	for scanner.Scan() {
73*9bb1b549SSpandan Das		parts := strings.SplitN(strings.TrimSpace(scanner.Text()), ":", 2)
74*9bb1b549SSpandan Das		b.info[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
75*9bb1b549SSpandan Das	}
76*9bb1b549SSpandan Das	return nil
77*9bb1b549SSpandan Das}
78*9bb1b549SSpandan Das
79*9bb1b549SSpandan Dasfunc (b *Bazel) run(ctx context.Context, command string, args ...string) (string, error) {
80*9bb1b549SSpandan Das	defaultArgs := []string{
81*9bb1b549SSpandan Das		command,
82*9bb1b549SSpandan Das		"--tool_tag=" + toolTag,
83*9bb1b549SSpandan Das		"--ui_actions_shown=0",
84*9bb1b549SSpandan Das	}
85*9bb1b549SSpandan Das	cmd := exec.CommandContext(ctx, b.bazelBin, concatStringsArrays(b.bazelStartupFlags, defaultArgs, args)...)
86*9bb1b549SSpandan Das	fmt.Fprintln(os.Stderr, "Running:", cmd.Args)
87*9bb1b549SSpandan Das	cmd.Dir = b.WorkspaceRoot()
88*9bb1b549SSpandan Das	cmd.Stderr = os.Stderr
89*9bb1b549SSpandan Das	output, err := cmd.Output()
90*9bb1b549SSpandan Das	return string(output), err
91*9bb1b549SSpandan Das}
92*9bb1b549SSpandan Das
93*9bb1b549SSpandan Dasfunc (b *Bazel) Build(ctx context.Context, args ...string) ([]string, error) {
94*9bb1b549SSpandan Das	jsonFile, err := ioutil.TempFile("", "gopackagesdriver_bep_")
95*9bb1b549SSpandan Das	if err != nil {
96*9bb1b549SSpandan Das		return nil, fmt.Errorf("unable to create BEP JSON file: %w", err)
97*9bb1b549SSpandan Das	}
98*9bb1b549SSpandan Das	defer func() {
99*9bb1b549SSpandan Das		jsonFile.Close()
100*9bb1b549SSpandan Das		os.Remove(jsonFile.Name())
101*9bb1b549SSpandan Das	}()
102*9bb1b549SSpandan Das
103*9bb1b549SSpandan Das	args = append([]string{
104*9bb1b549SSpandan Das		"--show_result=0",
105*9bb1b549SSpandan Das		"--build_event_json_file=" + jsonFile.Name(),
106*9bb1b549SSpandan Das		"--build_event_json_file_path_conversion=no",
107*9bb1b549SSpandan Das	}, args...)
108*9bb1b549SSpandan Das	if _, err := b.run(ctx, "build", args...); err != nil {
109*9bb1b549SSpandan Das		// Ignore a regular build failure to get partial data.
110*9bb1b549SSpandan Das		// See https://docs.bazel.build/versions/main/guide.html#what-exit-code-will-i-get on
111*9bb1b549SSpandan Das		// exit codes.
112*9bb1b549SSpandan Das		var exerr *exec.ExitError
113*9bb1b549SSpandan Das		if !errors.As(err, &exerr) || exerr.ExitCode() != 1 {
114*9bb1b549SSpandan Das			return nil, fmt.Errorf("bazel build failed: %w", err)
115*9bb1b549SSpandan Das		}
116*9bb1b549SSpandan Das	}
117*9bb1b549SSpandan Das
118*9bb1b549SSpandan Das	files := make([]string, 0)
119*9bb1b549SSpandan Das	decoder := json.NewDecoder(jsonFile)
120*9bb1b549SSpandan Das	for decoder.More() {
121*9bb1b549SSpandan Das		var namedSet BEPNamedSet
122*9bb1b549SSpandan Das		if err := decoder.Decode(&namedSet); err != nil {
123*9bb1b549SSpandan Das			return nil, fmt.Errorf("unable to decode %s: %w", jsonFile.Name(), err)
124*9bb1b549SSpandan Das		}
125*9bb1b549SSpandan Das
126*9bb1b549SSpandan Das		if namedSet.NamedSetOfFiles != nil {
127*9bb1b549SSpandan Das			for _, f := range namedSet.NamedSetOfFiles.Files {
128*9bb1b549SSpandan Das				fileUrl, err := url.Parse(f.URI)
129*9bb1b549SSpandan Das				if err != nil {
130*9bb1b549SSpandan Das					return nil, fmt.Errorf("unable to parse file URI: %w", err)
131*9bb1b549SSpandan Das				}
132*9bb1b549SSpandan Das				files = append(files, filepath.FromSlash(fileUrl.Path))
133*9bb1b549SSpandan Das			}
134*9bb1b549SSpandan Das		}
135*9bb1b549SSpandan Das	}
136*9bb1b549SSpandan Das
137*9bb1b549SSpandan Das	return files, nil
138*9bb1b549SSpandan Das}
139*9bb1b549SSpandan Das
140*9bb1b549SSpandan Dasfunc (b *Bazel) Query(ctx context.Context, args ...string) ([]string, error) {
141*9bb1b549SSpandan Das	output, err := b.run(ctx, "query", args...)
142*9bb1b549SSpandan Das	if err != nil {
143*9bb1b549SSpandan Das		return nil, fmt.Errorf("bazel query failed: %w", err)
144*9bb1b549SSpandan Das	}
145*9bb1b549SSpandan Das
146*9bb1b549SSpandan Das	trimmedOutput := strings.TrimSpace(output)
147*9bb1b549SSpandan Das	if len(trimmedOutput) == 0 {
148*9bb1b549SSpandan Das		return nil, nil
149*9bb1b549SSpandan Das	}
150*9bb1b549SSpandan Das
151*9bb1b549SSpandan Das	return strings.Split(trimmedOutput, "\n"), nil
152*9bb1b549SSpandan Das}
153*9bb1b549SSpandan Das
154*9bb1b549SSpandan Dasfunc (b *Bazel) WorkspaceRoot() string {
155*9bb1b549SSpandan Das	return b.workspaceRoot
156*9bb1b549SSpandan Das}
157*9bb1b549SSpandan Das
158*9bb1b549SSpandan Dasfunc (b *Bazel) ExecutionRoot() string {
159*9bb1b549SSpandan Das	return b.info["execution_root"]
160*9bb1b549SSpandan Das}
161*9bb1b549SSpandan Das
162*9bb1b549SSpandan Dasfunc (b *Bazel) OutputBase() string {
163*9bb1b549SSpandan Das	return b.info["output_base"]
164*9bb1b549SSpandan Das}
165