blob: ee72cfd43c5517e227d536005564d82e9ff672e2 [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 (
18 "fmt"
19 "io/ioutil"
20 "os"
21 "path/filepath"
Dan Willemsen417be1f2018-10-30 23:18:54 -070022 "runtime"
Dan Willemsen18490112018-05-25 16:30:04 -070023 "strings"
24
25 "github.com/google/blueprint/microfactory"
26
27 "android/soong/ui/build/paths"
Nan Zhang17f27672018-12-12 16:01:49 -080028 "android/soong/ui/metrics"
Dan Willemsen18490112018-05-25 16:30:04 -070029)
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
Nan Zhang17f27672018-12-12 16:01:49 -080061 ctx.BeginTrace(metrics.RunSetupTool, "path")
Dan Willemsen18490112018-05-25 16:30:04 -070062 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)
Dan Willemsen417be1f2018-10-30 23:18:54 -0700149
150 // Use the toybox prebuilts on linux
151 if runtime.GOOS == "linux" {
152 toyboxPath, _ := filepath.Abs("prebuilts/build-tools/toybox/linux-x86")
153 myPath = toyboxPath + string(os.PathListSeparator) + myPath
154 }
155
Dan Willemsen18490112018-05-25 16:30:04 -0700156 config.Environment().Set("PATH", myPath)
157 config.pathReplaced = true
158}