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