diff --git a/tools/ide_query/go.mod b/tools/ide_query/go.mod new file mode 100644 index 0000000000..f9d727f3ab --- /dev/null +++ b/tools/ide_query/go.mod @@ -0,0 +1,7 @@ +module ide_query + +go 1.21 + +require ( + google.golang.org/protobuf v0.0.0 +) diff --git a/tools/ide_query/go.work b/tools/ide_query/go.work new file mode 100644 index 0000000000..851f352af6 --- /dev/null +++ b/tools/ide_query/go.work @@ -0,0 +1,9 @@ +go 1.21 + +use ( + . +) + +replace ( + google.golang.org/protobuf v0.0.0 => ../../../../external/golang-protobuf +) \ No newline at end of file diff --git a/tools/ide_query/go.work.sum b/tools/ide_query/go.work.sum new file mode 100644 index 0000000000..cf42b48eb0 --- /dev/null +++ b/tools/ide_query/go.work.sum @@ -0,0 +1,5 @@ +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1 h1:7QnIQpGRHE5RnLKnESfDoxm2dTapTZua5a0kS0A+VXQ= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= diff --git a/tools/ide_query/ide_query.go b/tools/ide_query/ide_query.go new file mode 100644 index 0000000000..c1c4da0ed5 --- /dev/null +++ b/tools/ide_query/ide_query.go @@ -0,0 +1,265 @@ +// Binary ide_query generates and analyzes build artifacts. +// The produced result can be consumed by IDEs to provide language features. +package main + +import ( + "container/list" + "context" + "encoding/json" + "flag" + "fmt" + "log" + "os" + "os/exec" + "path" + "slices" + "strings" + + "google.golang.org/protobuf/proto" + pb "ide_query/ide_query_proto" +) + +// Env contains information about the current environment. +type Env struct { + LunchTarget LunchTarget + RepoDir string + OutDir string +} + +// LunchTarget is a parsed Android lunch target. +// Input format: -- +type LunchTarget struct { + Product string + Release string + Variant string +} + +var _ flag.Value = (*LunchTarget)(nil) + +// // Get implements flag.Value. +// func (l *LunchTarget) Get() any { +// return l +// } + +// Set implements flag.Value. +func (l *LunchTarget) Set(s string) error { + parts := strings.Split(s, "-") + if len(parts) != 3 { + return fmt.Errorf("invalid lunch target: %q, must have form --", s) + } + *l = LunchTarget{ + Product: parts[0], + Release: parts[1], + Variant: parts[2], + } + return nil +} + +// String implements flag.Value. +func (l *LunchTarget) String() string { + return fmt.Sprintf("%s-%s-%s", l.Product, l.Release, l.Variant) +} + +func main() { + var env Env + env.OutDir = os.Getenv("OUT_DIR") + env.RepoDir = os.Getenv("ANDROID_BUILD_TOP") + flag.Var(&env.LunchTarget, "lunch_target", "The lunch target to query") + flag.Parse() + files := flag.Args() + if len(files) == 0 { + fmt.Println("No files provided.") + os.Exit(1) + return + } + + var javaFiles []string + for _, f := range files { + switch { + case strings.HasSuffix(f, ".java") || strings.HasSuffix(f, ".kt"): + javaFiles = append(javaFiles, f) + default: + log.Printf("File %q is supported - will be skipped.", f) + } + } + + ctx := context.Background() + javaDepsPath := path.Join(env.RepoDir, env.OutDir, "soong/module_bp_java_deps.json") + // TODO(michaelmerg): Figure out if module_bp_java_deps.json is outdated. + runMake(ctx, env, "nothing") + + javaModules, err := loadJavaModules(javaDepsPath) + if err != nil { + log.Fatalf("Failed to load java modules: %v", err) + } + + fileToModule := make(map[string]*javaModule) // file path -> module + for _, f := range javaFiles { + for _, m := range javaModules { + if !slices.Contains(m.Srcs, f) { + continue + } + if fileToModule[f] != nil { + // TODO(michaelmerg): Handle the case where a file is covered by multiple modules. + log.Printf("File %q found in module %q but is already covered by module %q", f, m.Name, fileToModule[f].Name) + continue + } + fileToModule[f] = m + } + } + + var toMake []string + for _, m := range fileToModule { + toMake = append(toMake, m.Name) + } + fmt.Printf("Running make for modules: %v\n", strings.Join(toMake, ", ")) + if err := runMake(ctx, env, toMake...); err != nil { + log.Fatalf("Failed to run make: %v", err) + } + + var sources []*pb.SourceFile + type depsAndGenerated struct { + Deps []string + Generated []*pb.GeneratedFile + } + moduleToDeps := make(map[string]*depsAndGenerated) + for _, f := range files { + file := &pb.SourceFile{ + Path: f, + WorkingDir: env.RepoDir, + } + sources = append(sources, file) + + m := fileToModule[f] + if m == nil { + file.Status = &pb.Status{ + Code: pb.Status_FAILURE, + Message: proto.String("File not found in any module."), + } + continue + } + + file.Status = &pb.Status{Code: pb.Status_OK} + if moduleToDeps[m.Name] != nil { + file.Generated = moduleToDeps[m.Name].Generated + file.Deps = moduleToDeps[m.Name].Deps + continue + } + + deps := transitiveDeps(m, javaModules) + var generated []*pb.GeneratedFile + outPrefix := env.OutDir + "/" + for _, d := range deps { + if relPath, ok := strings.CutPrefix(d, outPrefix); ok { + contents, err := os.ReadFile(d) + if err != nil { + fmt.Printf("Generated file %q not found - will be skipped.\n", d) + continue + } + + generated = append(generated, &pb.GeneratedFile{ + Path: relPath, + Contents: contents, + }) + } + } + moduleToDeps[m.Name] = &depsAndGenerated{deps, generated} + file.Generated = generated + file.Deps = deps + } + + res := &pb.IdeAnalysis{ + BuildArtifactRoot: env.OutDir, + Sources: sources, + Status: &pb.Status{Code: pb.Status_OK}, + } + data, err := proto.Marshal(res) + if err != nil { + log.Fatalf("Failed to marshal result proto: %v", err) + } + + err = os.WriteFile(path.Join(env.OutDir, "ide_query.pb"), data, 0644) + if err != nil { + log.Fatalf("Failed to write result proto: %v", err) + } + + for _, s := range sources { + fmt.Printf("%s: %v (Deps: %d, Generated: %d)\n", s.GetPath(), s.GetStatus(), len(s.GetDeps()), len(s.GetGenerated())) + } +} + +// runMake runs Soong build for the given modules. +func runMake(ctx context.Context, env Env, modules ...string) error { + args := []string{ + "--make-mode", + "ANDROID_BUILD_ENVIRONMENT_CONFIG=googler-cog", + "TARGET_PRODUCT=" + env.LunchTarget.Product, + "TARGET_RELEASE=" + env.LunchTarget.Release, + "TARGET_BUILD_VARIANT=" + env.LunchTarget.Variant, + } + args = append(args, modules...) + cmd := exec.CommandContext(ctx, "build/soong/soong_ui.bash", args...) + cmd.Dir = env.RepoDir + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() +} + +type javaModule struct { + Name string + Path []string `json:"path,omitempty"` + Deps []string `json:"dependencies,omitempty"` + Srcs []string `json:"srcs,omitempty"` + Jars []string `json:"jars,omitempty"` + SrcJars []string `json:"srcjars,omitempty"` +} + +func loadJavaModules(path string) (map[string]*javaModule, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + + var ret map[string]*javaModule // module name -> module + if err = json.Unmarshal(data, &ret); err != nil { + return nil, err + } + + for name, module := range ret { + if strings.HasSuffix(name, "-jarjar") || strings.HasSuffix(name, ".impl") { + delete(ret, name) + continue + } + + module.Name = name + } + return ret, nil +} + +func transitiveDeps(m *javaModule, modules map[string]*javaModule) []string { + var ret []string + q := list.New() + q.PushBack(m.Name) + seen := make(map[string]bool) // module names -> true + for q.Len() > 0 { + name := q.Remove(q.Front()).(string) + mod := modules[name] + if mod == nil { + continue + } + + ret = append(ret, mod.Srcs...) + ret = append(ret, mod.SrcJars...) + ret = append(ret, mod.Jars...) + for _, d := range mod.Deps { + if seen[d] { + continue + } + seen[d] = true + q.PushBack(d) + } + } + slices.Sort(ret) + ret = slices.Compact(ret) + return ret +} diff --git a/tools/ide_query/ide_query.sh b/tools/ide_query/ide_query.sh new file mode 100755 index 0000000000..663c4dc14a --- /dev/null +++ b/tools/ide_query/ide_query.sh @@ -0,0 +1,12 @@ +#!/bin/bash -e + +cd $(dirname $BASH_SOURCE) +source $(pwd)/../../shell_utils.sh +require_top + +# Ensure cogsetup (out/ will be symlink outside the repo) +. ${TOP}/build/make/cogsetup.sh + +export ANDROID_BUILD_TOP=$TOP +export OUT_DIR=${OUT_DIR} +exec "${TOP}/prebuilts/go/linux-x86/bin/go" "run" "ide_query" "$@" diff --git a/tools/ide_query/ide_query_proto/ide_query.pb.go b/tools/ide_query/ide_query_proto/ide_query.pb.go new file mode 100644 index 0000000000..30571ccd14 --- /dev/null +++ b/tools/ide_query/ide_query_proto/ide_query.pb.go @@ -0,0 +1,522 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.30.0 +// protoc v3.21.12 +// source: ide_query.proto + +package ide_query_proto + +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 Status_Code int32 + +const ( + Status_OK Status_Code = 0 + Status_FAILURE Status_Code = 1 +) + +// Enum value maps for Status_Code. +var ( + Status_Code_name = map[int32]string{ + 0: "OK", + 1: "FAILURE", + } + Status_Code_value = map[string]int32{ + "OK": 0, + "FAILURE": 1, + } +) + +func (x Status_Code) Enum() *Status_Code { + p := new(Status_Code) + *p = x + return p +} + +func (x Status_Code) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Status_Code) Descriptor() protoreflect.EnumDescriptor { + return file_ide_query_proto_enumTypes[0].Descriptor() +} + +func (Status_Code) Type() protoreflect.EnumType { + return &file_ide_query_proto_enumTypes[0] +} + +func (x Status_Code) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Status_Code.Descriptor instead. +func (Status_Code) EnumDescriptor() ([]byte, []int) { + return file_ide_query_proto_rawDescGZIP(), []int{0, 0} +} + +// Indicates the success/failure for analysis. +type Status struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Code Status_Code `protobuf:"varint,1,opt,name=code,proto3,enum=cider.build.companion.Status_Code" json:"code,omitempty"` + // Details about the status, might be displayed to user. + Message *string `protobuf:"bytes,2,opt,name=message,proto3,oneof" json:"message,omitempty"` +} + +func (x *Status) Reset() { + *x = Status{} + if protoimpl.UnsafeEnabled { + mi := &file_ide_query_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Status) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Status) ProtoMessage() {} + +func (x *Status) ProtoReflect() protoreflect.Message { + mi := &file_ide_query_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 Status.ProtoReflect.Descriptor instead. +func (*Status) Descriptor() ([]byte, []int) { + return file_ide_query_proto_rawDescGZIP(), []int{0} +} + +func (x *Status) GetCode() Status_Code { + if x != nil { + return x.Code + } + return Status_OK +} + +func (x *Status) GetMessage() string { + if x != nil && x.Message != nil { + return *x.Message + } + return "" +} + +type GeneratedFile struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Path to the file relative to IdeAnalysis.build_artifact_root. + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + // The text of the generated file, if not provided contents will be read + // + // from the path above in user's workstation. + Contents []byte `protobuf:"bytes,2,opt,name=contents,proto3,oneof" json:"contents,omitempty"` +} + +func (x *GeneratedFile) Reset() { + *x = GeneratedFile{} + if protoimpl.UnsafeEnabled { + mi := &file_ide_query_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GeneratedFile) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GeneratedFile) ProtoMessage() {} + +func (x *GeneratedFile) ProtoReflect() protoreflect.Message { + mi := &file_ide_query_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 GeneratedFile.ProtoReflect.Descriptor instead. +func (*GeneratedFile) Descriptor() ([]byte, []int) { + return file_ide_query_proto_rawDescGZIP(), []int{1} +} + +func (x *GeneratedFile) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *GeneratedFile) GetContents() []byte { + if x != nil { + return x.Contents + } + return nil +} + +type SourceFile struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Repo root relative path to the source file in the tree. + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + // Working directory used by the build system. All the relative + // paths in compiler_arguments should be relative to this path. + // Relative to workspace root. + WorkingDir string `protobuf:"bytes,2,opt,name=working_dir,json=workingDir,proto3" json:"working_dir,omitempty"` + // Compiler arguments to compile the source file. If multiple variants + // of the module being compiled are possible, the query script will choose + // one. + CompilerArguments []string `protobuf:"bytes,3,rep,name=compiler_arguments,json=compilerArguments,proto3" json:"compiler_arguments,omitempty"` + // Any generated files that are used in compiling the file. + Generated []*GeneratedFile `protobuf:"bytes,4,rep,name=generated,proto3" json:"generated,omitempty"` + // Paths to all of the sources, like build files, code generators, + // proto files etc. that were used during analysis. Used to figure + // out when a set of build artifacts are stale and the query tool + // must be re-run. + // Relative to workspace root. + Deps []string `protobuf:"bytes,5,rep,name=deps,proto3" json:"deps,omitempty"` + // Represensts analysis status for this particular file. e.g. not part + // of the build graph. + Status *Status `protobuf:"bytes,6,opt,name=status,proto3,oneof" json:"status,omitempty"` +} + +func (x *SourceFile) Reset() { + *x = SourceFile{} + if protoimpl.UnsafeEnabled { + mi := &file_ide_query_proto_msgTypes[2] + 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_ide_query_proto_msgTypes[2] + 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_ide_query_proto_rawDescGZIP(), []int{2} +} + +func (x *SourceFile) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *SourceFile) GetWorkingDir() string { + if x != nil { + return x.WorkingDir + } + return "" +} + +func (x *SourceFile) GetCompilerArguments() []string { + if x != nil { + return x.CompilerArguments + } + return nil +} + +func (x *SourceFile) GetGenerated() []*GeneratedFile { + if x != nil { + return x.Generated + } + return nil +} + +func (x *SourceFile) GetDeps() []string { + if x != nil { + return x.Deps + } + return nil +} + +func (x *SourceFile) GetStatus() *Status { + if x != nil { + return x.Status + } + return nil +} + +type IdeAnalysis struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Path relative to workspace root, containing all the artifacts + // generated by the build system. GeneratedFile.path are always + // relative to this directory. + BuildArtifactRoot string `protobuf:"bytes,1,opt,name=build_artifact_root,json=buildArtifactRoot,proto3" json:"build_artifact_root,omitempty"` + Sources []*SourceFile `protobuf:"bytes,2,rep,name=sources,proto3" json:"sources,omitempty"` + // Status representing overall analysis. + // Should fail only when no analysis can be performed, e.g. workspace + // isn't setup. + Status *Status `protobuf:"bytes,3,opt,name=status,proto3,oneof" json:"status,omitempty"` +} + +func (x *IdeAnalysis) Reset() { + *x = IdeAnalysis{} + if protoimpl.UnsafeEnabled { + mi := &file_ide_query_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IdeAnalysis) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IdeAnalysis) ProtoMessage() {} + +func (x *IdeAnalysis) ProtoReflect() protoreflect.Message { + mi := &file_ide_query_proto_msgTypes[3] + 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 IdeAnalysis.ProtoReflect.Descriptor instead. +func (*IdeAnalysis) Descriptor() ([]byte, []int) { + return file_ide_query_proto_rawDescGZIP(), []int{3} +} + +func (x *IdeAnalysis) GetBuildArtifactRoot() string { + if x != nil { + return x.BuildArtifactRoot + } + return "" +} + +func (x *IdeAnalysis) GetSources() []*SourceFile { + if x != nil { + return x.Sources + } + return nil +} + +func (x *IdeAnalysis) GetStatus() *Status { + if x != nil { + return x.Status + } + return nil +} + +var File_ide_query_proto protoreflect.FileDescriptor + +var file_ide_query_proto_rawDesc = []byte{ + 0x0a, 0x0f, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x15, 0x63, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x69, 0x6f, 0x6e, 0x22, 0x88, 0x01, 0x0a, 0x06, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x36, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x22, 0x2e, 0x63, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x07, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x22, 0x1b, 0x0a, 0x04, 0x43, 0x6f, + 0x64, 0x65, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x41, + 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x22, 0x51, 0x0a, 0x0d, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, + 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, 0x1f, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x08, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x88, 0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x8f, 0x02, 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, 0x1f, 0x0a, 0x0b, 0x77, 0x6f, 0x72, + 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x72, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x6f, + 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, + 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x42, 0x0a, 0x09, 0x67, 0x65, 0x6e, + 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, + 0x69, 0x64, 0x65, 0x72, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, + 0x6c, 0x65, 0x52, 0x09, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x12, 0x12, 0x0a, + 0x04, 0x64, 0x65, 0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x70, + 0x73, 0x12, 0x3a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, + 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xc1, 0x01, 0x0a, 0x0b, 0x49, 0x64, 0x65, + 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x62, 0x75, 0x69, 0x6c, + 0x64, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x41, 0x72, 0x74, 0x69, + 0x66, 0x61, 0x63, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x3b, 0x0a, 0x07, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x63, 0x69, 0x64, 0x65, + 0x72, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x69, 0x6f, + 0x6e, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x07, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x62, 0x75, + 0x69, 0x6c, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, + 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x1b, 0x5a, 0x19, + 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2f, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, + 0x65, 0x72, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, +} + +var ( + file_ide_query_proto_rawDescOnce sync.Once + file_ide_query_proto_rawDescData = file_ide_query_proto_rawDesc +) + +func file_ide_query_proto_rawDescGZIP() []byte { + file_ide_query_proto_rawDescOnce.Do(func() { + file_ide_query_proto_rawDescData = protoimpl.X.CompressGZIP(file_ide_query_proto_rawDescData) + }) + return file_ide_query_proto_rawDescData +} + +var file_ide_query_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_ide_query_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_ide_query_proto_goTypes = []interface{}{ + (Status_Code)(0), // 0: cider.build.companion.Status.Code + (*Status)(nil), // 1: cider.build.companion.Status + (*GeneratedFile)(nil), // 2: cider.build.companion.GeneratedFile + (*SourceFile)(nil), // 3: cider.build.companion.SourceFile + (*IdeAnalysis)(nil), // 4: cider.build.companion.IdeAnalysis +} +var file_ide_query_proto_depIdxs = []int32{ + 0, // 0: cider.build.companion.Status.code:type_name -> cider.build.companion.Status.Code + 2, // 1: cider.build.companion.SourceFile.generated:type_name -> cider.build.companion.GeneratedFile + 1, // 2: cider.build.companion.SourceFile.status:type_name -> cider.build.companion.Status + 3, // 3: cider.build.companion.IdeAnalysis.sources:type_name -> cider.build.companion.SourceFile + 1, // 4: cider.build.companion.IdeAnalysis.status:type_name -> cider.build.companion.Status + 5, // [5:5] is the sub-list for method output_type + 5, // [5:5] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name +} + +func init() { file_ide_query_proto_init() } +func file_ide_query_proto_init() { + if File_ide_query_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_ide_query_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Status); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ide_query_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GeneratedFile); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ide_query_proto_msgTypes[2].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_ide_query_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IdeAnalysis); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_ide_query_proto_msgTypes[0].OneofWrappers = []interface{}{} + file_ide_query_proto_msgTypes[1].OneofWrappers = []interface{}{} + file_ide_query_proto_msgTypes[2].OneofWrappers = []interface{}{} + file_ide_query_proto_msgTypes[3].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_ide_query_proto_rawDesc, + NumEnums: 1, + NumMessages: 4, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_ide_query_proto_goTypes, + DependencyIndexes: file_ide_query_proto_depIdxs, + EnumInfos: file_ide_query_proto_enumTypes, + MessageInfos: file_ide_query_proto_msgTypes, + }.Build() + File_ide_query_proto = out.File + file_ide_query_proto_rawDesc = nil + file_ide_query_proto_goTypes = nil + file_ide_query_proto_depIdxs = nil +} diff --git a/tools/ide_query/ide_query_proto/ide_query.proto b/tools/ide_query/ide_query_proto/ide_query.proto new file mode 100644 index 0000000000..63eea39f4d --- /dev/null +++ b/tools/ide_query/ide_query_proto/ide_query.proto @@ -0,0 +1,66 @@ +syntax = "proto3"; + +package ide_query; +option go_package = "ide_query/ide_query_proto"; + +// Indicates the success/failure for analysis. +message Status { + enum Code { + OK = 0; + FAILURE = 1; + } + Code code = 1; + // Details about the status, might be displayed to user. + optional string message = 2; +} + +message GeneratedFile { + // Path to the file relative to IdeAnalysis.build_artifact_root. + string path = 1; + + // The text of the generated file, if not provided contents will be read + // from the path above in user's workstation. + optional bytes contents = 2; +} + +message SourceFile { + // Path to the source file relative to repository root. + string path = 1; + + // Working directory used by the build system. All the relative + // paths in compiler_arguments should be relative to this path. + // Relative to repository root. + string working_dir = 2; + + // Compiler arguments to compile the source file. If multiple variants + // of the module being compiled are possible, the query script will choose + // one. + repeated string compiler_arguments = 3; + + // Any generated files that are used in compiling the file. + repeated GeneratedFile generated = 4; + + // Paths to all of the sources, like build files, code generators, + // proto files etc. that were used during analysis. Used to figure + // out when a set of build artifacts are stale and the query tool + // must be re-run. + // Relative to repository root. + repeated string deps = 5; + + // Represents analysis status for this particular file. e.g. not part + // of the build graph. + optional Status status = 6; +} + +message IdeAnalysis { + // Path relative to repository root, containing all the artifacts + // generated by the build system. GeneratedFile.path are always + // relative to this directory. + string build_artifact_root = 1; + + repeated SourceFile sources = 2; + + // Status representing overall analysis. + // Should fail only when no analysis can be performed. + optional Status status = 3; +} diff --git a/tools/ide_query/ide_query_proto/regen.sh b/tools/ide_query/ide_query_proto/regen.sh new file mode 100755 index 0000000000..eec4f3731b --- /dev/null +++ b/tools/ide_query/ide_query_proto/regen.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +aprotoc --go_out=paths=source_relative:. ide_query.proto