1// Copyright 2017 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	"android/soong/finder"
19	"android/soong/finder/fs"
20	"android/soong/ui/logger"
21	"bytes"
22	"io/ioutil"
23	"os"
24	"path/filepath"
25	"strings"
26
27	"android/soong/ui/metrics"
28)
29
30// This file provides an interface to the Finder for use in Soong UI
31// This file stores configuration information about which files to find
32
33// NewSourceFinder returns a new Finder configured to search for source files.
34// Callers of NewSourceFinder should call <f.Shutdown()> when done
35func NewSourceFinder(ctx Context, config Config) (f *finder.Finder) {
36	ctx.BeginTrace(metrics.RunSetupTool, "find modules")
37	defer ctx.EndTrace()
38
39	dir, err := os.Getwd()
40	if err != nil {
41		ctx.Fatalf("No working directory for module-finder: %v", err.Error())
42	}
43	filesystem := fs.OsFs
44
45	// if the root dir is ignored, then the subsequent error messages are very confusing,
46	// so check for that upfront
47	pruneFiles := []string{".out-dir", ".find-ignore"}
48	for _, name := range pruneFiles {
49		prunePath := filepath.Join(dir, name)
50		_, statErr := filesystem.Lstat(prunePath)
51		if statErr == nil {
52			ctx.Fatalf("%v must not exist", prunePath)
53		}
54	}
55
56	cacheParams := finder.CacheParams{
57		WorkingDirectory: dir,
58		RootDirs:         []string{"."},
59		ExcludeDirs:      []string{".git", ".repo"},
60		PruneFiles:       pruneFiles,
61		IncludeFiles: []string{
62			"Android.mk",
63			"AndroidProducts.mk",
64			"Android.bp",
65			"Blueprints",
66			"CleanSpec.mk",
67			"OWNERS",
68			"TEST_MAPPING",
69		},
70	}
71	dumpDir := config.FileListDir()
72	f, err = finder.New(cacheParams, filesystem, logger.New(ioutil.Discard),
73		filepath.Join(dumpDir, "files.db"))
74	if err != nil {
75		ctx.Fatalf("Could not create module-finder: %v", err)
76	}
77	return f
78}
79
80// FindSources searches for source files known to <f> and writes them to the filesystem for
81// use later.
82func FindSources(ctx Context, config Config, f *finder.Finder) {
83	// note that dumpDir in FindSources may be different than dumpDir in NewSourceFinder
84	// if a caller such as multiproduct_kati wants to share one Finder among several builds
85	dumpDir := config.FileListDir()
86	os.MkdirAll(dumpDir, 0777)
87
88	androidMks := f.FindFirstNamedAt(".", "Android.mk")
89	err := dumpListToFile(ctx, config, androidMks, filepath.Join(dumpDir, "Android.mk.list"))
90	if err != nil {
91		ctx.Fatalf("Could not export module list: %v", err)
92	}
93
94	androidProductsMks := f.FindNamedAt("device", "AndroidProducts.mk")
95	androidProductsMks = append(androidProductsMks, f.FindNamedAt("vendor", "AndroidProducts.mk")...)
96	androidProductsMks = append(androidProductsMks, f.FindNamedAt("product", "AndroidProducts.mk")...)
97	err = dumpListToFile(ctx, config, androidProductsMks, filepath.Join(dumpDir, "AndroidProducts.mk.list"))
98	if err != nil {
99		ctx.Fatalf("Could not export product list: %v", err)
100	}
101
102	cleanSpecs := f.FindFirstNamedAt(".", "CleanSpec.mk")
103	err = dumpListToFile(ctx, config, cleanSpecs, filepath.Join(dumpDir, "CleanSpec.mk.list"))
104	if err != nil {
105		ctx.Fatalf("Could not export module list: %v", err)
106	}
107
108	owners := f.FindNamedAt(".", "OWNERS")
109	err = dumpListToFile(ctx, config, owners, filepath.Join(dumpDir, "OWNERS.list"))
110	if err != nil {
111		ctx.Fatalf("Could not find OWNERS: %v", err)
112	}
113
114	testMappings := f.FindNamedAt(".", "TEST_MAPPING")
115	err = dumpListToFile(ctx, config, testMappings, filepath.Join(dumpDir, "TEST_MAPPING.list"))
116	if err != nil {
117		ctx.Fatalf("Could not find TEST_MAPPING: %v", err)
118	}
119
120	androidBps := f.FindNamedAt(".", "Android.bp")
121	androidBps = append(androidBps, f.FindNamedAt("build/blueprint", "Blueprints")...)
122	if len(androidBps) == 0 {
123		ctx.Fatalf("No Android.bp found")
124	}
125	err = dumpListToFile(ctx, config, androidBps, filepath.Join(dumpDir, "Android.bp.list"))
126	if err != nil {
127		ctx.Fatalf("Could not find modules: %v", err)
128	}
129
130	if config.Dist() {
131		f.WaitForDbDump()
132		distFile(ctx, config, f.DbPath, "module_paths")
133	}
134}
135
136func dumpListToFile(ctx Context, config Config, list []string, filePath string) (err error) {
137	desiredText := strings.Join(list, "\n")
138	desiredBytes := []byte(desiredText)
139	actualBytes, readErr := ioutil.ReadFile(filePath)
140	if readErr != nil || !bytes.Equal(desiredBytes, actualBytes) {
141		err = ioutil.WriteFile(filePath, desiredBytes, 0777)
142		if err != nil {
143			return err
144		}
145	}
146
147	distFile(ctx, config, filePath, "module_paths")
148
149	return nil
150}
151