Allow primary builder to change working directory

Bug: 146437378
Test: pathtools/fs_test.go
Change-Id: I513ceb9b8b0b4f18223bc34ecad9846fe220709b
This commit is contained in:
Colin Cross 2019-12-17 13:12:35 -08:00
parent 55ca1c0860
commit c5fa50e057
7 changed files with 311 additions and 173 deletions

View file

@ -691,7 +691,7 @@ func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
if s.config.stage == StagePrimary {
mainNinjaFile := filepath.Join("$buildDir", "build.ninja")
primaryBuilderNinjaGlobFile := filepath.Join(BuildDir, bootstrapSubDir, "build-globs.ninja")
primaryBuilderNinjaGlobFile := absolutePath(filepath.Join(BuildDir, bootstrapSubDir, "build-globs.ninja"))
if _, err := os.Stat(primaryBuilderNinjaGlobFile); os.IsNotExist(err) {
err = ioutil.WriteFile(primaryBuilderNinjaGlobFile, nil, 0666)

View file

@ -70,7 +70,7 @@ func removeAbandonedFilesUnder(ctx *blueprint.Context, config *Config,
for _, filePath := range filePaths {
isTarget := targets[filePath]
if !isTarget {
err = removeFileAndEmptyDirs(filePath)
err = removeFileAndEmptyDirs(absolutePath(filePath))
if err != nil {
return err
}

View file

@ -47,6 +47,8 @@ var (
BuildDir string
NinjaBuildDir string
SrcDir string
absSrcDir string
)
func init() {
@ -76,8 +78,10 @@ func Main(ctx *blueprint.Context, config interface{}, extraNinjaFileDeps ...stri
debug.SetGCPercent(-1)
}
absSrcDir = ctx.SrcDir()
if cpuprofile != "" {
f, err := os.Create(cpuprofile)
f, err := os.Create(absolutePath(cpuprofile))
if err != nil {
fatalf("error opening cpuprofile: %s", err)
}
@ -87,7 +91,7 @@ func Main(ctx *blueprint.Context, config interface{}, extraNinjaFileDeps ...stri
}
if traceFile != "" {
f, err := os.Create(traceFile)
f, err := os.Create(absolutePath(traceFile))
if err != nil {
fatalf("error opening trace: %s", err)
}
@ -155,7 +159,7 @@ func Main(ctx *blueprint.Context, config interface{}, extraNinjaFileDeps ...stri
deps = append(deps, extraDeps...)
if docFile != "" {
err := writeDocs(ctx, docFile)
err := writeDocs(ctx, absolutePath(docFile))
if err != nil {
fatalErrors([]error{err})
}
@ -180,7 +184,7 @@ func Main(ctx *blueprint.Context, config interface{}, extraNinjaFileDeps ...stri
var buf *bufio.Writer
if stage != StageMain || !emptyNinjaFile {
f, err = os.OpenFile(outFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, outFilePermissions)
f, err = os.OpenFile(absolutePath(outFile), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, outFilePermissions)
if err != nil {
fatalf("error opening Ninja file: %s", err)
}
@ -215,14 +219,14 @@ func Main(ctx *blueprint.Context, config interface{}, extraNinjaFileDeps ...stri
fatalErrors(errs)
}
err = ioutil.WriteFile(globFile, buffer, outFilePermissions)
err = ioutil.WriteFile(absolutePath(globFile), buffer, outFilePermissions)
if err != nil {
fatalf("error writing %s: %s", outFile, err)
fatalf("error writing %s: %s", globFile, err)
}
}
if depFile != "" {
err := deptools.WriteDepFile(depFile, outFile, deps)
err := deptools.WriteDepFile(absolutePath(depFile), outFile, deps)
if err != nil {
fatalf("error writing depfile: %s", err)
}
@ -237,7 +241,7 @@ func Main(ctx *blueprint.Context, config interface{}, extraNinjaFileDeps ...stri
}
if memprofile != "" {
f, err := os.Create(memprofile)
f, err := os.Create(absolutePath(memprofile))
if err != nil {
fatalf("error opening memprofile: %s", err)
}
@ -268,3 +272,10 @@ func fatalErrors(errs []error) {
}
os.Exit(1)
}
func absolutePath(path string) string {
if filepath.IsAbs(path) {
return path
}
return filepath.Join(absSrcDir, path)
}

View file

@ -30,7 +30,7 @@ func bootstrapVariable(name string, value func() string) blueprint.Variable {
}
var (
// These variables are the only configuration needed by the boostrap
// These variables are the only configuration needed by the bootstrap
// modules.
srcDir = bootstrapVariable("srcDir", func() string {
return SrcDir

View file

@ -114,6 +114,7 @@ type Context struct {
globs map[string]GlobPath
globLock sync.Mutex
srcDir string
fs pathtools.FileSystem
moduleListFile string
}
@ -448,6 +449,15 @@ func (c *Context) SetNameInterface(i NameInterface) {
c.nameInterface = i
}
func (c *Context) SetSrcDir(path string) {
c.srcDir = path
c.fs = pathtools.NewOsFs(path)
}
func (c *Context) SrcDir() string {
return c.srcDir
}
func singletonPkgPath(singleton Singleton) string {
typ := reflect.TypeOf(singleton)
for typ.Kind() == reflect.Ptr {

View file

@ -36,7 +36,7 @@ const (
DontFollowSymlinks = ShouldFollowSymlinks(false)
)
var OsFs FileSystem = osFs{}
var OsFs FileSystem = &osFs{}
func MockFs(files map[string][]byte) FileSystem {
fs := &mockFs{
@ -123,11 +123,45 @@ type FileSystem interface {
}
// osFs implements FileSystem using the local disk.
type osFs struct{}
type osFs struct {
srcDir string
}
func (osFs) Open(name string) (ReaderAtSeekerCloser, error) { return os.Open(name) }
func (osFs) Exists(name string) (bool, bool, error) {
stat, err := os.Stat(name)
func NewOsFs(path string) FileSystem {
return &osFs{srcDir: path}
}
func (fs *osFs) removeSrcDirPrefix(path string) string {
if fs.srcDir == "" {
return path
}
rel, err := filepath.Rel(fs.srcDir, path)
if err != nil {
panic(fmt.Errorf("unexpected failure in removeSrcDirPrefix filepath.Rel(%s, %s): %s",
fs.srcDir, path, err))
}
if strings.HasPrefix(rel, "../") {
panic(fmt.Errorf("unexpected relative path outside directory in removeSrcDirPrefix filepath.Rel(%s, %s): %s",
fs.srcDir, path, rel))
}
return rel
}
func (fs *osFs) removeSrcDirPrefixes(paths []string) []string {
if fs.srcDir != "" {
for i, path := range paths {
paths[i] = fs.removeSrcDirPrefix(path)
}
}
return paths
}
func (fs *osFs) Open(name string) (ReaderAtSeekerCloser, error) {
return os.Open(filepath.Join(fs.srcDir, name))
}
func (fs *osFs) Exists(name string) (bool, bool, error) {
stat, err := os.Stat(filepath.Join(fs.srcDir, name))
if err == nil {
return true, stat.IsDir(), nil
} else if os.IsNotExist(err) {
@ -137,45 +171,47 @@ func (osFs) Exists(name string) (bool, bool, error) {
}
}
func (osFs) IsDir(name string) (bool, error) {
info, err := os.Stat(name)
func (fs *osFs) IsDir(name string) (bool, error) {
info, err := os.Stat(filepath.Join(fs.srcDir, name))
if err != nil {
return false, err
}
return info.IsDir(), nil
}
func (osFs) IsSymlink(name string) (bool, error) {
if info, err := os.Lstat(name); err != nil {
func (fs *osFs) IsSymlink(name string) (bool, error) {
if info, err := os.Lstat(filepath.Join(fs.srcDir, name)); err != nil {
return false, err
} else {
return info.Mode()&os.ModeSymlink != 0, nil
}
}
func (fs osFs) Glob(pattern string, excludes []string, follow ShouldFollowSymlinks) (matches, dirs []string, err error) {
func (fs *osFs) Glob(pattern string, excludes []string, follow ShouldFollowSymlinks) (matches, dirs []string, err error) {
return startGlob(fs, pattern, excludes, follow)
}
func (osFs) glob(pattern string) ([]string, error) {
return filepath.Glob(pattern)
func (fs *osFs) glob(pattern string) ([]string, error) {
paths, err := filepath.Glob(filepath.Join(fs.srcDir, pattern))
fs.removeSrcDirPrefixes(paths)
return paths, err
}
func (osFs) Lstat(path string) (stats os.FileInfo, err error) {
return os.Lstat(path)
func (fs *osFs) Lstat(path string) (stats os.FileInfo, err error) {
return os.Lstat(filepath.Join(fs.srcDir, path))
}
func (osFs) Stat(path string) (stats os.FileInfo, err error) {
return os.Stat(path)
func (fs *osFs) Stat(path string) (stats os.FileInfo, err error) {
return os.Stat(filepath.Join(fs.srcDir, path))
}
// Returns a list of all directories under dir
func (osFs) ListDirsRecursive(name string, follow ShouldFollowSymlinks) (dirs []string, err error) {
return listDirsRecursive(OsFs, name, follow)
func (fs *osFs) ListDirsRecursive(name string, follow ShouldFollowSymlinks) (dirs []string, err error) {
return listDirsRecursive(fs, name, follow)
}
func (osFs) ReadDirNames(name string) ([]string, error) {
dir, err := os.Open(name)
func (fs *osFs) ReadDirNames(name string) ([]string, error) {
dir, err := os.Open(filepath.Join(fs.srcDir, name))
if err != nil {
return nil, err
}
@ -190,8 +226,8 @@ func (osFs) ReadDirNames(name string) ([]string, error) {
return contents, nil
}
func (osFs) Readlink(name string) (string, error) {
return os.Readlink(name)
func (fs *osFs) Readlink(name string) (string, error) {
return os.Readlink(filepath.Join(fs.srcDir, name))
}
type mockFs struct {

View file

@ -22,6 +22,8 @@ import (
"testing"
)
const testdataDir = "testdata/dangling"
func symlinkMockFs() *mockFs {
files := []string{
"a/a/a",
@ -149,14 +151,8 @@ func TestFs_IsDir(t *testing.T) {
}
mock := symlinkMockFs()
fsList := []FileSystem{mock, OsFs}
names := []string{"mock", "os"}
os.Chdir("testdata/dangling")
defer os.Chdir("../..")
for i, fs := range fsList {
t.Run(names[i], func(t *testing.T) {
run := func(t *testing.T, fs FileSystem) {
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
got, err := fs.IsDir(test.name)
@ -166,8 +162,26 @@ func TestFs_IsDir(t *testing.T) {
}
})
}
})
}
t.Run("mock", func(t *testing.T) {
run(t, mock)
})
t.Run("os", func(t *testing.T) {
os.Chdir("testdata/dangling")
defer os.Chdir("../..")
run(t, OsFs)
})
t.Run("os relative srcDir", func(t *testing.T) {
run(t, NewOsFs(testdataDir))
})
t.Run("os absolute srcDir", func(t *testing.T) {
wd, _ := os.Getwd()
run(t, NewOsFs(filepath.Join(wd, testdataDir)))
})
}
func TestFs_ListDirsRecursiveFollowSymlinks(t *testing.T) {
@ -200,15 +214,8 @@ func TestFs_ListDirsRecursiveFollowSymlinks(t *testing.T) {
}
mock := symlinkMockFs()
fsList := []FileSystem{mock, OsFs}
names := []string{"mock", "os"}
os.Chdir("testdata/dangling")
defer os.Chdir("../..")
for i, fs := range fsList {
t.Run(names[i], func(t *testing.T) {
run := func(t *testing.T, fs FileSystem) {
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
got, err := fs.ListDirsRecursive(test.name, FollowSymlinks)
@ -218,8 +225,26 @@ func TestFs_ListDirsRecursiveFollowSymlinks(t *testing.T) {
}
})
}
})
}
t.Run("mock", func(t *testing.T) {
run(t, mock)
})
t.Run("os", func(t *testing.T) {
os.Chdir("testdata/dangling")
defer os.Chdir("../..")
run(t, OsFs)
})
t.Run("os relative srcDir", func(t *testing.T) {
run(t, NewOsFs(testdataDir))
})
t.Run("os absolute srcDir", func(t *testing.T) {
wd, _ := os.Getwd()
run(t, NewOsFs(filepath.Join(wd, testdataDir)))
})
}
func TestFs_ListDirsRecursiveDontFollowSymlinks(t *testing.T) {
@ -252,15 +277,8 @@ func TestFs_ListDirsRecursiveDontFollowSymlinks(t *testing.T) {
}
mock := symlinkMockFs()
fsList := []FileSystem{mock, OsFs}
names := []string{"mock", "os"}
os.Chdir("testdata/dangling")
defer os.Chdir("../..")
for i, fs := range fsList {
t.Run(names[i], func(t *testing.T) {
run := func(t *testing.T, fs FileSystem) {
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
got, err := fs.ListDirsRecursive(test.name, DontFollowSymlinks)
@ -270,8 +288,26 @@ func TestFs_ListDirsRecursiveDontFollowSymlinks(t *testing.T) {
}
})
}
})
}
t.Run("mock", func(t *testing.T) {
run(t, mock)
})
t.Run("os", func(t *testing.T) {
os.Chdir("testdata/dangling")
defer os.Chdir("../..")
run(t, OsFs)
})
t.Run("os relative srcDir", func(t *testing.T) {
run(t, NewOsFs(testdataDir))
})
t.Run("os absolute srcDir", func(t *testing.T) {
wd, _ := os.Getwd()
run(t, NewOsFs(filepath.Join(wd, testdataDir)))
})
}
func TestFs_Readlink(t *testing.T) {
@ -321,15 +357,8 @@ func TestFs_Readlink(t *testing.T) {
}
mock := symlinkMockFs()
fsList := []FileSystem{mock, OsFs}
names := []string{"mock", "os"}
os.Chdir("testdata/dangling")
defer os.Chdir("../..")
for i, fs := range fsList {
t.Run(names[i], func(t *testing.T) {
run := func(t *testing.T, fs FileSystem) {
for _, test := range testCases {
t.Run(test.from, func(t *testing.T) {
got, err := fs.Readlink(test.from)
@ -339,8 +368,26 @@ func TestFs_Readlink(t *testing.T) {
}
})
}
})
}
t.Run("mock", func(t *testing.T) {
run(t, mock)
})
t.Run("os", func(t *testing.T) {
os.Chdir("testdata/dangling")
defer os.Chdir("../..")
run(t, OsFs)
})
t.Run("os relative srcDir", func(t *testing.T) {
run(t, NewOsFs(testdataDir))
})
t.Run("os absolute srcDir", func(t *testing.T) {
wd, _ := os.Getwd()
run(t, NewOsFs(filepath.Join(wd, testdataDir)))
})
}
func TestFs_Lstat(t *testing.T) {
@ -392,15 +439,8 @@ func TestFs_Lstat(t *testing.T) {
}
mock := symlinkMockFs()
fsList := []FileSystem{mock, OsFs}
names := []string{"mock", "os"}
os.Chdir("testdata/dangling")
defer os.Chdir("../..")
for i, fs := range fsList {
t.Run(names[i], func(t *testing.T) {
run := func(t *testing.T, fs FileSystem) {
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
got, err := fs.Lstat(test.name)
@ -417,8 +457,26 @@ func TestFs_Lstat(t *testing.T) {
}
})
}
})
}
t.Run("mock", func(t *testing.T) {
run(t, mock)
})
t.Run("os", func(t *testing.T) {
os.Chdir("testdata/dangling")
defer os.Chdir("../..")
run(t, OsFs)
})
t.Run("os relative srcDir", func(t *testing.T) {
run(t, NewOsFs(testdataDir))
})
t.Run("os absolute srcDir", func(t *testing.T) {
wd, _ := os.Getwd()
run(t, NewOsFs(filepath.Join(wd, testdataDir)))
})
}
func TestFs_Stat(t *testing.T) {
@ -470,15 +528,8 @@ func TestFs_Stat(t *testing.T) {
}
mock := symlinkMockFs()
fsList := []FileSystem{mock, OsFs}
names := []string{"mock", "os"}
os.Chdir("testdata/dangling")
defer os.Chdir("../..")
for i, fs := range fsList {
t.Run(names[i], func(t *testing.T) {
run := func(t *testing.T, fs FileSystem) {
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
got, err := fs.Stat(test.name)
@ -495,8 +546,26 @@ func TestFs_Stat(t *testing.T) {
}
})
}
})
}
t.Run("mock", func(t *testing.T) {
run(t, mock)
})
t.Run("os", func(t *testing.T) {
os.Chdir("testdata/dangling")
defer os.Chdir("../..")
run(t, OsFs)
})
t.Run("os relative srcDir", func(t *testing.T) {
run(t, NewOsFs(testdataDir))
})
t.Run("os absolute srcDir", func(t *testing.T) {
wd, _ := os.Getwd()
run(t, NewOsFs(filepath.Join(wd, testdataDir)))
})
}
func TestMockFs_glob(t *testing.T) {
@ -538,14 +607,8 @@ func TestMockFs_glob(t *testing.T) {
}
mock := symlinkMockFs()
fsList := []FileSystem{mock, OsFs}
names := []string{"mock", "os"}
os.Chdir("testdata/dangling")
defer os.Chdir("../..")
for i, fs := range fsList {
t.Run(names[i], func(t *testing.T) {
run := func(t *testing.T, fs FileSystem) {
for _, test := range testCases {
t.Run(test.pattern, func(t *testing.T) {
got, err := fs.glob(test.pattern)
@ -557,8 +620,26 @@ func TestMockFs_glob(t *testing.T) {
}
})
}
})
}
t.Run("mock", func(t *testing.T) {
run(t, mock)
})
t.Run("os", func(t *testing.T) {
os.Chdir("testdata/dangling")
defer os.Chdir("../..")
run(t, OsFs)
})
t.Run("os relative srcDir", func(t *testing.T) {
run(t, NewOsFs(testdataDir))
})
t.Run("os absolute srcDir", func(t *testing.T) {
wd, _ := os.Getwd()
run(t, NewOsFs(filepath.Join(wd, testdataDir)))
})
}
func syscallError(err error) error {