blob: a47639c48d1253c2d9725d491e99fe8234bc256d [file] [log] [blame]
Dan Willemsen18490112018-05-25 16:30:04 -07001// 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 (
Harsh Shandilya6cc5f3e2019-09-14 15:45:51 +053018// "fmt"
Dan Willemsen18490112018-05-25 16:30:04 -070019 "io/ioutil"
20 "os"
Dan Willemsen4e2456b2019-10-03 16:45:58 -070021 "os/exec"
Dan Willemsen18490112018-05-25 16:30:04 -070022 "path/filepath"
Dan Willemsen417be1f2018-10-30 23:18:54 -070023 "runtime"
Harsh Shandilya6cc5f3e2019-09-14 15:45:51 +053024// "strings"
Dan Willemsen18490112018-05-25 16:30:04 -070025
26 "github.com/google/blueprint/microfactory"
27
28 "android/soong/ui/build/paths"
Nan Zhang17f27672018-12-12 16:01:49 -080029 "android/soong/ui/metrics"
Dan Willemsen18490112018-05-25 16:30:04 -070030)
31
32func parsePathDir(dir string) []string {
33 f, err := os.Open(dir)
34 if err != nil {
35 return nil
36 }
37 defer f.Close()
38
39 if s, err := f.Stat(); err != nil || !s.IsDir() {
40 return nil
41 }
42
43 infos, err := f.Readdir(-1)
44 if err != nil {
45 return nil
46 }
47
48 ret := make([]string, 0, len(infos))
49 for _, info := range infos {
50 if m := info.Mode(); !m.IsDir() && m&0111 != 0 {
51 ret = append(ret, info.Name())
52 }
53 }
54 return ret
55}
56
Dan Willemsen4e2456b2019-10-03 16:45:58 -070057// A "lite" version of SetupPath used for dumpvars, or other places that need
Patrice Arruda2ce07762020-06-04 19:34:41 +000058// minimal overhead (but at the expense of logging). If tmpDir is empty, the
59// default TMPDIR is used from config.
60func SetupLitePath(ctx Context, config Config, tmpDir string) {
Dan Willemsen4e2456b2019-10-03 16:45:58 -070061 if config.pathReplaced {
62 return
63 }
64
65 ctx.BeginTrace(metrics.RunSetupTool, "litepath")
66 defer ctx.EndTrace()
67
68 origPath, _ := config.Environment().Get("PATH")
Patrice Arruda2ce07762020-06-04 19:34:41 +000069
70 if tmpDir == "" {
71 tmpDir, _ = config.Environment().Get("TMPDIR")
72 }
73 myPath := filepath.Join(tmpDir, "path")
Dan Willemsen4e2456b2019-10-03 16:45:58 -070074 ensureEmptyDirectoriesExist(ctx, myPath)
75
76 os.Setenv("PATH", origPath)
77 for name, pathConfig := range paths.Configuration {
78 if !pathConfig.Symlink {
79 continue
80 }
81
82 origExec, err := exec.LookPath(name)
83 if err != nil {
84 continue
85 }
86 origExec, err = filepath.Abs(origExec)
87 if err != nil {
88 continue
89 }
90
91 err = os.Symlink(origExec, filepath.Join(myPath, name))
92 if err != nil {
93 ctx.Fatalln("Failed to create symlink:", err)
94 }
95 }
96
97 myPath, _ = filepath.Abs(myPath)
98
99 prebuiltsPath, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86")
100 myPath = prebuiltsPath + string(os.PathListSeparator) + myPath
101
102 config.Environment().Set("PATH", myPath)
103 config.pathReplaced = true
104}
105
Dan Willemsen18490112018-05-25 16:30:04 -0700106func SetupPath(ctx Context, config Config) {
107 if config.pathReplaced {
108 return
109 }
110
Nan Zhang17f27672018-12-12 16:01:49 -0800111 ctx.BeginTrace(metrics.RunSetupTool, "path")
Dan Willemsen18490112018-05-25 16:30:04 -0700112 defer ctx.EndTrace()
113
114 origPath, _ := config.Environment().Get("PATH")
115 myPath := filepath.Join(config.OutDir(), ".path")
116 interposer := myPath + "_interposer"
117
118 var cfg microfactory.Config
119 cfg.Map("android/soong", "build/soong")
120 cfg.TrimPath, _ = filepath.Abs(".")
121 if _, err := microfactory.Build(&cfg, interposer, "android/soong/cmd/path_interposer"); err != nil {
122 ctx.Fatalln("Failed to build path interposer:", err)
123 }
124
125 if err := ioutil.WriteFile(interposer+"_origpath", []byte(origPath), 0777); err != nil {
126 ctx.Fatalln("Failed to write original path:", err)
127 }
128
Harsh Shandilya6cc5f3e2019-09-14 15:45:51 +0530129/*
Dan Willemsen18490112018-05-25 16:30:04 -0700130 entries, err := paths.LogListener(ctx.Context, interposer+"_log")
131 if err != nil {
132 ctx.Fatalln("Failed to listen for path logs:", err)
133 }
134
135 go func() {
136 for log := range entries {
137 curPid := os.Getpid()
138 for i, proc := range log.Parents {
139 if proc.Pid == curPid {
140 log.Parents = log.Parents[i:]
141 break
142 }
143 }
144 procPrints := []string{
145 "See https://android.googlesource.com/platform/build/+/master/Changes.md#PATH_Tools for more information.",
146 }
147 if len(log.Parents) > 0 {
148 procPrints = append(procPrints, "Process tree:")
149 for i, proc := range log.Parents {
150 procPrints = append(procPrints, fmt.Sprintf("%s→ %s", strings.Repeat(" ", i), proc.Command))
151 }
152 }
153
154 config := paths.GetConfig(log.Basename)
155 if config.Error {
156 ctx.Printf("Disallowed PATH tool %q used: %#v", log.Basename, log.Args)
157 for _, line := range procPrints {
158 ctx.Println(line)
159 }
160 } else {
161 ctx.Verbosef("Unknown PATH tool %q used: %#v", log.Basename, log.Args)
162 for _, line := range procPrints {
163 ctx.Verboseln(line)
164 }
165 }
166 }
167 }()
Harsh Shandilya6cc5f3e2019-09-14 15:45:51 +0530168*/
Dan Willemsen18490112018-05-25 16:30:04 -0700169
170 ensureEmptyDirectoriesExist(ctx, myPath)
171
172 var execs []string
173 for _, pathEntry := range filepath.SplitList(origPath) {
174 if pathEntry == "" {
175 // Ignore the current directory
176 continue
177 }
178 // TODO(dwillemsen): remove path entries under TOP? or anything
179 // that looks like an android source dir? They won't exist on
180 // the build servers, since they're added by envsetup.sh.
181 // (Except for the JDK, which is configured in ui/build/config.go)
182
183 execs = append(execs, parsePathDir(pathEntry)...)
184 }
185
186 allowAllSymlinks := config.Environment().IsEnvTrue("TEMPORARY_DISABLE_PATH_RESTRICTIONS")
187 for _, name := range execs {
188 if !paths.GetConfig(name).Symlink && !allowAllSymlinks {
189 continue
190 }
191
192 err := os.Symlink("../.path_interposer", filepath.Join(myPath, name))
193 // Intentionally ignore existing files -- that means that we
194 // just created it, and the first one should win.
195 if err != nil && !os.IsExist(err) {
196 ctx.Fatalln("Failed to create symlink:", err)
197 }
198 }
199
200 myPath, _ = filepath.Abs(myPath)
Dan Willemsen417be1f2018-10-30 23:18:54 -0700201
Dan Willemsen91219732019-02-14 20:00:56 -0800202 // We put some prebuilts in $PATH, since it's infeasible to add dependencies for all of
203 // them.
Dan Willemsen733547d2019-02-14 20:11:26 -0800204 prebuiltsPath, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86")
205 myPath = prebuiltsPath + string(os.PathListSeparator) + myPath
Dan Willemsen417be1f2018-10-30 23:18:54 -0700206
Dan Willemsen18490112018-05-25 16:30:04 -0700207 config.Environment().Set("PATH", myPath)
208 config.pathReplaced = true
209}