[BACK]Return to mklines.go CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / pkgsrc / pkgtools / pkglint / files

Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.

Diff for /pkgsrc/pkgtools/pkglint/files/Attic/mklines.go between version 1.58 and 1.59

version 1.58, 2019/11/02 16:37:48 version 1.59, 2019/11/17 01:26:25
Line 1 
Line 1 
 package pkglint  package pkglint
   
 import (  import "strings"
         "strings"  
 )  
   
 // MkLines contains data for the Makefile (or *.mk) that is currently checked.  // MkLines contains data for the Makefile (or *.mk) that is currently checked.
 type MkLines struct {  type MkLines struct {
Line 70  func NewMkLines(lines *Lines) *MkLines {
Line 68  func NewMkLines(lines *Lines) *MkLines {
 //  ck.AfterLine  //  ck.AfterLine
 //  ck.Finish  //  ck.Finish
   
 // Whole returns a virtual line that can be used for issuing diagnostics  
 // and explanations, but not for text replacements.  
 func (mklines *MkLines) Whole() *Line { return mklines.lines.Whole() }  
   
 // UseVar remembers that the given variable is used in the given line.  
 // This controls the "defined but not used" warning.  
 func (mklines *MkLines) UseVar(mkline *MkLine, varname string, time VucTime) {  
         mklines.vars.Use(varname, mkline, time)  
         if G.Pkg != nil {  
                 G.Pkg.vars.Use(varname, mkline, time)  
         }  
 }  
   
 func (mklines *MkLines) Check() {  func (mklines *MkLines) Check() {
         if trace.Tracing {          if trace.Tracing {
                 defer trace.Call1(mklines.lines.Filename)()                  defer trace.Call1(mklines.lines.Filename)()
Line 105  func (mklines *MkLines) Check() {
Line 90  func (mklines *MkLines) Check() {
         SaveAutofixChanges(mklines.lines)          SaveAutofixChanges(mklines.lines)
 }  }
   
 func (mklines *MkLines) checkAll() {  func (mklines *MkLines) collectRationale() {
         allowedTargets := map[string]bool{  
                 "pre-fetch": true, "do-fetch": true, "post-fetch": true,  
                 "pre-extract": true, "do-extract": true, "post-extract": true,  
                 "pre-patch": true, "do-patch": true, "post-patch": true,  
                 "pre-tools": true, "do-tools": true, "post-tools": true,  
                 "pre-wrapper": true, "do-wrapper": true, "post-wrapper": true,  
                 "pre-configure": true, "do-configure": true, "post-configure": true,  
                 "pre-build": true, "do-build": true, "post-build": true,  
                 "pre-test": true, "do-test": true, "post-test": true,  
                 "pre-install": true, "do-install": true, "post-install": true,  
                 "pre-package": true, "do-package": true, "post-package": true,  
                 "pre-clean": true, "do-clean": true, "post-clean": true}  
   
         mklines.lines.CheckCvsID(0, `#[\t ]+`, "# ")  
   
         substContext := NewSubstContext()  
         var varalign VaralignBlock  
         vargroupsChecker := NewVargroupsChecker(mklines)  
         isHacksMk := mklines.lines.BaseName == "hacks.mk"  
   
         lineAction := func(mkline *MkLine) bool {  
                 if isHacksMk {  
                         // Needs to be set here because it is reset in MkLines.ForEach.  
                         mklines.Tools.SeenPrefs = true  
                 }  
   
                 ck := MkLineChecker{mklines, mkline}  
                 ck.Check()  
                 vargroupsChecker.Check(mkline)  
   
                 varalign.Process(mkline)  
                 mklines.Tools.ParseToolLine(mklines, mkline, false, false)  
                 substContext.Process(mkline)  
   
                 switch {  
   
                 case mkline.IsVarassign():  
                         mklines.target = ""  
                         mkline.Tokenize(mkline.Value(), true) // Just for the side-effect of the warnings.  
   
                         mklines.checkVarassignPlist(mkline)  
   
                 case mkline.IsInclude():  
                         mklines.target = ""  
                         if G.Pkg != nil {  
                                 G.Pkg.checkIncludeConditionally(mkline, mklines.indentation)  
                         }  
   
                 case mkline.IsDirective():  
                         ck.checkDirective(mklines.forVars, mklines.indentation)  
   
                 case mkline.IsDependency():  
                         ck.checkDependencyRule(allowedTargets)  
                         mklines.target = mkline.Targets()  
   
                 case mkline.IsShellCommand():  
                         mkline.Tokenize(mkline.ShellCommand(), true) // Just for the side-effect of the warnings.  
                 }  
   
                 return true          isUseful := func(mkline *MkLine) bool {
                   comment := trimHspace(mkline.Comment())
                   return comment != "" && !hasPrefix(comment, "$NetBSD")
         }          }
   
         atEnd := func(mkline *MkLine) {          isRealComment := func(mkline *MkLine) bool {
                 mklines.indentation.CheckFinish(mklines.lines.Filename)                  return mkline.IsComment() && !mkline.IsCommentedVarassign()
                 vargroupsChecker.Finish(mkline)  
         }          }
   
         if trace.Tracing {          rationale := false
                 trace.Stepf("Starting main checking loop")          for _, mkline := range mklines.mklines {
                   rationale = rationale || isRealComment(mkline) && isUseful(mkline)
                   mkline.splitResult.hasRationale = rationale || isUseful(mkline)
                   rationale = rationale && !mkline.IsEmpty()
         }          }
         mklines.ForEachEnd(lineAction, atEnd)  }
   
         substContext.Finish(mklines.EOFLine())  func (mklines *MkLines) collectUsedVariables() {
         varalign.Finish()          for _, mkline := range mklines.mklines {
                   mkline.ForEachUsed(func(varUse *MkVarUse, time VucTime) {
                           mklines.UseVar(mkline, varUse.varname, time)
                   })
           }
   
         CheckLinesTrailingEmptyLines(mklines.lines)          mklines.collectDocumentedVariables()
 }  }
   
 func (mklines *MkLines) checkVarassignPlist(mkline *MkLine) {  // UseVar remembers that the given variable is used in the given line.
         switch mkline.Varcanon() {  // This controls the "defined but not used" warning.
         case "PLIST_VARS":  func (mklines *MkLines) UseVar(mkline *MkLine, varname string, time VucTime) {
                 for _, id := range mkline.ValueFields(resolveVariableRefs(mklines, mkline.Value())) {          mklines.vars.Use(varname, mkline, time)
                         if !mklines.plistVarSkip && mklines.plistVarSet[id] == nil {          if G.Pkg != nil {
                                 mkline.Warnf("%q is added to PLIST_VARS, but PLIST.%s is not defined in this file.", id, id)                  G.Pkg.vars.Use(varname, mkline, time)
                         }  
                 }  
   
         case "PLIST.*":  
                 id := mkline.Varparam()  
                 if !mklines.plistVarSkip && mklines.plistVarAdded[id] == nil {  
                         mkline.Warnf("PLIST.%s is defined, but %q is not added to PLIST_VARS in this file.", id, id)  
                 }  
         }          }
 }  }
   
 func (mklines *MkLines) SplitToParagraphs() []*Paragraph {  // collectDocumentedVariables collects the variables that are mentioned in the human-readable
         var paras []*Paragraph  // documentation of the Makefile fragments from the pkgsrc infrastructure.
   //
         lines := mklines.mklines  // Loosely based on mk/help/help.awk, revision 1.28, but much simpler.
         isEmpty := func(i int) bool {  func (mklines *MkLines) collectDocumentedVariables() {
                 if lines[i].IsEmpty() {          scope := NewScope()
                         return true          commentLines := 0
                 }          relevant := true
                 return lines[i].IsComment() &&  
                         lines[i].Text == "#" &&  
                         (i == 0 || lines[i-1].IsComment()) &&  
                         (i == len(lines)-1 || lines[i+1].IsComment())  
         }  
   
         i := 0          // TODO: Correctly interpret declarations like "package-settable variables:" and
         for i < len(lines) {          //  "user-settable variables", as well as "default: ...", "allowed: ...",
                 from := i          //  "list of" and other types.
                 for from < len(lines) && isEmpty(from) {  
                         from++  
                 }  
   
                 to := from          finish := func() {
                 for to < len(lines) && !isEmpty(to) {                  // The commentLines include the the line containing the variable name,
                         to++                  // leaving 2 of these 3 lines for the actual documentation.
                   if commentLines >= 3 && relevant {
                           for varname, mkline := range scope.used {
                                   mklines.vars.Define(varname, mkline)
                                   mklines.vars.Use(varname, mkline, VucRunTime)
                           }
                 }                  }
   
                 if from != to {                  scope = NewScope()
                         paras = append(paras, NewParagraph(mklines, from, to))                  commentLines = 0
                 }                  relevant = true
                 i = to  
         }          }
   
         return paras  
 }  
   
 // ForEach calls the action for each line, until the action returns false.  
 // It keeps track of the indentation (see MkLines.indentation)  
 // and all conditional variables (see Indentation.IsConditional).  
 func (mklines *MkLines) ForEach(action func(mkline *MkLine)) {  
         mklines.ForEachEnd(  
                 func(mkline *MkLine) bool { action(mkline); return true },  
                 func(mkline *MkLine) {})  
 }  
   
 // ForEachEnd calls the action for each line, until the action returns false.  
 // It keeps track of the indentation and all conditional variables.  
 // At the end, atEnd is called with the last line as its argument.  
 func (mklines *MkLines) ForEachEnd(action func(mkline *MkLine) bool, atEnd func(lastMkline *MkLine)) bool {  
   
         // XXX: To avoid looping over the lines multiple times, it would  
         // be nice to have an interface LinesChecker that checks a single topic.  
         // Multiple of these line checkers could be run in parallel, so that  
         // the diagnostics appear in the correct order, from top to bottom.  
   
         mklines.indentation = NewIndentation()  
         mklines.Tools.SeenPrefs = false  
   
         result := true  
         for _, mkline := range mklines.mklines {          for _, mkline := range mklines.mklines {
                 mklines.indentation.TrackBefore(mkline)                  text := mkline.Text
                 if !action(mkline) {                  switch {
                         result = false                  case hasPrefix(text, "#"):
                         break                          words := strings.Fields(text)
                 }                          if len(words) <= 1 {
                 mklines.indentation.TrackAfter(mkline)                                  break
         }                          }
   
         if len(mklines.mklines) > 0 {  
                 atEnd(mklines.mklines[len(mklines.mklines)-1])  
         }  
         mklines.indentation = nil  
   
         return result                          commentLines++
 }  
   
 // ExpandLoopVar searches the surrounding .for loops for the given                          parser := NewMkParser(nil, words[1])
 // variable and returns a slice containing all its values, fully                          varname := parser.Varname()
 // expanded.                          if len(varname) < 3 {
 //                                  break
 // It can only be used during an active ForEach call.                          }
 func (mklines *MkLines) ExpandLoopVar(varname string) []string {                          if hasSuffix(varname, ".") {
                                   if !parser.lexer.SkipRegexp(regcomp(`^<\w+>`)) {
                                           break
                                   }
                                   varname += "*"
                           }
                           parser.lexer.SkipByte(':')
   
         // From the inner loop to the outer loop, just in case                          varcanon := varnameCanon(varname)
         // that two loops should ever use the same variable.                          if varcanon == strings.ToUpper(varcanon) && matches(varcanon, `[A-Z]`) && parser.EOF() {
         for i := len(mklines.indentation.levels) - 1; i >= 0; i-- {                                  scope.Define(varcanon, mkline)
                 ind := mklines.indentation.levels[i]                                  scope.Use(varcanon, mkline, VucRunTime)
                           }
   
                 mkline := ind.mkline                          if words[1] == "Copyright" {
                 if mkline.Directive() != "for" {                                  relevant = false
                         continue                          }
                 }  
   
                 // TODO: If needed, add support for multi-variable .for loops.                  case mkline.IsEmpty():
                 resolved := resolveVariableRefs(mklines, mkline.Args())                          finish()
                 words := mkline.ValueFields(resolved)  
                 if len(words) >= 3 && words[0] == varname && words[1] == "in" {  
                         return words[2:]  
                 }                  }
         }          }
   
         return nil          finish()
 }  }
   
 func (mklines *MkLines) collectVariables() {  func (mklines *MkLines) collectVariables() {
Line 369  func (mklines *MkLines) collectVariables
Line 263  func (mklines *MkLines) collectVariables
         })          })
 }  }
   
   // ForEach calls the action for each line, until the action returns false.
   // It keeps track of the indentation (see MkLines.indentation)
   // and all conditional variables (see Indentation.IsConditional).
   func (mklines *MkLines) ForEach(action func(mkline *MkLine)) {
           mklines.ForEachEnd(
                   func(mkline *MkLine) bool { action(mkline); return true },
                   func(mkline *MkLine) {})
   }
   
   // ForEachEnd calls the action for each line, until the action returns false.
   // It keeps track of the indentation and all conditional variables.
   // At the end, atEnd is called with the last line as its argument.
   func (mklines *MkLines) ForEachEnd(action func(mkline *MkLine) bool, atEnd func(lastMkline *MkLine)) bool {
   
           // XXX: To avoid looping over the lines multiple times, it would
           // be nice to have an interface LinesChecker that checks a single topic.
           // Multiple of these line checkers could be run in parallel, so that
           // the diagnostics appear in the correct order, from top to bottom.
   
           mklines.indentation = NewIndentation()
           mklines.Tools.SeenPrefs = false
   
           result := true
           for _, mkline := range mklines.mklines {
                   mklines.indentation.TrackBefore(mkline)
                   if !action(mkline) {
                           result = false
                           break
                   }
                   mklines.indentation.TrackAfter(mkline)
           }
   
           if len(mklines.mklines) > 0 {
                   atEnd(mklines.mklines[len(mklines.mklines)-1])
           }
           mklines.indentation = nil
   
           return result
   }
   
 // defineVar marks a variable as defined in both the current package and the current file.  // defineVar marks a variable as defined in both the current package and the current file.
 func (mklines *MkLines) defineVar(pkg *Package, mkline *MkLine, varname string) {  func (mklines *MkLines) defineVar(pkg *Package, mkline *MkLine, varname string) {
         mklines.vars.Define(varname, mkline)          mklines.vars.Define(varname, mkline)
Line 408  func (mklines *MkLines) collectElse() {
Line 342  func (mklines *MkLines) collectElse() {
         // TODO: Check whether this ForEach is redundant because it is already run somewhere else.          // TODO: Check whether this ForEach is redundant because it is already run somewhere else.
 }  }
   
 func (mklines *MkLines) collectRationale() {  func (mklines *MkLines) checkAll() {
           allowedTargets := map[string]bool{
                   "pre-fetch": true, "do-fetch": true, "post-fetch": true,
                   "pre-extract": true, "do-extract": true, "post-extract": true,
                   "pre-patch": true, "do-patch": true, "post-patch": true,
                   "pre-tools": true, "do-tools": true, "post-tools": true,
                   "pre-wrapper": true, "do-wrapper": true, "post-wrapper": true,
                   "pre-configure": true, "do-configure": true, "post-configure": true,
                   "pre-build": true, "do-build": true, "post-build": true,
                   "pre-test": true, "do-test": true, "post-test": true,
                   "pre-install": true, "do-install": true, "post-install": true,
                   "pre-package": true, "do-package": true, "post-package": true,
                   "pre-clean": true, "do-clean": true, "post-clean": true}
   
         useful := func(mkline *MkLine) bool {          mklines.lines.CheckCvsID(0, `#[\t ]+`, "# ")
                 comment := trimHspace(mkline.Comment())  
                 return comment != "" && !hasPrefix(comment, "$")  
         }  
   
         realComment := func(mkline *MkLine) bool {          substContext := NewSubstContext()
                 return mkline.IsComment() && !mkline.IsCommentedVarassign()          var varalign VaralignBlock
         }          vargroupsChecker := NewVargroupsChecker(mklines)
           isHacksMk := mklines.lines.BaseName == "hacks.mk"
   
         rationale := false          lineAction := func(mkline *MkLine) bool {
         for _, mkline := range mklines.mklines {                  if isHacksMk {
                 rationale = rationale || realComment(mkline) && useful(mkline)                          // Needs to be set here because it is reset in MkLines.ForEach.
                 mkline.splitResult.hasRationale = rationale || useful(mkline)                          mklines.Tools.SeenPrefs = true
                 rationale = rationale && !mkline.IsEmpty()                  }
         }  
 }  
   
 func (mklines *MkLines) collectUsedVariables() {                  ck := MkLineChecker{mklines, mkline}
         for _, mkline := range mklines.mklines {                  ck.Check()
                 mkline.ForEachUsed(func(varUse *MkVarUse, time VucTime) {                  vargroupsChecker.Check(mkline)
                         mklines.UseVar(mkline, varUse.varname, time)  
                 })  
         }  
   
         mklines.collectDocumentedVariables()                  varalign.Process(mkline)
 }                  mklines.Tools.ParseToolLine(mklines, mkline, false, false)
                   substContext.Process(mkline)
   
 // collectDocumentedVariables collects the variables that are mentioned in the human-readable                  switch {
 // documentation of the Makefile fragments from the pkgsrc infrastructure.  
 //  
 // Loosely based on mk/help/help.awk, revision 1.28, but much simpler.  
 func (mklines *MkLines) collectDocumentedVariables() {  
         scope := NewScope()  
         commentLines := 0  
         relevant := true  
   
         // TODO: Correctly interpret declarations like "package-settable variables:" and                  case mkline.IsVarassign():
         //  "user-settable variables", as well as "default: ...", "allowed: ...",                          mklines.target = ""
         //  "list of" and other types.                          mkline.Tokenize(mkline.Value(), true) // Just for the side-effect of the warnings.
   
         finish := func() {                          mklines.checkVarassignPlist(mkline)
                 // The commentLines include the the line containing the variable name,  
                 // leaving 2 of these 3 lines for the actual documentation.                  case mkline.IsInclude():
                 if commentLines >= 3 && relevant {                          mklines.target = ""
                         for varname, mkline := range scope.used {                          if G.Pkg != nil {
                                 mklines.vars.Define(varname, mkline)                                  G.Pkg.checkIncludeConditionally(mkline, mklines.indentation)
                                 mklines.vars.Use(varname, mkline, VucRunTime)  
                         }                          }
   
                   case mkline.IsDirective():
                           ck.checkDirective(mklines.forVars, mklines.indentation)
   
                   case mkline.IsDependency():
                           ck.checkDependencyRule(allowedTargets)
                           mklines.target = mkline.Targets()
   
                   case mkline.IsShellCommand():
                           mkline.Tokenize(mkline.ShellCommand(), true) // Just for the side-effect of the warnings.
                 }                  }
   
                 scope = NewScope()                  return true
                 commentLines = 0  
                 relevant = true  
         }          }
   
         for _, mkline := range mklines.mklines {          atEnd := func(mkline *MkLine) {
                 text := mkline.Text                  mklines.indentation.CheckFinish(mklines.lines.Filename)
                 switch {                  vargroupsChecker.Finish(mkline)
                 case hasPrefix(text, "#"):          }
                         words := strings.Fields(text)  
                         if len(words) <= 1 {  
                                 break  
                         }  
   
                         commentLines++          if trace.Tracing {
                   trace.Stepf("Starting main checking loop")
           }
           mklines.ForEachEnd(lineAction, atEnd)
   
                         parser := NewMkParser(nil, words[1])          substContext.Finish(mklines.EOFLine())
                         varname := parser.Varname()          varalign.Finish()
                         if len(varname) < 3 {  
                                 break  
                         }  
                         if hasSuffix(varname, ".") {  
                                 if !parser.lexer.SkipRegexp(regcomp(`^<\w+>`)) {  
                                         break  
                                 }  
                                 varname += "*"  
                         }  
                         parser.lexer.SkipByte(':')  
   
                         varcanon := varnameCanon(varname)          CheckLinesTrailingEmptyLines(mklines.lines)
                         if varcanon == strings.ToUpper(varcanon) && matches(varcanon, `[A-Z]`) && parser.EOF() {  }
                                 scope.Define(varcanon, mkline)  
                                 scope.Use(varcanon, mkline, VucRunTime)  
                         }  
   
                         if words[1] == "Copyright" {  func (mklines *MkLines) checkVarassignPlist(mkline *MkLine) {
                                 relevant = false          switch mkline.Varcanon() {
           case "PLIST_VARS":
                   for _, id := range mkline.ValueFields(resolveVariableRefs(mklines, mkline.Value())) {
                           if !mklines.plistVarSkip && mklines.plistVarSet[id] == nil {
                                   mkline.Warnf("%q is added to PLIST_VARS, but PLIST.%s is not defined in this file.", id, id)
                         }                          }
                   }
   
                 case mkline.IsEmpty():          case "PLIST.*":
                         finish()                  id := mkline.Varparam()
                   if !mklines.plistVarSkip && mklines.plistVarAdded[id] == nil {
                           mkline.Warnf("PLIST.%s is defined, but %q is not added to PLIST_VARS in this file.", id, id)
                 }                  }
         }          }
   
         finish()  
 }  }
   
 // CheckUsedBy checks that this file (a Makefile.common) has the given  // CheckUsedBy checks that this file (a Makefile.common) has the given
Line 597  func (mklines *MkLines) CheckUsedBy(rela
Line 528  func (mklines *MkLines) CheckUsedBy(rela
         SaveAutofixChanges(lines)          SaveAutofixChanges(lines)
 }  }
   
   func (mklines *MkLines) SplitToParagraphs() []*Paragraph {
           var paras []*Paragraph
   
           lines := mklines.mklines
           isEmpty := func(i int) bool {
                   if lines[i].IsEmpty() {
                           return true
                   }
                   return lines[i].IsComment() &&
                           lines[i].Text == "#" &&
                           (i == 0 || lines[i-1].IsComment()) &&
                           (i == len(lines)-1 || lines[i+1].IsComment())
           }
   
           i := 0
           for i < len(lines) {
                   from := i
                   for from < len(lines) && isEmpty(from) {
                           from++
                   }
   
                   to := from
                   for to < len(lines) && !isEmpty(to) {
                           to++
                   }
   
                   if from != to {
                           paras = append(paras, NewParagraph(mklines, from, to))
                   }
                   i = to
           }
   
           return paras
   }
   
   // ExpandLoopVar searches the surrounding .for loops for the given
   // variable and returns a slice containing all its values, fully
   // expanded.
   //
   // It can only be used during an active ForEach call.
   func (mklines *MkLines) ExpandLoopVar(varname string) []string {
   
           // From the inner loop to the outer loop, just in case
           // that two loops should ever use the same variable.
           for i := len(mklines.indentation.levels) - 1; i >= 0; i-- {
                   ind := mklines.indentation.levels[i]
   
                   mkline := ind.mkline
                   if mkline.Directive() != "for" {
                           continue
                   }
   
                   // TODO: If needed, add support for multi-variable .for loops.
                   resolved := resolveVariableRefs(mklines, mkline.Args())
                   words := mkline.ValueFields(resolved)
                   if len(words) >= 3 && words[0] == varname && words[1] == "in" {
                           return words[2:]
                   }
           }
   
           return nil
   }
   
 func (mklines *MkLines) SaveAutofixChanges() {  func (mklines *MkLines) SaveAutofixChanges() {
         mklines.lines.SaveAutofixChanges()          mklines.lines.SaveAutofixChanges()
 }  }
Line 604  func (mklines *MkLines) SaveAutofixChang
Line 598  func (mklines *MkLines) SaveAutofixChang
 func (mklines *MkLines) EOFLine() *MkLine {  func (mklines *MkLines) EOFLine() *MkLine {
         return NewMkLineParser().Parse(mklines.lines.EOFLine())          return NewMkLineParser().Parse(mklines.lines.EOFLine())
 }  }
   
   // Whole returns a virtual line that can be used for issuing diagnostics
   // and explanations, but not for text replacements.
   func (mklines *MkLines) Whole() *Line { return mklines.lines.Whole() }

Legend:
Removed from v.1.58  
changed lines
  Added in v.1.59

CVSweb <webmaster@jp.NetBSD.org>