diff options
author | 2024-12-06 12:56:08 -0800 | |
---|---|---|
committer | 2024-12-09 10:01:21 -0800 | |
commit | c77bdde587cc484b5a1f6176ebdc66da84926075 (patch) | |
tree | 6ca5b9eb957c4106bc6836173e8a1d3c5b5b95b5 /cmd | |
parent | e0d520fd7584d0c0c5939928f91544560adc937c (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.bp | 28 | ||||
-rwxr-xr-x | cmd/source_tree_size/regen.sh | 4 | ||||
-rw-r--r-- | cmd/source_tree_size/source_tree.pb.go | 230 | ||||
-rw-r--r-- | cmd/source_tree_size/source_tree.proto | 27 | ||||
-rw-r--r-- | cmd/source_tree_size/source_tree_size.go | 126 |
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) + } +} |