main.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. package main
  2. import (
  3. "bufio"
  4. "bytes"
  5. "fmt"
  6. "io"
  7. "io/ioutil"
  8. "os"
  9. "strings"
  10. "github.com/gogo/protobuf/proto"
  11. "gopkg.in/src-d/hercules.v3"
  12. "gopkg.in/src-d/hercules.v3/pb"
  13. "sort"
  14. )
  15. func main() {
  16. files := os.Args[1:]
  17. if len(files) == 0 {
  18. fmt.Fprintln(os.Stderr, "Usage: hercules-combine file [file...]")
  19. os.Exit(1)
  20. }
  21. if len(files) == 1 {
  22. file, err := os.Open(files[0])
  23. if err != nil {
  24. panic(err)
  25. }
  26. defer file.Close()
  27. io.Copy(os.Stdout, bufio.NewReader(file))
  28. return
  29. }
  30. repos := []string{}
  31. allErrors := map[string][]string{}
  32. mergedResults := map[string]interface{}{}
  33. mergedMetadata := &hercules.CommonAnalysisResult{}
  34. for _, fileName := range files {
  35. anotherResults, anotherMetadata, errs := loadMessage(fileName, &repos)
  36. if anotherMetadata != nil {
  37. mergeResults(mergedResults, mergedMetadata, anotherResults, anotherMetadata)
  38. }
  39. allErrors[fileName] = errs
  40. }
  41. printErrors(allErrors)
  42. sort.Strings(repos)
  43. if mergedMetadata == nil {
  44. return
  45. }
  46. mergedMessage := pb.AnalysisResults{
  47. Header: &pb.Metadata{
  48. Version: 2,
  49. Hash: hercules.GIT_HASH,
  50. Repository: strings.Join(repos, " & "),
  51. },
  52. Contents: map[string][]byte{},
  53. }
  54. mergedMetadata.FillMetadata(mergedMessage.Header)
  55. for key, val := range mergedResults {
  56. buffer := bytes.Buffer{}
  57. hercules.Registry.Summon(key)[0].(hercules.LeafPipelineItem).Serialize(
  58. val, true, &buffer)
  59. mergedMessage.Contents[key] = buffer.Bytes()
  60. }
  61. serialized, err := proto.Marshal(&mergedMessage)
  62. if err != nil {
  63. panic(err)
  64. }
  65. os.Stdout.Write(serialized)
  66. }
  67. func loadMessage(fileName string, repos *[]string) (
  68. map[string]interface{}, *hercules.CommonAnalysisResult, []string) {
  69. errs := []string{}
  70. buffer, err := ioutil.ReadFile(fileName)
  71. if err != nil {
  72. errs = append(errs, "Cannot read " + fileName + ": " + err.Error())
  73. return nil, nil, errs
  74. }
  75. message := pb.AnalysisResults{}
  76. err = proto.Unmarshal(buffer, &message)
  77. if err != nil {
  78. errs = append(errs, "Cannot parse " + fileName + ": " + err.Error())
  79. return nil, nil, errs
  80. }
  81. *repos = append(*repos, message.Header.Repository)
  82. results := map[string]interface{}{}
  83. for key, val := range message.Contents {
  84. summoned := hercules.Registry.Summon(key)
  85. if len(summoned) == 0 {
  86. errs = append(errs, fileName + ": item not found: " + key)
  87. continue
  88. }
  89. mpi, ok := summoned[0].(hercules.MergeablePipelineItem)
  90. if !ok {
  91. errs = append(errs, fileName + ": " + key + ": MergeablePipelineItem is not implemented")
  92. continue
  93. }
  94. msg, err := mpi.Deserialize(val)
  95. if err != nil {
  96. errs = append(errs, fileName + ": deserialization failed: " + key + ": " + err.Error())
  97. continue
  98. }
  99. results[key] = msg
  100. }
  101. return results, hercules.MetadataToCommonAnalysisResult(message.Header), errs
  102. }
  103. func printErrors(allErrors map[string][]string) {
  104. needToPrintErrors := false
  105. for _, errs := range allErrors {
  106. if len(errs) > 0 {
  107. needToPrintErrors = true
  108. break
  109. }
  110. }
  111. if !needToPrintErrors {
  112. return
  113. }
  114. fmt.Fprintln(os.Stderr, "Errors:")
  115. for key, errs := range allErrors {
  116. if len(errs) > 0 {
  117. fmt.Fprintln(os.Stderr, " " + key)
  118. for _, err := range errs {
  119. fmt.Fprintln(os.Stderr, " " + err)
  120. }
  121. }
  122. }
  123. }
  124. func mergeResults(mergedResults map[string]interface{},
  125. mergedCommons *hercules.CommonAnalysisResult,
  126. anotherResults map[string]interface{},
  127. anotherCommons *hercules.CommonAnalysisResult) {
  128. for key, val := range anotherResults {
  129. mergedResult, exists := mergedResults[key]
  130. if !exists {
  131. mergedResults[key] = val
  132. continue
  133. }
  134. item := hercules.Registry.Summon(key)[0].(hercules.MergeablePipelineItem)
  135. mergedResult, *mergedCommons = item.MergeResults(
  136. mergedResult, val, mergedCommons, anotherCommons)
  137. mergedResults[key] = mergedResult
  138. }
  139. if mergedCommons.CommitsNumber == 0 {
  140. *mergedCommons = *anotherCommons
  141. }
  142. }