211 lines
5.6 KiB
Go
211 lines
5.6 KiB
Go
// Copyright 2018 Google Inc. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package build
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"github.com/google/blueprint/microfactory"
|
|
|
|
"android/soong/ui/build/paths"
|
|
"android/soong/ui/metrics"
|
|
)
|
|
|
|
func parsePathDir(dir string) []string {
|
|
f, err := os.Open(dir)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
defer f.Close()
|
|
|
|
if s, err := f.Stat(); err != nil || !s.IsDir() {
|
|
return nil
|
|
}
|
|
|
|
infos, err := f.Readdir(-1)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
ret := make([]string, 0, len(infos))
|
|
for _, info := range infos {
|
|
if m := info.Mode(); !m.IsDir() && m&0111 != 0 {
|
|
ret = append(ret, info.Name())
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// A "lite" version of SetupPath used for dumpvars, or other places that need
|
|
// minimal overhead (but at the expense of logging). If tmpDir is empty, the
|
|
// default TMPDIR is used from config.
|
|
func SetupLitePath(ctx Context, config Config, tmpDir string) {
|
|
if config.pathReplaced {
|
|
return
|
|
}
|
|
|
|
ctx.BeginTrace(metrics.RunSetupTool, "litepath")
|
|
defer ctx.EndTrace()
|
|
|
|
origPath, _ := config.Environment().Get("PATH")
|
|
|
|
if tmpDir == "" {
|
|
tmpDir, _ = config.Environment().Get("TMPDIR")
|
|
}
|
|
myPath := filepath.Join(tmpDir, "path")
|
|
ensureEmptyDirectoriesExist(ctx, myPath)
|
|
|
|
os.Setenv("PATH", origPath)
|
|
for name, pathConfig := range paths.Configuration {
|
|
if !pathConfig.Symlink {
|
|
continue
|
|
}
|
|
|
|
origExec, err := exec.LookPath(name)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
origExec, err = filepath.Abs(origExec)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
err = os.Symlink(origExec, filepath.Join(myPath, name))
|
|
if err != nil {
|
|
ctx.Fatalln("Failed to create symlink:", err)
|
|
}
|
|
}
|
|
|
|
myPath, _ = filepath.Abs(myPath)
|
|
|
|
prebuiltsPath, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86")
|
|
myPath = prebuiltsPath + string(os.PathListSeparator) + myPath
|
|
|
|
config.Environment().Set("PATH", myPath)
|
|
config.pathReplaced = true
|
|
}
|
|
|
|
func SetupPath(ctx Context, config Config) {
|
|
if config.pathReplaced {
|
|
return
|
|
}
|
|
|
|
ctx.BeginTrace(metrics.RunSetupTool, "path")
|
|
defer ctx.EndTrace()
|
|
|
|
origPath, _ := config.Environment().Get("PATH")
|
|
myPath := filepath.Join(config.OutDir(), ".path")
|
|
interposer := myPath + "_interposer"
|
|
|
|
var cfg microfactory.Config
|
|
cfg.Map("android/soong", "build/soong")
|
|
cfg.TrimPath, _ = filepath.Abs(".")
|
|
if _, err := microfactory.Build(&cfg, interposer, "android/soong/cmd/path_interposer"); err != nil {
|
|
ctx.Fatalln("Failed to build path interposer:", err)
|
|
}
|
|
|
|
if err := ioutil.WriteFile(interposer+"_origpath", []byte(origPath), 0777); err != nil {
|
|
ctx.Fatalln("Failed to write original path:", err)
|
|
}
|
|
|
|
entries, err := paths.LogListener(ctx.Context, interposer+"_log")
|
|
if err != nil {
|
|
ctx.Fatalln("Failed to listen for path logs:", err)
|
|
}
|
|
|
|
go func() {
|
|
for log := range entries {
|
|
curPid := os.Getpid()
|
|
for i, proc := range log.Parents {
|
|
if proc.Pid == curPid {
|
|
log.Parents = log.Parents[i:]
|
|
break
|
|
}
|
|
}
|
|
procPrints := []string{
|
|
"See https://android.googlesource.com/platform/build/+/master/Changes.md#PATH_Tools for more information.",
|
|
}
|
|
if len(log.Parents) > 0 {
|
|
procPrints = append(procPrints, "Process tree:")
|
|
for i, proc := range log.Parents {
|
|
procPrints = append(procPrints, fmt.Sprintf("%s→ %s", strings.Repeat(" ", i), proc.Command))
|
|
}
|
|
}
|
|
|
|
config := paths.GetConfig(log.Basename)
|
|
if config.Error {
|
|
ctx.Printf("Disallowed PATH tool %q used: %#v", log.Basename, log.Args)
|
|
for _, line := range procPrints {
|
|
ctx.Println(line)
|
|
}
|
|
} else {
|
|
ctx.Verbosef("Unknown PATH tool %q used: %#v", log.Basename, log.Args)
|
|
for _, line := range procPrints {
|
|
ctx.Verboseln(line)
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
|
|
ensureEmptyDirectoriesExist(ctx, myPath)
|
|
|
|
var execs []string
|
|
for _, pathEntry := range filepath.SplitList(origPath) {
|
|
if pathEntry == "" {
|
|
// Ignore the current directory
|
|
continue
|
|
}
|
|
// TODO(dwillemsen): remove path entries under TOP? or anything
|
|
// that looks like an android source dir? They won't exist on
|
|
// the build servers, since they're added by envsetup.sh.
|
|
// (Except for the JDK, which is configured in ui/build/config.go)
|
|
|
|
execs = append(execs, parsePathDir(pathEntry)...)
|
|
}
|
|
|
|
if config.Environment().IsEnvTrue("TEMPORARY_DISABLE_PATH_RESTRICTIONS") {
|
|
ctx.Fatalln("TEMPORARY_DISABLE_PATH_RESTRICTIONS was a temporary migration method, and is now obsolete.")
|
|
}
|
|
|
|
for _, name := range execs {
|
|
if !paths.GetConfig(name).Symlink {
|
|
continue
|
|
}
|
|
|
|
err := os.Symlink("../.path_interposer", filepath.Join(myPath, name))
|
|
// Intentionally ignore existing files -- that means that we
|
|
// just created it, and the first one should win.
|
|
if err != nil && !os.IsExist(err) {
|
|
ctx.Fatalln("Failed to create symlink:", err)
|
|
}
|
|
}
|
|
|
|
myPath, _ = filepath.Abs(myPath)
|
|
|
|
// We put some prebuilts in $PATH, since it's infeasible to add dependencies for all of
|
|
// them.
|
|
prebuiltsPath, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86")
|
|
myPath = prebuiltsPath + string(os.PathListSeparator) + myPath
|
|
|
|
config.Environment().Set("PATH", myPath)
|
|
config.pathReplaced = true
|
|
}
|