• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2018 Google Inc. All rights reserved.
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 build
16
17import (
18	"fmt"
19	"io/ioutil"
20	"os"
21	"path/filepath"
22	"runtime"
23	"strings"
24
25	"github.com/google/blueprint/microfactory"
26
27	"android/soong/ui/build/paths"
28	"android/soong/ui/metrics"
29)
30
31func parsePathDir(dir string) []string {
32	f, err := os.Open(dir)
33	if err != nil {
34		return nil
35	}
36	defer f.Close()
37
38	if s, err := f.Stat(); err != nil || !s.IsDir() {
39		return nil
40	}
41
42	infos, err := f.Readdir(-1)
43	if err != nil {
44		return nil
45	}
46
47	ret := make([]string, 0, len(infos))
48	for _, info := range infos {
49		if m := info.Mode(); !m.IsDir() && m&0111 != 0 {
50			ret = append(ret, info.Name())
51		}
52	}
53	return ret
54}
55
56func SetupPath(ctx Context, config Config) {
57	if config.pathReplaced {
58		return
59	}
60
61	ctx.BeginTrace(metrics.RunSetupTool, "path")
62	defer ctx.EndTrace()
63
64	origPath, _ := config.Environment().Get("PATH")
65	myPath := filepath.Join(config.OutDir(), ".path")
66	interposer := myPath + "_interposer"
67
68	var cfg microfactory.Config
69	cfg.Map("android/soong", "build/soong")
70	cfg.TrimPath, _ = filepath.Abs(".")
71	if _, err := microfactory.Build(&cfg, interposer, "android/soong/cmd/path_interposer"); err != nil {
72		ctx.Fatalln("Failed to build path interposer:", err)
73	}
74
75	if err := ioutil.WriteFile(interposer+"_origpath", []byte(origPath), 0777); err != nil {
76		ctx.Fatalln("Failed to write original path:", err)
77	}
78
79	entries, err := paths.LogListener(ctx.Context, interposer+"_log")
80	if err != nil {
81		ctx.Fatalln("Failed to listen for path logs:", err)
82	}
83
84	go func() {
85		for log := range entries {
86			curPid := os.Getpid()
87			for i, proc := range log.Parents {
88				if proc.Pid == curPid {
89					log.Parents = log.Parents[i:]
90					break
91				}
92			}
93			procPrints := []string{
94				"See https://android.googlesource.com/platform/build/+/master/Changes.md#PATH_Tools for more information.",
95			}
96			if len(log.Parents) > 0 {
97				procPrints = append(procPrints, "Process tree:")
98				for i, proc := range log.Parents {
99					procPrints = append(procPrints, fmt.Sprintf("%s→ %s", strings.Repeat(" ", i), proc.Command))
100				}
101			}
102
103			config := paths.GetConfig(log.Basename)
104			if config.Error {
105				ctx.Printf("Disallowed PATH tool %q used: %#v", log.Basename, log.Args)
106				for _, line := range procPrints {
107					ctx.Println(line)
108				}
109			} else {
110				ctx.Verbosef("Unknown PATH tool %q used: %#v", log.Basename, log.Args)
111				for _, line := range procPrints {
112					ctx.Verboseln(line)
113				}
114			}
115		}
116	}()
117
118	ensureEmptyDirectoriesExist(ctx, myPath)
119
120	var execs []string
121	for _, pathEntry := range filepath.SplitList(origPath) {
122		if pathEntry == "" {
123			// Ignore the current directory
124			continue
125		}
126		// TODO(dwillemsen): remove path entries under TOP? or anything
127		// that looks like an android source dir? They won't exist on
128		// the build servers, since they're added by envsetup.sh.
129		// (Except for the JDK, which is configured in ui/build/config.go)
130
131		execs = append(execs, parsePathDir(pathEntry)...)
132	}
133
134	allowAllSymlinks := config.Environment().IsEnvTrue("TEMPORARY_DISABLE_PATH_RESTRICTIONS")
135	for _, name := range execs {
136		if !paths.GetConfig(name).Symlink && !allowAllSymlinks {
137			continue
138		}
139
140		err := os.Symlink("../.path_interposer", filepath.Join(myPath, name))
141		// Intentionally ignore existing files -- that means that we
142		// just created it, and the first one should win.
143		if err != nil && !os.IsExist(err) {
144			ctx.Fatalln("Failed to create symlink:", err)
145		}
146	}
147
148	myPath, _ = filepath.Abs(myPath)
149
150	// We put some prebuilts in $PATH, since it's infeasible to add dependencies for all of
151	// them.
152	prebuiltsPath, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86")
153	myPath = prebuiltsPath + string(os.PathListSeparator) + myPath
154
155	config.Environment().Set("PATH", myPath)
156	config.pathReplaced = true
157}
158