xref: /aosp_15_r20/build/soong/cmd/fileslist/fileslist.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1*333d2b36SAndroid Build Coastguard Worker// Copyright 2017 Google Inc. All rights reserved.
2*333d2b36SAndroid Build Coastguard Worker//
3*333d2b36SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*333d2b36SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*333d2b36SAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*333d2b36SAndroid Build Coastguard Worker//
7*333d2b36SAndroid Build Coastguard Worker//     http://www.apache.org/licenses/LICENSE-2.0
8*333d2b36SAndroid Build Coastguard Worker//
9*333d2b36SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*333d2b36SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*333d2b36SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*333d2b36SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*333d2b36SAndroid Build Coastguard Worker// limitations under the License.
14*333d2b36SAndroid Build Coastguard Worker
15*333d2b36SAndroid Build Coastguard Worker// fileslist.py replacement written in GO, which utilizes multi-cores.
16*333d2b36SAndroid Build Coastguard Worker
17*333d2b36SAndroid Build Coastguard Workerpackage main
18*333d2b36SAndroid Build Coastguard Worker
19*333d2b36SAndroid Build Coastguard Workerimport (
20*333d2b36SAndroid Build Coastguard Worker	"crypto/sha256"
21*333d2b36SAndroid Build Coastguard Worker	"encoding/json"
22*333d2b36SAndroid Build Coastguard Worker	"flag"
23*333d2b36SAndroid Build Coastguard Worker	"fmt"
24*333d2b36SAndroid Build Coastguard Worker	"io"
25*333d2b36SAndroid Build Coastguard Worker	"os"
26*333d2b36SAndroid Build Coastguard Worker	"path/filepath"
27*333d2b36SAndroid Build Coastguard Worker	"runtime"
28*333d2b36SAndroid Build Coastguard Worker	"sort"
29*333d2b36SAndroid Build Coastguard Worker	"strings"
30*333d2b36SAndroid Build Coastguard Worker	"sync"
31*333d2b36SAndroid Build Coastguard Worker)
32*333d2b36SAndroid Build Coastguard Worker
33*333d2b36SAndroid Build Coastguard Workerconst (
34*333d2b36SAndroid Build Coastguard Worker	MAX_DEFAULT_PARA = 24
35*333d2b36SAndroid Build Coastguard Worker)
36*333d2b36SAndroid Build Coastguard Worker
37*333d2b36SAndroid Build Coastguard Workerfunc defaultPara() int {
38*333d2b36SAndroid Build Coastguard Worker	ret := runtime.NumCPU()
39*333d2b36SAndroid Build Coastguard Worker	if ret > MAX_DEFAULT_PARA {
40*333d2b36SAndroid Build Coastguard Worker		return MAX_DEFAULT_PARA
41*333d2b36SAndroid Build Coastguard Worker	}
42*333d2b36SAndroid Build Coastguard Worker	return ret
43*333d2b36SAndroid Build Coastguard Worker}
44*333d2b36SAndroid Build Coastguard Worker
45*333d2b36SAndroid Build Coastguard Workervar (
46*333d2b36SAndroid Build Coastguard Worker	para = flag.Int("para", defaultPara(), "Number of goroutines")
47*333d2b36SAndroid Build Coastguard Worker)
48*333d2b36SAndroid Build Coastguard Worker
49*333d2b36SAndroid Build Coastguard Worker// Represents each file.
50*333d2b36SAndroid Build Coastguard Workertype Node struct {
51*333d2b36SAndroid Build Coastguard Worker	SHA256 string
52*333d2b36SAndroid Build Coastguard Worker	Name   string // device side path.
53*333d2b36SAndroid Build Coastguard Worker	Size   int64
54*333d2b36SAndroid Build Coastguard Worker	path   string // host side path.
55*333d2b36SAndroid Build Coastguard Worker	stat   os.FileInfo
56*333d2b36SAndroid Build Coastguard Worker}
57*333d2b36SAndroid Build Coastguard Worker
58*333d2b36SAndroid Build Coastguard Workerfunc newNode(hostPath string, devicePath string, stat os.FileInfo) Node {
59*333d2b36SAndroid Build Coastguard Worker	return Node{Name: devicePath, path: hostPath, stat: stat}
60*333d2b36SAndroid Build Coastguard Worker}
61*333d2b36SAndroid Build Coastguard Worker
62*333d2b36SAndroid Build Coastguard Worker// Scan a Node and returns true if it should be added to the result.
63*333d2b36SAndroid Build Coastguard Workerfunc (n *Node) scan() bool {
64*333d2b36SAndroid Build Coastguard Worker	n.Size = n.stat.Size()
65*333d2b36SAndroid Build Coastguard Worker
66*333d2b36SAndroid Build Coastguard Worker	// Calculate SHA256.
67*333d2b36SAndroid Build Coastguard Worker	h := sha256.New()
68*333d2b36SAndroid Build Coastguard Worker	if n.stat.Mode()&os.ModeSymlink == 0 {
69*333d2b36SAndroid Build Coastguard Worker		f, err := os.Open(n.path)
70*333d2b36SAndroid Build Coastguard Worker		if err != nil {
71*333d2b36SAndroid Build Coastguard Worker			panic(err)
72*333d2b36SAndroid Build Coastguard Worker		}
73*333d2b36SAndroid Build Coastguard Worker		defer f.Close()
74*333d2b36SAndroid Build Coastguard Worker
75*333d2b36SAndroid Build Coastguard Worker		if _, err := io.Copy(h, f); err != nil {
76*333d2b36SAndroid Build Coastguard Worker			panic(err)
77*333d2b36SAndroid Build Coastguard Worker		}
78*333d2b36SAndroid Build Coastguard Worker	} else {
79*333d2b36SAndroid Build Coastguard Worker		// Hash the content of symlink, not the file it points to.
80*333d2b36SAndroid Build Coastguard Worker		s, err := os.Readlink(n.path)
81*333d2b36SAndroid Build Coastguard Worker		if err != nil {
82*333d2b36SAndroid Build Coastguard Worker			panic(err)
83*333d2b36SAndroid Build Coastguard Worker		}
84*333d2b36SAndroid Build Coastguard Worker		if _, err := io.WriteString(h, s); err != nil {
85*333d2b36SAndroid Build Coastguard Worker			panic(err)
86*333d2b36SAndroid Build Coastguard Worker		}
87*333d2b36SAndroid Build Coastguard Worker	}
88*333d2b36SAndroid Build Coastguard Worker	n.SHA256 = fmt.Sprintf("%x", h.Sum(nil))
89*333d2b36SAndroid Build Coastguard Worker	return true
90*333d2b36SAndroid Build Coastguard Worker}
91*333d2b36SAndroid Build Coastguard Worker
92*333d2b36SAndroid Build Coastguard Workerfunc main() {
93*333d2b36SAndroid Build Coastguard Worker	flag.Parse()
94*333d2b36SAndroid Build Coastguard Worker
95*333d2b36SAndroid Build Coastguard Worker	allOutput := make([]Node, 0, 1024) // Store all outputs.
96*333d2b36SAndroid Build Coastguard Worker	mutex := &sync.Mutex{}             // Guard allOutput
97*333d2b36SAndroid Build Coastguard Worker
98*333d2b36SAndroid Build Coastguard Worker	ch := make(chan Node) // Pass nodes to goroutines.
99*333d2b36SAndroid Build Coastguard Worker
100*333d2b36SAndroid Build Coastguard Worker	var wg sync.WaitGroup // To wait for all goroutines.
101*333d2b36SAndroid Build Coastguard Worker	wg.Add(*para)
102*333d2b36SAndroid Build Coastguard Worker
103*333d2b36SAndroid Build Coastguard Worker	// Scan files in multiple goroutines.
104*333d2b36SAndroid Build Coastguard Worker	for i := 0; i < *para; i++ {
105*333d2b36SAndroid Build Coastguard Worker		go func() {
106*333d2b36SAndroid Build Coastguard Worker			defer wg.Done()
107*333d2b36SAndroid Build Coastguard Worker
108*333d2b36SAndroid Build Coastguard Worker			output := make([]Node, 0, 1024) // Local output list.
109*333d2b36SAndroid Build Coastguard Worker			for node := range ch {
110*333d2b36SAndroid Build Coastguard Worker				if node.scan() {
111*333d2b36SAndroid Build Coastguard Worker					output = append(output, node)
112*333d2b36SAndroid Build Coastguard Worker				}
113*333d2b36SAndroid Build Coastguard Worker			}
114*333d2b36SAndroid Build Coastguard Worker			// Add to the global output list.
115*333d2b36SAndroid Build Coastguard Worker			mutex.Lock()
116*333d2b36SAndroid Build Coastguard Worker			allOutput = append(allOutput, output...)
117*333d2b36SAndroid Build Coastguard Worker			mutex.Unlock()
118*333d2b36SAndroid Build Coastguard Worker		}()
119*333d2b36SAndroid Build Coastguard Worker	}
120*333d2b36SAndroid Build Coastguard Worker
121*333d2b36SAndroid Build Coastguard Worker	// Walk the directories and find files to scan.
122*333d2b36SAndroid Build Coastguard Worker	for _, dir := range flag.Args() {
123*333d2b36SAndroid Build Coastguard Worker		absDir, err := filepath.Abs(dir)
124*333d2b36SAndroid Build Coastguard Worker		if err != nil {
125*333d2b36SAndroid Build Coastguard Worker			panic(err)
126*333d2b36SAndroid Build Coastguard Worker		}
127*333d2b36SAndroid Build Coastguard Worker		deviceRoot := filepath.Clean(absDir + "/..")
128*333d2b36SAndroid Build Coastguard Worker		err = filepath.Walk(dir, func(path string, stat os.FileInfo, err error) error {
129*333d2b36SAndroid Build Coastguard Worker			if err != nil {
130*333d2b36SAndroid Build Coastguard Worker				panic(err)
131*333d2b36SAndroid Build Coastguard Worker			}
132*333d2b36SAndroid Build Coastguard Worker			if stat.IsDir() {
133*333d2b36SAndroid Build Coastguard Worker				return nil
134*333d2b36SAndroid Build Coastguard Worker			}
135*333d2b36SAndroid Build Coastguard Worker			absPath, err := filepath.Abs(path)
136*333d2b36SAndroid Build Coastguard Worker			if err != nil {
137*333d2b36SAndroid Build Coastguard Worker				panic(err)
138*333d2b36SAndroid Build Coastguard Worker			}
139*333d2b36SAndroid Build Coastguard Worker			devicePath, err := filepath.Rel(deviceRoot, absPath)
140*333d2b36SAndroid Build Coastguard Worker			if err != nil {
141*333d2b36SAndroid Build Coastguard Worker				panic(err)
142*333d2b36SAndroid Build Coastguard Worker			}
143*333d2b36SAndroid Build Coastguard Worker			devicePath = "/" + devicePath
144*333d2b36SAndroid Build Coastguard Worker			ch <- newNode(absPath, devicePath, stat)
145*333d2b36SAndroid Build Coastguard Worker			return nil
146*333d2b36SAndroid Build Coastguard Worker		})
147*333d2b36SAndroid Build Coastguard Worker		if err != nil {
148*333d2b36SAndroid Build Coastguard Worker			panic(err)
149*333d2b36SAndroid Build Coastguard Worker		}
150*333d2b36SAndroid Build Coastguard Worker	}
151*333d2b36SAndroid Build Coastguard Worker
152*333d2b36SAndroid Build Coastguard Worker	// Wait until all the goroutines finish.
153*333d2b36SAndroid Build Coastguard Worker	close(ch)
154*333d2b36SAndroid Build Coastguard Worker	wg.Wait()
155*333d2b36SAndroid Build Coastguard Worker
156*333d2b36SAndroid Build Coastguard Worker	// Sort the entries and dump as json.
157*333d2b36SAndroid Build Coastguard Worker	sort.Slice(allOutput, func(i, j int) bool {
158*333d2b36SAndroid Build Coastguard Worker		if allOutput[i].Size > allOutput[j].Size {
159*333d2b36SAndroid Build Coastguard Worker			return true
160*333d2b36SAndroid Build Coastguard Worker		}
161*333d2b36SAndroid Build Coastguard Worker		if allOutput[i].Size == allOutput[j].Size && strings.Compare(allOutput[i].Name, allOutput[j].Name) > 0 {
162*333d2b36SAndroid Build Coastguard Worker			return true
163*333d2b36SAndroid Build Coastguard Worker		}
164*333d2b36SAndroid Build Coastguard Worker		return false
165*333d2b36SAndroid Build Coastguard Worker	})
166*333d2b36SAndroid Build Coastguard Worker
167*333d2b36SAndroid Build Coastguard Worker	j, err := json.MarshalIndent(allOutput, "", "  ")
168*333d2b36SAndroid Build Coastguard Worker	if err != nil {
169*333d2b36SAndroid Build Coastguard Worker		panic(nil)
170*333d2b36SAndroid Build Coastguard Worker	}
171*333d2b36SAndroid Build Coastguard Worker
172*333d2b36SAndroid Build Coastguard Worker	fmt.Printf("%s\n", j)
173*333d2b36SAndroid Build Coastguard Worker}
174