version 1.2, 2019/12/08 22:03:38 |
version 1.8, 2020/03/15 11:31:24 |
Line 25 func (ck *MkVarUseChecker) Check(vuc *Va |
|
Line 25 func (ck *MkVarUseChecker) Check(vuc *Va |
|
ck.checkUndefined() |
ck.checkUndefined() |
ck.checkPermissions(vuc) |
ck.checkPermissions(vuc) |
|
|
ck.checkVarname() |
ck.checkVarname(vuc.time) |
ck.checkModifiers() |
ck.checkModifiers() |
|
ck.checkAssignable(vuc) |
ck.checkQuoting(vuc) |
ck.checkQuoting(vuc) |
|
|
|
ck.checkToolsPlatform() |
ck.checkBuildDefs() |
ck.checkBuildDefs() |
ck.checkDeprecated() |
ck.checkDeprecated() |
|
ck.checkPkgBuildOptions() |
|
|
NewMkLineChecker(ck.MkLines, ck.MkLine). |
NewMkLineChecker(ck.MkLines, ck.MkLine). |
checkTextVarUse(ck.use.varname, ck.vartype, vuc.time) |
checkTextVarUse(ck.use.varname, ck.vartype, vuc.time) |
Line 42 func (ck *MkVarUseChecker) checkUndefine |
|
Line 45 func (ck *MkVarUseChecker) checkUndefine |
|
varname := varuse.varname |
varname := varuse.varname |
|
|
switch { |
switch { |
case !G.Opts.WarnExtra, |
case !G.WarnExtra, |
// Well-known variables are probably defined by the infrastructure. |
// Well-known variables are probably defined by the infrastructure. |
vartype != nil && !vartype.IsGuessed(), |
vartype != nil && !vartype.IsGuessed(), |
// TODO: At load time, check ck.MkLines.loadVars instead of allVars. |
// TODO: At load time, check ck.MkLines.loadVars instead of allVars. |
ck.MkLines.allVars.IsDefinedSimilar(varname), |
ck.MkLines.allVars.IsDefinedSimilar(varname), |
ck.MkLines.forVars[varname], |
ck.MkLines.checkAllData.forVars[varname], |
ck.MkLines.allVars.Mentioned(varname) != nil, |
ck.MkLines.allVars.Mentioned(varname) != nil, |
G.Pkg != nil && G.Pkg.vars.IsDefinedSimilar(varname), |
ck.MkLines.pkg != nil && ck.MkLines.pkg.vars.IsDefinedSimilar(varname), |
containsVarRef(varname), |
containsVarUse(varname), |
G.Pkgsrc.vartypes.IsDefinedCanon(varname), |
G.Pkgsrc.vartypes.IsDefinedCanon(varname), |
varname == "": |
varname == "": |
return |
return |
Line 130 func (ck *MkVarUseChecker) checkModifier |
|
Line 133 func (ck *MkVarUseChecker) checkModifier |
|
fix.Apply() |
fix.Apply() |
} |
} |
|
|
func (ck *MkVarUseChecker) checkVarname() { |
func (ck *MkVarUseChecker) checkVarname(time VucTime) { |
varname := ck.use.varname |
varname := ck.use.varname |
if varname == "@" { |
if varname == "@" { |
ck.MkLine.Warnf("Please use %q instead of %q.", "${.TARGET}", "$@") |
ck.MkLine.Warnf("Please use %q instead of %q.", "${.TARGET}", "$@") |
Line 139 func (ck *MkVarUseChecker) checkVarname( |
|
Line 142 func (ck *MkVarUseChecker) checkVarname( |
|
"of the same name.") |
"of the same name.") |
} |
} |
|
|
if varname == "LOCALBASE" && !G.Infrastructure { |
if varname == "LOCALBASE" && !G.Infrastructure && time == VucRunTime { |
fix := ck.MkLine.Autofix() |
fix := ck.MkLine.Autofix() |
fix.Warnf("Please use PREFIX instead of LOCALBASE.") |
fix.Warnf("Please use PREFIX instead of LOCALBASE.") |
fix.ReplaceRegex(`\$\{LOCALBASE\b`, "${PREFIX", 1) |
fix.ReplaceAfter("${", "LOCALBASE", "PREFIX") |
fix.Apply() |
fix.Apply() |
} |
} |
} |
} |
Line 153 func (ck *MkVarUseChecker) checkVarname( |
|
Line 156 func (ck *MkVarUseChecker) checkVarname( |
|
// |
// |
// See checkVarassignLeftPermissions. |
// See checkVarassignLeftPermissions. |
func (ck *MkVarUseChecker) checkPermissions(vuc *VarUseContext) { |
func (ck *MkVarUseChecker) checkPermissions(vuc *VarUseContext) { |
if !G.Opts.WarnPerm { |
if !G.WarnPerm { |
return |
return |
} |
} |
if G.Infrastructure { |
if G.Infrastructure { |
Line 200 func (ck *MkVarUseChecker) checkPermissi |
|
Line 203 func (ck *MkVarUseChecker) checkPermissi |
|
|
|
effPerms := vartype.EffectivePermissions(basename) |
effPerms := vartype.EffectivePermissions(basename) |
if effPerms.Contains(aclpUseLoadtime) { |
if effPerms.Contains(aclpUseLoadtime) { |
ck.checkUseAtLoadTime(vuc.time) |
if vuc.time == VucLoadTime { |
|
ck.checkUseAtLoadTime() |
|
} |
|
|
// Since the variable may be used at load time, it probably |
// Since the variable may be used at load time, it probably |
// may be used at run time as well. If it weren't, that would |
// may be used at run time as well. If it weren't, that would |
Line 285 func (ck *MkVarUseChecker) warnPermissio |
|
Line 290 func (ck *MkVarUseChecker) warnPermissio |
|
} |
} |
alternativeFiles := vartype.AlternativeFiles(needed) |
alternativeFiles := vartype.AlternativeFiles(needed) |
|
|
loadTimeExplanation := func() []string { |
loadTimeExplanation := []string{ |
return []string{ |
"Many variables, especially lists of something, get their values incrementally.", |
"Many variables, especially lists of something, get their values incrementally.", |
"Therefore it is generally unsafe to rely on their", |
"Therefore it is generally unsafe to rely on their", |
"value until it is clear that it will never change again.", |
"value until it is clear that it will never change again.", |
"This point is reached when the whole package Makefile is loaded and", |
"This point is reached when the whole package Makefile is loaded and", |
"execution of the shell commands starts; in some cases earlier.", |
"execution of the shell commands starts; in some cases earlier.", |
"", |
"", |
"Additionally, when using the \":=\" operator, each $$ is replaced", |
"Additionally, when using the \":=\" operator, each $$ is replaced", |
"with a single $, so variables that have references to shell", |
"with a single $, so variables that have references to shell", |
"variables or regular expressions are modified in a subtle way."} |
"variables or regular expressions are modified in a subtle way."} |
|
} |
|
|
|
switch { |
switch { |
case alternativeFiles == "" && directly: |
case alternativeFiles == "" && directly: |
mkline.Warnf("%s should not be used at load time in any file.", varname) |
mkline.Warnf("%s should not be used at load time in any file.", varname) |
ck.explainPermissions(varname, vartype, loadTimeExplanation()...) |
ck.explainPermissions(varname, vartype, loadTimeExplanation...) |
|
|
case alternativeFiles == "": |
case alternativeFiles == "": |
mkline.Warnf("%s should not be used in any file.", varname) |
mkline.Warnf("%s should not be used in any file.", varname) |
ck.explainPermissions(varname, vartype, loadTimeExplanation()...) |
ck.explainPermissions(varname, vartype, loadTimeExplanation...) |
|
|
case directly: |
case directly: |
mkline.Warnf( |
mkline.Warnf( |
"%s should not be used at load time in this file; "+ |
"%s should not be used at load time in this file; "+ |
"it would be ok in %s.", |
"it would be ok in %s.", |
varname, alternativeFiles) |
varname, alternativeFiles) |
ck.explainPermissions(varname, vartype, loadTimeExplanation()...) |
ck.explainPermissions(varname, vartype, loadTimeExplanation...) |
|
|
default: |
default: |
mkline.Warnf( |
mkline.Warnf( |
Line 364 func (ck *MkVarUseChecker) explainPermis |
|
Line 367 func (ck *MkVarUseChecker) explainPermis |
|
ck.MkLine.Explain(expl...) |
ck.MkLine.Explain(expl...) |
} |
} |
|
|
func (ck *MkVarUseChecker) checkUseAtLoadTime(time VucTime) { |
func (ck *MkVarUseChecker) checkUseAtLoadTime() { |
if time != VucLoadTime { |
|
return |
|
} |
|
if ck.vartype.IsAlwaysInScope() || ck.MkLines.Tools.SeenPrefs { |
if ck.vartype.IsAlwaysInScope() || ck.MkLines.Tools.SeenPrefs { |
return |
return |
} |
} |
if G.Pkg != nil && G.Pkg.seenPrefs { |
if ck.MkLines.pkg != nil && ck.MkLines.pkg.seenPrefs { |
return |
return |
} |
} |
mkline := ck.MkLine |
mkline := ck.MkLine |
Line 380 func (ck *MkVarUseChecker) checkUseAtLoa |
|
Line 380 func (ck *MkVarUseChecker) checkUseAtLoa |
|
return |
return |
} |
} |
|
|
if ck.vartype.IsPackageSettable() && |
if ck.vartype.IsPackageSettable() { |
basename != "Makefile" && basename != "options.mk" { |
// For package-settable variables, the explanation below |
|
// doesn't make sense since including bsd.prefs.mk won't |
// For package-settable variables, the explanation doesn't |
// define any package-settable variables. |
// make sense since it talks about completely different |
|
// types of variables. |
|
return |
return |
} |
} |
|
|
Line 419 func (ck *MkVarUseChecker) warnToolLoadT |
|
Line 417 func (ck *MkVarUseChecker) warnToolLoadT |
|
// to skip the shell and execute the commands via execve, which |
// to skip the shell and execute the commands via execve, which |
// means that even echo is not a shell-builtin anymore. |
// means that even echo is not a shell-builtin anymore. |
|
|
// TODO: Replace "parse time" with "load time" everywhere. |
|
|
|
if tool.Validity == AfterPrefsMk { |
if tool.Validity == AfterPrefsMk { |
ck.MkLine.Warnf("To use the tool ${%s} at load time, bsd.prefs.mk has to be included before.", varname) |
ck.MkLine.Warnf("To use the tool ${%s} at load time, bsd.prefs.mk has to be included before.", varname) |
return |
return |
Line 452 func (ck *MkVarUseChecker) warnToolLoadT |
|
Line 448 func (ck *MkVarUseChecker) warnToolLoadT |
|
"except in the package Makefile itself.") |
"except in the package Makefile itself.") |
} |
} |
|
|
|
func (ck *MkVarUseChecker) checkAssignable(vuc *VarUseContext) { |
|
leftType := vuc.vartype |
|
if leftType == nil || leftType.basicType != BtPathname { |
|
return |
|
} |
|
rightType := G.Pkgsrc.VariableType(ck.MkLines, ck.use.varname) |
|
if rightType == nil || rightType.basicType != BtShellCommand { |
|
return |
|
} |
|
|
|
mkline := ck.MkLine |
|
if mkline.Varcanon() == "PKG_SHELL.*" { |
|
switch ck.use.varname { |
|
case "SH", "BASH", "TOOLS_PLATFORM.sh": |
|
return |
|
} |
|
} |
|
|
|
mkline.Warnf( |
|
"Incompatible types: %s (type %q) cannot be assigned to type %q.", |
|
ck.use.varname, rightType.basicType.name, leftType.basicType.name) |
|
mkline.Explain( |
|
"Shell commands often start with a pathname.", |
|
"They could also start with a list of environment variable", |
|
"definitions, since that is accepted by the shell.", |
|
"They can also contain addition command line arguments", |
|
"that are not filenames at all.") |
|
} |
|
|
// checkVarUseWords checks whether a variable use of the form ${VAR} |
// checkVarUseWords checks whether a variable use of the form ${VAR} |
// or ${VAR:modifiers} is allowed in a certain context. |
// or ${VAR:modifiers} is allowed in a certain context. |
func (ck *MkVarUseChecker) checkQuoting(vuc *VarUseContext) { |
func (ck *MkVarUseChecker) checkQuoting(vuc *VarUseContext) { |
if !G.Opts.WarnQuoting || vuc.quoting == VucQuotUnknown { |
if !G.WarnQuoting || vuc.quoting == VucQuotUnknown { |
return |
return |
} |
} |
|
|
Line 475 func (ck *MkVarUseChecker) checkQuoting( |
|
Line 500 func (ck *MkVarUseChecker) checkQuoting( |
|
// since the GNU configure scripts cannot handle these space characters. |
// since the GNU configure scripts cannot handle these space characters. |
// |
// |
// When doing checks outside a package, the :M* modifier is needed for safety. |
// When doing checks outside a package, the :M* modifier is needed for safety. |
needMstar := (G.Pkg == nil || G.Pkg.vars.IsDefined("GNU_CONFIGURE")) && |
needMstar := (ck.MkLines.pkg == nil || ck.MkLines.pkg.vars.IsDefined("GNU_CONFIGURE")) && |
matches(varUse.varname, `^(?:.*_)?(?:CFLAGS|CPPFLAGS|CXXFLAGS|FFLAGS|LDFLAGS|LIBS)$`) |
matches(varUse.varname, `^(?:.*_)?(?:CFLAGS|CPPFLAGS|CXXFLAGS|FFLAGS|LDFLAGS|LIBS)$`) |
|
|
mkline := ck.MkLine |
mkline := ck.MkLine |
Line 504 func (ck *MkVarUseChecker) checkQuotingQ |
|
Line 529 func (ck *MkVarUseChecker) checkQuotingQ |
|
if correctMod == mod+":Q" && vuc.IsWordPart && !vartype.IsShell() { |
if correctMod == mod+":Q" && vuc.IsWordPart && !vartype.IsShell() { |
|
|
isSingleWordConstant := func() bool { |
isSingleWordConstant := func() bool { |
if G.Pkg == nil { |
if ck.MkLines.pkg == nil { |
return false |
return false |
} |
} |
|
|
varinfo := G.Pkg.redundant.vars[varname] |
varinfo := ck.MkLines.pkg.redundant.vars[varname] |
if varinfo == nil || !varinfo.vari.IsConstant() { |
if varinfo == nil || !varinfo.vari.IsConstant() { |
return false |
return false |
} |
} |
Line 645 func (ck *MkVarUseChecker) warnRedundant |
|
Line 670 func (ck *MkVarUseChecker) warnRedundant |
|
fix.Apply() |
fix.Apply() |
} |
} |
|
|
|
func (ck *MkVarUseChecker) checkToolsPlatform() { |
|
if ck.MkLine.IsDirective() { |
|
return |
|
} |
|
|
|
varname := ck.use.varname |
|
if varnameCanon(varname) != "TOOLS_PLATFORM.*" { |
|
return |
|
} |
|
|
|
indentation := ck.MkLines.indentation |
|
switch { |
|
case indentation.DependsOn("OPSYS"), |
|
indentation.DependsOn("MACHINE_PLATFORM"), |
|
indentation.DependsOn(varname): |
|
// TODO: Only return if the conditional is on the correct OPSYS. |
|
return |
|
} |
|
|
|
toolName := varnameParam(varname) |
|
tool := G.Pkgsrc.Tools.ByName(toolName) |
|
if tool == nil { |
|
return |
|
} |
|
|
|
if len(tool.undefinedOn) > 0 { |
|
ck.MkLine.Warnf("%s is undefined on %s.", |
|
varname, joinCambridge("and", tool.undefinedOn...)) |
|
} else if len(tool.conditionalOn) > 0 { |
|
ck.MkLine.Warnf("%s may be undefined on %s.", |
|
varname, joinCambridge("and", tool.conditionalOn...)) |
|
} |
|
} |
|
|
func (ck *MkVarUseChecker) checkBuildDefs() { |
func (ck *MkVarUseChecker) checkBuildDefs() { |
varname := ck.use.varname |
varname := ck.use.varname |
|
|
Line 680 func (ck *MkVarUseChecker) checkDeprecat |
|
Line 739 func (ck *MkVarUseChecker) checkDeprecat |
|
|
|
ck.MkLine.Warnf("Use of %q is deprecated. %s", varname, instead) |
ck.MkLine.Warnf("Use of %q is deprecated. %s", varname, instead) |
} |
} |
|
|
|
func (ck *MkVarUseChecker) checkPkgBuildOptions() { |
|
pkg := ck.MkLines.pkg |
|
if pkg == nil { |
|
return |
|
} |
|
varname := ck.use.varname |
|
if !hasPrefix(varname, "PKG_BUILD_OPTIONS.") { |
|
return |
|
} |
|
param := varnameParam(varname) |
|
if pkg.seenPkgbase.Seen(param) { |
|
return |
|
} |
|
|
|
ck.MkLine.Warnf("The PKG_BUILD_OPTIONS for %q are not available to this package.", |
|
param) |
|
ck.MkLine.Explain( |
|
"The variable parameter for PKG_BUILD_OPTIONS must correspond", |
|
"to the value of \"pkgbase\" above.", |
|
"", |
|
"For more information, see mk/pkg-build-options.mk.") |
|
} |