platform_build_soong/ui/build/path.go
Patrice Arruda ae2694b30d Pass the TMPDIR directly to the command when dumping makefile variables.
External applications such as metrics uploader depends on the TMPDIR.
DumpMakeVars is changing the TMPDIR environment variable value. Once
the work is done, TMPDIR directory is deleted. Additional flow of
the tool or external applications can panic since TMPDIR directory
does not exist. Since this is an isolated case, pass the TMPDIR
directory to the command of dumping the makefile variables.

Bug: b/140638454
Test: * Ran lunch command with ANDROID_ENABLE_METRICS_UPLOAD set
        and metrics_uploader did not crash.
      * m nothing
Change-Id: Ib78a3f8cfb5da48f65bd9bce1511e9abdf3d9cca
2020-06-05 17:28:41 +00:00

210 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
}