summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
author Joe Onorato <joeo@google.com> 2024-12-06 12:56:08 -0800
committer Joe Onorato <joeo@google.com> 2024-12-09 10:01:21 -0800
commitc77bdde587cc484b5a1f6176ebdc66da84926075 (patch)
tree6ca5b9eb957c4106bc6836173e8a1d3c5b5b95b5 /cmd
parente0d520fd7584d0c0c5939928f91544560adc937c (diff)
Add source_tree_size command to report names and sizes of all files in the source tree.
Bug: 383124666 Test: treehugger Change-Id: I91c0df7739b794e3a8d25f61f9efa8c3dbb15db1
Diffstat (limited to 'cmd')
-rw-r--r--cmd/source_tree_size/Android.bp28
-rwxr-xr-xcmd/source_tree_size/regen.sh4
-rw-r--r--cmd/source_tree_size/source_tree.pb.go230
-rw-r--r--cmd/source_tree_size/source_tree.proto27
-rw-r--r--cmd/source_tree_size/source_tree_size.go126
5 files changed, 415 insertions, 0 deletions
diff --git a/cmd/source_tree_size/Android.bp b/cmd/source_tree_size/Android.bp
new file mode 100644
index 000000000..97d29c684
--- /dev/null
+++ b/cmd/source_tree_size/Android.bp
@@ -0,0 +1,28 @@
+// Copyright 2024 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+blueprint_go_binary {
+ name: "source_tree_size",
+ srcs: [
+ "source_tree.pb.go",
+ "source_tree_size.go",
+ ],
+ deps: [
+ "golang-protobuf-runtime-protoimpl",
+ ],
+}
diff --git a/cmd/source_tree_size/regen.sh b/cmd/source_tree_size/regen.sh
new file mode 100755
index 000000000..b0f1c805d
--- /dev/null
+++ b/cmd/source_tree_size/regen.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+aprotoc --go_out=paths=source_relative:. source_tree.proto
+
diff --git a/cmd/source_tree_size/source_tree.pb.go b/cmd/source_tree_size/source_tree.pb.go
new file mode 100644
index 000000000..da8cba5e7
--- /dev/null
+++ b/cmd/source_tree_size/source_tree.pb.go
@@ -0,0 +1,230 @@
+// Copyright 2024 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.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.30.0
+// protoc v3.21.12
+// source: source_tree.proto
+
+package main
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type SourceFile struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Path *string `protobuf:"bytes,1,opt,name=path" json:"path,omitempty"`
+ SizeBytes *int32 `protobuf:"varint,2,opt,name=size_bytes,json=sizeBytes" json:"size_bytes,omitempty"`
+}
+
+func (x *SourceFile) Reset() {
+ *x = SourceFile{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_source_tree_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *SourceFile) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SourceFile) ProtoMessage() {}
+
+func (x *SourceFile) ProtoReflect() protoreflect.Message {
+ mi := &file_source_tree_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use SourceFile.ProtoReflect.Descriptor instead.
+func (*SourceFile) Descriptor() ([]byte, []int) {
+ return file_source_tree_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *SourceFile) GetPath() string {
+ if x != nil && x.Path != nil {
+ return *x.Path
+ }
+ return ""
+}
+
+func (x *SourceFile) GetSizeBytes() int32 {
+ if x != nil && x.SizeBytes != nil {
+ return *x.SizeBytes
+ }
+ return 0
+}
+
+type SourceTree struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Files []*SourceFile `protobuf:"bytes,1,rep,name=files" json:"files,omitempty"`
+}
+
+func (x *SourceTree) Reset() {
+ *x = SourceTree{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_source_tree_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *SourceTree) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SourceTree) ProtoMessage() {}
+
+func (x *SourceTree) ProtoReflect() protoreflect.Message {
+ mi := &file_source_tree_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use SourceTree.ProtoReflect.Descriptor instead.
+func (*SourceTree) Descriptor() ([]byte, []int) {
+ return file_source_tree_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *SourceTree) GetFiles() []*SourceFile {
+ if x != nil {
+ return x.Files
+ }
+ return nil
+}
+
+var File_source_tree_proto protoreflect.FileDescriptor
+
+var file_source_tree_proto_rawDesc = []byte{
+ 0x0a, 0x11, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65,
+ 0x73, 0x22, 0x3f, 0x0a, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12,
+ 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70,
+ 0x61, 0x74, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65,
+ 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x73, 0x69, 0x7a, 0x65, 0x42, 0x79, 0x74,
+ 0x65, 0x73, 0x22, 0x3c, 0x0a, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x72, 0x65, 0x65,
+ 0x12, 0x2e, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
+ 0x18, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2e, 0x53,
+ 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73,
+ 0x42, 0x08, 0x5a, 0x06, 0x2e, 0x2f, 0x6d, 0x61, 0x69, 0x6e,
+}
+
+var (
+ file_source_tree_proto_rawDescOnce sync.Once
+ file_source_tree_proto_rawDescData = file_source_tree_proto_rawDesc
+)
+
+func file_source_tree_proto_rawDescGZIP() []byte {
+ file_source_tree_proto_rawDescOnce.Do(func() {
+ file_source_tree_proto_rawDescData = protoimpl.X.CompressGZIP(file_source_tree_proto_rawDescData)
+ })
+ return file_source_tree_proto_rawDescData
+}
+
+var file_source_tree_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_source_tree_proto_goTypes = []interface{}{
+ (*SourceFile)(nil), // 0: source_files.SourceFile
+ (*SourceTree)(nil), // 1: source_files.SourceTree
+}
+var file_source_tree_proto_depIdxs = []int32{
+ 0, // 0: source_files.SourceTree.files:type_name -> source_files.SourceFile
+ 1, // [1:1] is the sub-list for method output_type
+ 1, // [1:1] is the sub-list for method input_type
+ 1, // [1:1] is the sub-list for extension type_name
+ 1, // [1:1] is the sub-list for extension extendee
+ 0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_source_tree_proto_init() }
+func file_source_tree_proto_init() {
+ if File_source_tree_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_source_tree_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*SourceFile); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_source_tree_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*SourceTree); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_source_tree_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 2,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_source_tree_proto_goTypes,
+ DependencyIndexes: file_source_tree_proto_depIdxs,
+ MessageInfos: file_source_tree_proto_msgTypes,
+ }.Build()
+ File_source_tree_proto = out.File
+ file_source_tree_proto_rawDesc = nil
+ file_source_tree_proto_goTypes = nil
+ file_source_tree_proto_depIdxs = nil
+}
diff --git a/cmd/source_tree_size/source_tree.proto b/cmd/source_tree_size/source_tree.proto
new file mode 100644
index 000000000..1a5b89fa0
--- /dev/null
+++ b/cmd/source_tree_size/source_tree.proto
@@ -0,0 +1,27 @@
+// Copyright 2024 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.
+
+syntax = "proto2";
+
+package source_files;
+option go_package = "./main";
+
+message SourceFile {
+ optional string path = 1;
+ optional int32 size_bytes = 2;
+}
+
+message SourceTree {
+ repeated SourceFile files = 1;
+}
diff --git a/cmd/source_tree_size/source_tree_size.go b/cmd/source_tree_size/source_tree_size.go
new file mode 100644
index 000000000..8b2a4cf17
--- /dev/null
+++ b/cmd/source_tree_size/source_tree_size.go
@@ -0,0 +1,126 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path"
+ "path/filepath"
+ "sort"
+ "strings"
+ "sync"
+
+ "google.golang.org/protobuf/proto"
+)
+
+var root string
+var repo string
+var outDir string
+var channel chan SourceFile
+var waiter sync.WaitGroup
+var sourceTree SourceTree
+
+func normalizeOutDir(outDir, root string) string {
+ if len(outDir) == 0 {
+ return ""
+ }
+ if filepath.IsAbs(outDir) {
+ if strings.HasPrefix(outDir, root) {
+ // Absolute path inside root, use it
+ return outDir
+ } else {
+ // Not inside root, we don't care about it
+ return ""
+ }
+ } else {
+ // Relative path inside root, make it absolute
+ return root + outDir
+ }
+}
+
+func walkDir(dir string) {
+ defer waiter.Done()
+
+ visit := func(path string, info os.FileInfo, err error) error {
+ name := info.Name()
+
+ // Repo git projects are symlinks. A real directory called .git counts as checked in
+ // (and is very likely to be wasted space)
+ if info.Mode().Type()&os.ModeSymlink != 0 && name == ".git" {
+ return nil
+ }
+
+ // Skip .repo and out
+ if info.IsDir() && (path == repo || path == outDir) {
+ return filepath.SkipDir
+ }
+
+ if info.IsDir() && path != dir {
+ waiter.Add(1)
+ go walkDir(path)
+ return filepath.SkipDir
+ }
+
+ if !info.IsDir() {
+ sourcePath := strings.TrimPrefix(path, root)
+ file := SourceFile{
+ Path: proto.String(sourcePath),
+ SizeBytes: proto.Int32(42),
+ }
+ channel <- file
+ }
+ return nil
+
+ }
+ filepath.Walk(dir, visit)
+}
+
+func main() {
+ var outputFile string
+ flag.StringVar(&outputFile, "o", "", "The file to write")
+ flag.StringVar(&outDir, "out_dir", "out", "The out directory")
+ flag.Parse()
+
+ if outputFile == "" {
+ fmt.Fprintf(os.Stderr, "source_tree_size: Missing argument: -o\n")
+ os.Exit(1)
+ }
+
+ root, _ = os.Getwd()
+ if root[len(root)-1] != '/' {
+ root += "/"
+ }
+
+ outDir = normalizeOutDir(outDir, root)
+ repo = path.Join(root, ".repo")
+
+ // The parallel scanning reduces the run time by about a minute
+ channel = make(chan SourceFile)
+ waiter.Add(1)
+ go walkDir(root)
+ go func() {
+ waiter.Wait()
+ close(channel)
+ }()
+ for sourceFile := range channel {
+ sourceTree.Files = append(sourceTree.Files, &sourceFile)
+ }
+
+ // Sort the results, for a stable output
+ sort.Slice(sourceTree.Files, func(i, j int) bool {
+ return *sourceTree.Files[i].Path < *sourceTree.Files[j].Path
+ })
+
+ // Flatten and write
+ buf, err := proto.Marshal(&sourceTree)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Couldn't marshal protobuf\n")
+ os.Exit(1)
+ }
+ err = ioutil.WriteFile(outputFile, buf, 0644)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error writing file: %v\n", err)
+ os.Exit(1)
+ }
+}